DOMString.h 8.2 KB

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