Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/dom/DOMString.h
Line
Count
Source (jump to first uncovered line)
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 "nsString.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 "nsAtom.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 these setters:
26
 *
27
 *   SetKnownLiveStringBuffer
28
 *   SetStringBuffer
29
 *   SetKnownLiveString
30
 *   SetKnownLiveAtom
31
 *   SetNull
32
 *
33
 * to assign a value to the DOMString without instantiating an actual nsString
34
 * in the process, or use AsAString() to instantiate an nsString and work with
35
 * it.  These options are mutually exclusive!  Don't do more than one of them.
36
 *
37
 * It's only OK to call
38
 * SetKnownLiveStringBuffer/SetKnownLiveString/SetKnownLiveAtom if the caller of
39
 * the method in question plans to keep holding a strong ref to the stringbuffer
40
 * involved, whether it's a raw nsStringBuffer, or stored inside the string or
41
 * atom being passed.  In the string/atom cases that means the caller must own
42
 * the string or atom, and not mutate it (in the string case) for the lifetime
43
 * of the DOMString.
44
 *
45
 * The proper way to extract a value is to check IsNull().  If not null, then
46
 * check IsEmpty().  If neither of those is true, check HasStringBuffer().  If
47
 * that's true, call StringBuffer()/StringBufferLength().  If HasStringBuffer()
48
 * returns false, check HasLiteral, and if that returns true call
49
 * Literal()/LiteralLength().  If HasLiteral() is false, call AsAString() and
50
 * get the value from that.
51
 */
52
class MOZ_STACK_CLASS DOMString {
53
public:
54
  DOMString()
55
    : mStringBuffer(nullptr)
56
    , mLength(0)
57
    , mState(State::Empty)
58
0
  {}
59
  ~DOMString()
60
0
  {
61
0
    MOZ_ASSERT(!mString || !mStringBuffer,
62
0
               "Shouldn't have both present!");
63
0
    if (mState == State::OwnedStringBuffer) {
64
0
      MOZ_ASSERT(mStringBuffer);
65
0
      mStringBuffer->Release();
66
0
    }
67
0
  }
68
69
  operator nsString&()
70
0
  {
71
0
    return AsAString();
72
0
  }
73
74
  // It doesn't make any sense to convert a DOMString to a const nsString or
75
  // nsAString reference; this class is meant for outparams only.
76
  operator const nsString&() = delete;
77
  operator const nsAString&() = delete;
78
79
  nsString& AsAString()
80
0
  {
81
0
    MOZ_ASSERT(mState == State::Empty || mState == State::String,
82
0
               "Moving from nonempty state to another nonempty state?");
83
0
    MOZ_ASSERT(!mStringBuffer, "We already have a stringbuffer?");
84
0
    if (!mString) {
85
0
      mString.emplace();
86
0
      mState = State::String;
87
0
    }
88
0
    return *mString;
89
0
  }
90
91
  bool HasStringBuffer() const
92
0
  {
93
0
    MOZ_ASSERT(!mString || !mStringBuffer,
94
0
               "Shouldn't have both present!");
95
0
    MOZ_ASSERT(mState > State::Null,
96
0
               "Caller should have checked IsNull() and IsEmpty() first");
97
0
    return mState >= State::OwnedStringBuffer;
98
0
  }
99
100
  // Get the stringbuffer.  This can only be called if HasStringBuffer()
101
  // returned true.  If that's true, it will never return null.  Note that
102
  // constructing a string from this nsStringBuffer with length given by
103
  // StringBufferLength() might give you something that is not null-terminated.
104
  nsStringBuffer* StringBuffer() const
105
0
  {
106
0
    MOZ_ASSERT(HasStringBuffer(),
107
0
               "Don't ask for the stringbuffer if we don't have it");
108
0
    MOZ_ASSERT(mStringBuffer,
109
0
               "We better have a stringbuffer if we claim to");
110
0
    return mStringBuffer;
111
0
  }
112
113
  // Get the length of the stringbuffer.  Can only be called if
114
  // HasStringBuffer().
115
  uint32_t StringBufferLength() const
116
0
  {
117
0
    MOZ_ASSERT(HasStringBuffer(), "Don't call this if there is no stringbuffer");
118
0
    return mLength;
119
0
  }
120
121
  // Tell the DOMString to relinquish ownership of its nsStringBuffer to the
122
  // caller.  Can only be called if HasStringBuffer().
123
  void RelinquishBufferOwnership()
124
0
  {
125
0
    MOZ_ASSERT(HasStringBuffer(), "Don't call this if there is no stringbuffer");
126
0
    if (mState == State::OwnedStringBuffer) {
127
0
      // Just hand that ref over.
128
0
      mState = State::UnownedStringBuffer;
129
0
    } else {
130
0
      // Caller should end up holding a ref.
131
0
      mStringBuffer->AddRef();
132
0
    }
133
0
  }
134
135
  bool HasLiteral() const
136
0
  {
137
0
    MOZ_ASSERT(!mString || !mStringBuffer,
138
0
               "Shouldn't have both present!");
139
0
    MOZ_ASSERT(mState > State::Null,
140
0
               "Caller should have checked IsNull() and IsEmpty() first");
141
0
    return mState == State::Literal;
142
0
  }
143
144
  // Get the literal string.  This can only be called if HasLiteral()
145
  // returned true.  If that's true, it will never return null.
146
  const char16_t* Literal() const
147
0
  {
148
0
    MOZ_ASSERT(HasLiteral(),
149
0
               "Don't ask for the literal if we don't have it");
150
0
    MOZ_ASSERT(mLiteral,
151
0
               "We better have a literal if we claim to");
152
0
    return mLiteral;
153
0
  }
154
155
  // Get the length of the literal.  Can only be called if HasLiteral().
156
  uint32_t LiteralLength() const
157
0
  {
158
0
    MOZ_ASSERT(HasLiteral(), "Don't call this if there is no literal");
159
0
    return mLength;
160
0
  }
161
162
  bool HasAtom() const
163
0
  {
164
0
    MOZ_ASSERT(!mString || !mStringBuffer,
165
0
               "Shouldn't have both present!");
166
0
    MOZ_ASSERT(mState > State::Null,
167
0
               "Caller should have checked IsNull() and IsEmpty() first");
168
0
    return mState == State::UnownedAtom;
169
0
  }
170
171
  // Get the atom.  This can only be called if HasAtom() returned true.  If
172
  // that's true, it will never return null.
173
  nsDynamicAtom* Atom() const
174
0
  {
175
0
    MOZ_ASSERT(HasAtom(),
176
0
               "Don't ask for the atom if we don't have it");
177
0
    MOZ_ASSERT(mAtom,
178
0
               "We better have an atom if we claim to");
179
0
    return mAtom;
180
0
  }
181
182
  // Initialize the DOMString to a (nsStringBuffer, length) pair.  The length
183
  // does NOT have to be the full length of the (null-terminated) string in the
184
  // nsStringBuffer.
185
  void SetKnownLiveStringBuffer(nsStringBuffer* aStringBuffer, uint32_t aLength)
186
0
  {
187
0
    MOZ_ASSERT(mState == State::Empty, "We're already set to a value");
188
0
    if (aLength != 0) {
189
0
      SetStringBufferInternal(aStringBuffer, aLength);
190
0
      mState = State::UnownedStringBuffer;
191
0
    }
192
0
    // else nothing to do
193
0
  }
194
195
  // Like SetKnownLiveStringBuffer, but holds a reference to the nsStringBuffer.
196
  void SetStringBuffer(nsStringBuffer* aStringBuffer, uint32_t aLength)
197
0
  {
198
0
    MOZ_ASSERT(mState == State::Empty, "We're already set to a value");
199
0
    if (aLength != 0) {
200
0
      SetStringBufferInternal(aStringBuffer, aLength);
201
0
      aStringBuffer->AddRef();
202
0
      mState = State::OwnedStringBuffer;
203
0
    }
204
0
    // else nothing to do
205
0
  }
206
207
  void SetKnownLiveString(const nsAString& aString)
208
0
  {
209
0
    MOZ_ASSERT(mString.isNothing(), "We already have a string?");
210
0
    MOZ_ASSERT(mState == State::Empty, "We're already set to a value");
211
0
    MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
212
0
    if (MOZ_UNLIKELY(aString.IsVoid())) {
213
0
      SetNull();
214
0
    } else if (!aString.IsEmpty()) {
215
0
      nsStringBuffer* buf = nsStringBuffer::FromString(aString);
216
0
      if (buf) {
217
0
        SetKnownLiveStringBuffer(buf, aString.Length());
218
0
      } else if (aString.IsLiteral()) {
219
0
        SetLiteralInternal(aString.BeginReading(), aString.Length());
220
0
      } else {
221
0
        AsAString() = aString;
222
0
      }
223
0
    }
224
0
  }
225
226
  enum NullHandling
227
  {
228
    eTreatNullAsNull,
229
    eTreatNullAsEmpty,
230
    eNullNotExpected
231
  };
232
233
  void SetKnownLiveAtom(nsAtom* aAtom, NullHandling aNullHandling)
234
0
  {
235
0
    MOZ_ASSERT(mString.isNothing(), "We already have a string?");
236
0
    MOZ_ASSERT(mState == State::Empty, "We're already set to a value");
237
0
    MOZ_ASSERT(!mAtom, "Setting atom twice?");
238
0
    MOZ_ASSERT(aAtom || aNullHandling != eNullNotExpected);
239
0
    if (aNullHandling == eNullNotExpected || aAtom) {
240
0
      if (aAtom->IsStatic()) {
241
0
        // Static atoms are backed by literals.  Explicitly call AsStatic() here
242
0
        // to avoid the extra IsStatic() checks in nsAtom::GetUTF16String().
243
0
        SetLiteralInternal(aAtom->AsStatic()->GetUTF16String(),
244
0
                           aAtom->GetLength());
245
0
      } else {
246
0
        mAtom = aAtom->AsDynamic();
247
0
        mState = State::UnownedAtom;
248
0
      }
249
0
    } else if (aNullHandling == eTreatNullAsNull) {
250
0
      SetNull();
251
0
    }
252
0
  }
253
254
  void SetNull()
255
0
  {
256
0
    MOZ_ASSERT(!mStringBuffer, "Should have no stringbuffer if null");
257
0
    MOZ_ASSERT(mString.isNothing(), "Should have no string if null");
258
0
    MOZ_ASSERT(mState == State::Empty, "Already set to a value?");
259
0
    mState = State::Null;
260
0
  }
261
262
  bool IsNull() const
263
0
  {
264
0
    MOZ_ASSERT(!mStringBuffer || mString.isNothing(),
265
0
               "How could we have a stringbuffer and a nonempty string?");
266
0
    return mState == State::Null || (mString && mString->IsVoid());
267
0
  }
268
269
  bool IsEmpty() const
270
0
  {
271
0
    MOZ_ASSERT(!mStringBuffer || mString.isNothing(),
272
0
               "How could we have a stringbuffer and a nonempty string?");
273
0
    // This is not exact, because we might still have an empty XPCOM string.
274
0
    // But that's OK; in that case the callers will try the XPCOM string
275
0
    // themselves.
276
0
    return mState == State::Empty;
277
0
  }
278
279
  void ToString(nsAString& aString)
280
0
  {
281
0
    if (IsNull()) {
282
0
      SetDOMStringToNull(aString);
283
0
    } else if (IsEmpty()) {
284
0
      aString.Truncate();
285
0
    } else if (HasStringBuffer()) {
286
0
      // Don't share the nsStringBuffer with aString if the result would not
287
0
      // be null-terminated.
288
0
      nsStringBuffer* buf = StringBuffer();
289
0
      uint32_t len = StringBufferLength();
290
0
      auto chars = static_cast<char16_t*>(buf->Data());
291
0
      if (chars[len] == '\0') {
292
0
        // Safe to share the buffer.
293
0
        buf->ToString(len, aString);
294
0
      } else {
295
0
        // We need to copy, unfortunately.
296
0
        aString.Assign(chars, len);
297
0
      }
298
0
    } else if (HasLiteral()) {
299
0
      aString.AssignLiteral(Literal(), LiteralLength());
300
0
    } else if (HasAtom()) {
301
0
      mAtom->ToString(aString);
302
0
    } else {
303
0
      aString = AsAString();
304
0
    }
305
0
  }
306
307
private:
308
  void SetStringBufferInternal(nsStringBuffer* aStringBuffer, uint32_t aLength)
309
0
  {
310
0
    MOZ_ASSERT(mString.isNothing(), "We already have a string?");
311
0
    MOZ_ASSERT(mState == State::Empty, "We're already set to a value");
312
0
    MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
313
0
    MOZ_ASSERT(aStringBuffer, "Why are we getting null?");
314
0
    MOZ_ASSERT(aLength != 0, "Should not have empty string here");
315
0
    mStringBuffer = aStringBuffer;
316
0
    mLength = aLength;
317
0
  }
318
319
  void SetLiteralInternal(const char16_t* aLiteral, uint32_t aLength)
320
0
  {
321
0
    MOZ_ASSERT(!mLiteral, "What's going on here?");
322
0
    mLiteral = aLiteral;
323
0
    mLength = aLength;
324
0
    mState = State::Literal;
325
0
  }
326
327
  enum class State : uint8_t
328
  {
329
    Empty, // An empty string.  Default state.
330
    Null,  // Null (not a string at all)
331
332
    // All states that involve actual string data should come after
333
    // Empty and Null.
334
335
    String, // An XPCOM string stored in mString.
336
    Literal, // A string literal (static lifetime).
337
    UnownedAtom, // mAtom is valid and we are not holding a ref.
338
    // If we ever add an OwnedAtom state, XPCStringConvert::DynamicAtomToJSVal
339
    // will need to grow an out param for whether the atom was shared.
340
    OwnedStringBuffer, // mStringBuffer is valid and we have a ref to it.
341
    UnownedStringBuffer, // mStringBuffer is valid; we are not holding a ref.
342
    // The two string buffer values must come last.  This lets us avoid doing
343
    // two tests to figure out whether we have a stringbuffer.
344
  };
345
346
  // We need to be able to act like a string as needed
347
  Maybe<nsAutoString> mString;
348
349
  union
350
  {
351
    // The nsStringBuffer in the OwnedStringBuffer/UnownedStringBuffer cases.
352
    nsStringBuffer* MOZ_UNSAFE_REF("The ways in which this can be safe are "
353
                                 "documented above and enforced through "
354
                                 "assertions") mStringBuffer;
355
    // The literal in the Literal case.
356
    const char16_t* mLiteral;
357
    // The atom in the UnownedAtom case.
358
    nsDynamicAtom* MOZ_UNSAFE_REF("The ways in which this can be safe are "
359
                                  "documented above and enforced through "
360
                                  "assertions") mAtom;
361
  };
362
363
  // Length in the stringbuffer and literal cases.
364
  uint32_t mLength;
365
366
  State mState;
367
};
368
369
} // namespace dom
370
} // namespace mozilla
371
372
#endif // mozilla_dom_DOMString_h