Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / bindings / DOMString.h
blob7b2d66ec7752c7428058cb9511b5e3bf7b042290
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/. */
7 #ifndef mozilla_dom_DOMString_h
8 #define mozilla_dom_DOMString_h
10 #include "nsString.h"
11 #include "mozilla/Assertions.h"
12 #include "mozilla/Attributes.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/StringBuffer.h"
15 #include "nsDOMString.h"
16 #include "nsAtom.h"
18 namespace mozilla::dom {
20 /**
21 * A class for representing string return values. This can be either passed to
22 * callees that have an nsString or nsAString out param or passed to a callee
23 * that actually knows about this class and can work with it. Such a callee may
24 * call these setters:
26 * SetKnownLiveStringBuffer
27 * SetStringBuffer
28 * SetKnownLiveString
29 * SetKnownLiveAtom
30 * SetNull
32 * to assign a value to the DOMString without instantiating an actual nsString
33 * in the process, or use AsAString() to instantiate an nsString and work with
34 * it. These options are mutually exclusive! Don't do more than one of them.
36 * It's only OK to call
37 * SetKnownLiveStringBuffer/SetKnownLiveString/SetKnownLiveAtom if the caller of
38 * the method in question plans to keep holding a strong ref to the stringbuffer
39 * involved, whether it's a raw mozilla::StringBuffer, or stored inside the
40 * string or atom being passed. In the string/atom cases that means the caller
41 * must own the string or atom, and not mutate it (in the string case) for the
42 * lifetime of the DOMString.
44 * The proper way to extract a value is to check IsNull(). If not null, then
45 * check IsEmpty(). If neither of those is true, check HasStringBuffer(). If
46 * that's true, call StringBuffer()/StringBufferLength(). If HasStringBuffer()
47 * returns false, check HasLiteral, and if that returns true call
48 * Literal()/LiteralLength(). If HasLiteral() is false, call AsAString() and
49 * get the value from that.
51 class MOZ_STACK_CLASS DOMString {
52 public:
53 DOMString() : mStringBuffer(nullptr), mLength(0), mState(State::Empty) {}
54 ~DOMString() {
55 MOZ_ASSERT(!mString || !mStringBuffer, "Shouldn't have both present!");
56 if (mState == State::OwnedStringBuffer) {
57 MOZ_ASSERT(mStringBuffer);
58 mStringBuffer->Release();
62 operator nsString&() { return AsAString(); }
64 // It doesn't make any sense to convert a DOMString to a const nsString or
65 // nsAString reference; this class is meant for outparams only.
66 operator const nsString&() = delete;
67 operator const nsAString&() = delete;
69 nsString& AsAString() {
70 MOZ_ASSERT(mState == State::Empty || mState == State::String,
71 "Moving from nonempty state to another nonempty state?");
72 MOZ_ASSERT(!mStringBuffer, "We already have a stringbuffer?");
73 if (!mString) {
74 mString.emplace();
75 mState = State::String;
77 return *mString;
80 bool HasStringBuffer() const {
81 MOZ_ASSERT(!mString || !mStringBuffer, "Shouldn't have both present!");
82 MOZ_ASSERT(mState > State::Null,
83 "Caller should have checked IsNull() and IsEmpty() first");
84 return mState >= State::OwnedStringBuffer;
87 // Get the stringbuffer. This can only be called if HasStringBuffer()
88 // returned true. If that's true, it will never return null. Note that
89 // constructing a string from this mozilla::StringBuffer with length given by
90 // StringBufferLength() might give you something that is not null-terminated.
91 mozilla::StringBuffer* StringBuffer() const {
92 MOZ_ASSERT(HasStringBuffer(),
93 "Don't ask for the stringbuffer if we don't have it");
94 MOZ_ASSERT(mStringBuffer, "We better have a stringbuffer if we claim to");
95 return mStringBuffer;
98 // Get the length of the stringbuffer. Can only be called if
99 // HasStringBuffer().
100 uint32_t StringBufferLength() const {
101 MOZ_ASSERT(HasStringBuffer(),
102 "Don't call this if there is no stringbuffer");
103 return mLength;
106 bool HasLiteral() const {
107 MOZ_ASSERT(!mString || !mStringBuffer, "Shouldn't have both present!");
108 MOZ_ASSERT(mState > State::Null,
109 "Caller should have checked IsNull() and IsEmpty() first");
110 return mState == State::Literal;
113 // Get the literal string. This can only be called if HasLiteral()
114 // returned true. If that's true, it will never return null.
115 const char16_t* Literal() const {
116 MOZ_ASSERT(HasLiteral(), "Don't ask for the literal if we don't have it");
117 MOZ_ASSERT(mLiteral, "We better have a literal if we claim to");
118 return mLiteral;
121 // Get the length of the literal. Can only be called if HasLiteral().
122 uint32_t LiteralLength() const {
123 MOZ_ASSERT(HasLiteral(), "Don't call this if there is no literal");
124 return mLength;
127 // Initialize the DOMString to a (mozilla::StringBuffer, length) pair. The
128 // length does NOT have to be the full length of the (null-terminated) string
129 // in the mozilla::StringBuffer.
130 void SetKnownLiveStringBuffer(mozilla::StringBuffer* aStringBuffer,
131 uint32_t aLength) {
132 MOZ_ASSERT(mState == State::Empty, "We're already set to a value");
133 if (aLength != 0) {
134 SetStringBufferInternal(aStringBuffer, aLength);
135 mState = State::UnownedStringBuffer;
137 // else nothing to do
140 // Like SetKnownLiveStringBuffer, but holds a reference to the
141 // mozilla::StringBuffer.
142 void SetStringBuffer(mozilla::StringBuffer* aStringBuffer, uint32_t aLength) {
143 MOZ_ASSERT(mState == State::Empty, "We're already set to a value");
144 if (aLength != 0) {
145 SetStringBufferInternal(aStringBuffer, aLength);
146 aStringBuffer->AddRef();
147 mState = State::OwnedStringBuffer;
149 // else nothing to do
152 void SetKnownLiveString(const nsAString& aString) {
153 MOZ_ASSERT(mString.isNothing(), "We already have a string?");
154 MOZ_ASSERT(mState == State::Empty, "We're already set to a value");
155 MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
156 if (MOZ_UNLIKELY(aString.IsVoid())) {
157 SetNull();
158 } else if (!aString.IsEmpty()) {
159 if (mozilla::StringBuffer* buf = aString.GetStringBuffer()) {
160 SetKnownLiveStringBuffer(buf, aString.Length());
161 } else if (aString.IsLiteral()) {
162 SetLiteralInternal(aString.BeginReading(), aString.Length());
163 } else {
164 AsAString() = aString;
169 enum NullHandling { eTreatNullAsNull, eTreatNullAsEmpty, eNullNotExpected };
171 void SetKnownLiveAtom(nsAtom* aAtom, NullHandling aNullHandling) {
172 MOZ_ASSERT(mString.isNothing(), "We already have a string?");
173 MOZ_ASSERT(mState == State::Empty, "We're already set to a value");
174 MOZ_ASSERT(aAtom || aNullHandling != eNullNotExpected);
175 if (aNullHandling == eNullNotExpected || aAtom) {
176 if (aAtom->IsStatic()) {
177 // Static atoms are backed by literals. Explicitly call AsStatic() here
178 // to avoid the extra IsStatic() checks in nsAtom::GetUTF16String().
179 SetLiteralInternal(aAtom->AsStatic()->GetUTF16String(),
180 aAtom->GetLength());
181 } else {
182 SetKnownLiveStringBuffer(aAtom->AsDynamic()->StringBuffer(),
183 aAtom->GetLength());
185 } else if (aNullHandling == eTreatNullAsNull) {
186 SetNull();
190 void SetNull() {
191 MOZ_ASSERT(!mStringBuffer, "Should have no stringbuffer if null");
192 MOZ_ASSERT(mString.isNothing(), "Should have no string if null");
193 MOZ_ASSERT(mState == State::Empty, "Already set to a value?");
194 mState = State::Null;
197 bool IsNull() const {
198 MOZ_ASSERT(!mStringBuffer || mString.isNothing(),
199 "How could we have a stringbuffer and a nonempty string?");
200 return mState == State::Null || (mString && mString->IsVoid());
203 bool IsEmpty() const {
204 MOZ_ASSERT(!mStringBuffer || mString.isNothing(),
205 "How could we have a stringbuffer and a nonempty string?");
206 // This is not exact, because we might still have an empty XPCOM string.
207 // But that's OK; in that case the callers will try the XPCOM string
208 // themselves.
209 return mState == State::Empty;
212 void ToString(nsAString& aString) {
213 if (IsNull()) {
214 SetDOMStringToNull(aString);
215 } else if (IsEmpty()) {
216 aString.Truncate();
217 } else if (HasStringBuffer()) {
218 // Don't share the mozilla::StringBuffer with aString if the result would
219 // not be null-terminated.
220 mozilla::StringBuffer* buf = StringBuffer();
221 uint32_t len = StringBufferLength();
222 auto chars = static_cast<char16_t*>(buf->Data());
223 if (chars[len] == '\0') {
224 // Safe to share the buffer.
225 aString.Assign(buf, len);
226 } else {
227 // We need to copy, unfortunately.
228 aString.Assign(chars, len);
230 } else if (HasLiteral()) {
231 aString.AssignLiteral(Literal(), LiteralLength());
232 } else {
233 aString = AsAString();
237 private:
238 void SetStringBufferInternal(mozilla::StringBuffer* aStringBuffer,
239 uint32_t aLength) {
240 MOZ_ASSERT(mString.isNothing(), "We already have a string?");
241 MOZ_ASSERT(mState == State::Empty, "We're already set to a value");
242 MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
243 MOZ_ASSERT(aStringBuffer, "Why are we getting null?");
244 MOZ_ASSERT(aLength != 0, "Should not have empty string here");
245 mStringBuffer = aStringBuffer;
246 mLength = aLength;
249 void SetLiteralInternal(const char16_t* aLiteral, uint32_t aLength) {
250 MOZ_ASSERT(!mLiteral, "What's going on here?");
251 mLiteral = aLiteral;
252 mLength = aLength;
253 mState = State::Literal;
256 enum class State : uint8_t {
257 Empty, // An empty string. Default state.
258 Null, // Null (not a string at all)
260 // All states that involve actual string data should come after
261 // Empty and Null.
263 String, // An XPCOM string stored in mString.
264 Literal, // A string literal (static lifetime).
265 OwnedStringBuffer, // mStringBuffer is valid and we have a ref to it.
266 UnownedStringBuffer, // mStringBuffer is valid; we are not holding a ref.
267 // The two string buffer values must come last. This lets us avoid doing
268 // two tests to figure out whether we have a stringbuffer.
271 // We need to be able to act like a string as needed
272 Maybe<nsAutoString> mString;
274 union {
275 // The mozilla::StringBuffer in the OwnedStringBuffer/UnownedStringBuffer
276 // cases.
277 mozilla::StringBuffer* MOZ_UNSAFE_REF(
278 "The ways in which this can be safe are "
279 "documented above and enforced through "
280 "assertions") mStringBuffer;
281 // The literal in the Literal case.
282 const char16_t* mLiteral;
285 // Length in the stringbuffer and literal cases.
286 uint32_t mLength;
288 State mState;
291 } // namespace mozilla::dom
293 #endif // mozilla_dom_DOMString_h