Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef mozilla_dom_DOMString_h
8 : #define mozilla_dom_DOMString_h
9 :
10 : #include "nsStringGlue.h"
11 : #include "nsStringBuffer.h"
12 : #include "mozilla/Assertions.h"
13 : #include "mozilla/Attributes.h"
14 : #include "mozilla/Maybe.h"
15 : #include "nsDOMString.h"
16 : #include "nsIAtom.h"
17 :
18 : namespace mozilla {
19 : namespace dom {
20 :
21 : /**
22 : * A class for representing string return values. This can be either passed to
23 : * callees that have an nsString or nsAString out param or passed to a callee
24 : * that actually knows about this class and can work with it. Such a callee may
25 : * call SetStringBuffer or SetEphemeralStringBuffer or SetOwnedString or
26 : * SetOwnedAtom on this object. It's only OK to call
27 : * SetStringBuffer/SetOwnedString/SetOwnedAtom if the caller of the method in
28 : * question plans to keep holding a strong ref to the stringbuffer involved,
29 : * whether it's a raw nsStringBuffer, or stored inside the string or atom being
30 : * passed. In the string/atom cases that means the caller must own the string
31 : * or atom, and not mutate it (in the string case) for the lifetime of the
32 : * DOMString.
33 : *
34 : * The proper way to store a value in this class is to either to do nothing
35 : * (which leaves this as an empty string), to call
36 : * SetStringBuffer/SetEphemeralStringBuffer with a non-null stringbuffer, to
37 : * call SetOwnedString, to call SetOwnedAtom, to call SetNull(), or to call
38 : * AsAString() and set the value in the resulting nsString. These options are
39 : * mutually exclusive! Don't do more than one of them.
40 : *
41 : * The proper way to extract a value is to check IsNull(). If not null, then
42 : * check HasStringBuffer(). If that's true, check for a zero length, and if the
43 : * length is nonzero call StringBuffer(). If the length is zero this is the
44 : * empty string. If HasStringBuffer() returns false, call AsAString() and get
45 : * the value from that.
46 : */
47 : class MOZ_STACK_CLASS DOMString {
48 : public:
49 21748 : DOMString()
50 21748 : : mStringBuffer(nullptr)
51 : , mLength(0)
52 : , mIsNull(false)
53 21748 : , mStringBufferOwned(false)
54 21748 : {}
55 21748 : ~DOMString()
56 21748 : {
57 21748 : MOZ_ASSERT(!mString || !mStringBuffer,
58 : "Shouldn't have both present!");
59 21748 : if (mStringBufferOwned) {
60 0 : MOZ_ASSERT(mStringBuffer);
61 0 : mStringBuffer->Release();
62 : }
63 21748 : }
64 :
65 116 : operator nsString&()
66 : {
67 116 : return AsAString();
68 : }
69 :
70 : // It doesn't make any sense to convert a DOMString to a const nsString or
71 : // nsAString reference; this class is meant for outparams only.
72 : operator const nsString&() = delete;
73 : operator const nsAString&() = delete;
74 :
75 243 : nsString& AsAString()
76 : {
77 243 : MOZ_ASSERT(!mStringBuffer, "We already have a stringbuffer?");
78 243 : MOZ_ASSERT(!mIsNull, "We're already set as null");
79 243 : if (!mString) {
80 122 : mString.emplace();
81 : }
82 243 : return *mString;
83 : }
84 :
85 91812 : bool HasStringBuffer() const
86 : {
87 91812 : MOZ_ASSERT(!mString || !mStringBuffer,
88 : "Shouldn't have both present!");
89 91812 : MOZ_ASSERT(!mIsNull, "Caller should have checked IsNull() first");
90 91812 : return !mString;
91 : }
92 :
93 : // Get the stringbuffer. This can only be called if HasStringBuffer()
94 : // returned true and StringBufferLength() is nonzero. If that's true, it will
95 : // never return null. Note that constructing a string from this
96 : // nsStringBuffer with length given by StringBufferLength() might give you
97 : // something that is not null-terminated.
98 1950 : nsStringBuffer* StringBuffer() const
99 : {
100 1950 : MOZ_ASSERT(!mIsNull, "Caller should have checked IsNull() first");
101 1950 : MOZ_ASSERT(HasStringBuffer(),
102 : "Don't ask for the stringbuffer if we don't have it");
103 1950 : MOZ_ASSERT(StringBufferLength() != 0, "Why are you asking for this?");
104 1950 : MOZ_ASSERT(mStringBuffer,
105 : "If our length is nonzero, we better have a stringbuffer.");
106 1950 : return mStringBuffer;
107 : }
108 :
109 : // Get the length of the stringbuffer. Can only be called if
110 : // HasStringBuffer().
111 46631 : uint32_t StringBufferLength() const
112 : {
113 46631 : MOZ_ASSERT(HasStringBuffer(), "Don't call this if there is no stringbuffer");
114 46631 : return mLength;
115 : }
116 :
117 : // Tell the DOMString to relinquish ownership of its nsStringBuffer to the
118 : // caller. Can only be called if HasStringBuffer().
119 149 : void RelinquishBufferOwnership()
120 : {
121 149 : MOZ_ASSERT(HasStringBuffer(), "Don't call this if there is no stringbuffer");
122 149 : if (mStringBufferOwned) {
123 : // Just hand that ref over.
124 2 : mStringBufferOwned = false;
125 : } else {
126 : // Caller should end up holding a ref.
127 147 : mStringBuffer->AddRef();
128 : }
129 149 : }
130 :
131 : // Initialize the DOMString to a (nsStringBuffer, length) pair. The length
132 : // does NOT have to be the full length of the (null-terminated) string in the
133 : // nsStringBuffer.
134 1950 : void SetStringBuffer(nsStringBuffer* aStringBuffer, uint32_t aLength)
135 : {
136 1950 : MOZ_ASSERT(mString.isNothing(), "We already have a string?");
137 1950 : MOZ_ASSERT(!mIsNull, "We're already set as null");
138 1950 : MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
139 1950 : MOZ_ASSERT(aStringBuffer, "Why are we getting null?");
140 1950 : mStringBuffer = aStringBuffer;
141 1950 : mLength = aLength;
142 1950 : }
143 :
144 : // Like SetStringBuffer, but holds a reference to the nsStringBuffer.
145 2 : void SetEphemeralStringBuffer(nsStringBuffer* aStringBuffer, uint32_t aLength)
146 : {
147 : // We rely on SetStringBuffer to ensure our state invariants.
148 2 : SetStringBuffer(aStringBuffer, aLength);
149 2 : aStringBuffer->AddRef();
150 2 : mStringBufferOwned = true;
151 2 : }
152 :
153 0 : void SetOwnedString(const nsAString& aString)
154 : {
155 0 : MOZ_ASSERT(mString.isNothing(), "We already have a string?");
156 0 : MOZ_ASSERT(!mIsNull, "We're already set as null");
157 0 : MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
158 0 : nsStringBuffer* buf = nsStringBuffer::FromString(aString);
159 0 : if (buf) {
160 0 : SetStringBuffer(buf, aString.Length());
161 0 : } else if (aString.IsVoid()) {
162 0 : SetNull();
163 0 : } else if (!aString.IsEmpty()) {
164 0 : AsAString() = aString;
165 : }
166 0 : }
167 :
168 : enum NullHandling
169 : {
170 : eTreatNullAsNull,
171 : eTreatNullAsEmpty,
172 : eNullNotExpected
173 : };
174 :
175 0 : void SetOwnedAtom(nsIAtom* aAtom, NullHandling aNullHandling)
176 : {
177 0 : MOZ_ASSERT(mString.isNothing(), "We already have a string?");
178 0 : MOZ_ASSERT(!mIsNull, "We're already set as null");
179 0 : MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
180 0 : MOZ_ASSERT(aAtom || aNullHandling != eNullNotExpected);
181 0 : if (aNullHandling == eNullNotExpected || aAtom) {
182 0 : SetStringBuffer(aAtom->GetStringBuffer(), aAtom->GetLength());
183 0 : } else if (aNullHandling == eTreatNullAsNull) {
184 0 : SetNull();
185 : }
186 0 : }
187 :
188 7 : void SetNull()
189 : {
190 7 : MOZ_ASSERT(!mStringBuffer, "Should have no stringbuffer if null");
191 7 : MOZ_ASSERT(mString.isNothing(), "Should have no string if null");
192 7 : mIsNull = true;
193 7 : }
194 :
195 21534 : bool IsNull() const
196 : {
197 21534 : MOZ_ASSERT(!mStringBuffer || mString.isNothing(),
198 : "How could we have a stringbuffer and a nonempty string?");
199 21534 : return mIsNull || (mString && mString->IsVoid());
200 : }
201 :
202 21297 : void ToString(nsAString& aString)
203 : {
204 21297 : if (IsNull()) {
205 7 : SetDOMStringToNull(aString);
206 21290 : } else if (HasStringBuffer()) {
207 21284 : if (StringBufferLength() == 0) {
208 19564 : aString.Truncate();
209 : } else {
210 : // Don't share the nsStringBuffer with aString if the result would not
211 : // be null-terminated.
212 1720 : nsStringBuffer* buf = StringBuffer();
213 1720 : uint32_t len = StringBufferLength();
214 1720 : auto chars = static_cast<char16_t*>(buf->Data());
215 1720 : if (chars[len] == '\0') {
216 : // Safe to share the buffer.
217 1720 : buf->ToString(len, aString);
218 : } else {
219 : // We need to copy, unfortunately.
220 0 : aString.Assign(chars, len);
221 : }
222 : }
223 : } else {
224 6 : aString = AsAString();
225 : }
226 21297 : }
227 :
228 : private:
229 : // We need to be able to act like a string as needed
230 : Maybe<nsAutoString> mString;
231 :
232 : // For callees that know we exist, we can be a stringbuffer/length/null-flag
233 : // triple.
234 : nsStringBuffer* MOZ_UNSAFE_REF("The ways in which this can be safe are "
235 : "documented above and enforced through "
236 : "assertions") mStringBuffer;
237 : uint32_t mLength;
238 : bool mIsNull;
239 : bool mStringBufferOwned;
240 : };
241 :
242 : } // namespace dom
243 : } // namespace mozilla
244 :
245 : #endif // mozilla_dom_DOMString_h
|