summaryrefslogtreecommitdiffstats
path: root/dom/bindings/DOMString.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/bindings/DOMString.h')
-rw-r--r--dom/bindings/DOMString.h245
1 files changed, 245 insertions, 0 deletions
diff --git a/dom/bindings/DOMString.h b/dom/bindings/DOMString.h
new file mode 100644
index 000000000..72c8445ec
--- /dev/null
+++ b/dom/bindings/DOMString.h
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_DOMString_h
+#define mozilla_dom_DOMString_h
+
+#include "nsStringGlue.h"
+#include "nsStringBuffer.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "nsDOMString.h"
+#include "nsIAtom.h"
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * A class for representing string return values. This can be either passed to
+ * callees that have an nsString or nsAString out param or passed to a callee
+ * that actually knows about this class and can work with it. Such a callee may
+ * call SetStringBuffer or SetEphemeralStringBuffer or SetOwnedString or
+ * SetOwnedAtom on this object. It's only OK to call
+ * SetStringBuffer/SetOwnedString/SetOwnedAtom if the caller of the method in
+ * question plans to keep holding a strong ref to the stringbuffer involved,
+ * whether it's a raw nsStringBuffer, or stored inside the string or atom being
+ * passed. In the string/atom cases that means the caller must own the string
+ * or atom, and not mutate it (in the string case) for the lifetime of the
+ * DOMString.
+ *
+ * The proper way to store a value in this class is to either to do nothing
+ * (which leaves this as an empty string), to call
+ * SetStringBuffer/SetEphemeralStringBuffer with a non-null stringbuffer, to
+ * call SetOwnedString, to call SetOwnedAtom, to call SetNull(), or to call
+ * AsAString() and set the value in the resulting nsString. These options are
+ * mutually exclusive! Don't do more than one of them.
+ *
+ * The proper way to extract a value is to check IsNull(). If not null, then
+ * check HasStringBuffer(). If that's true, check for a zero length, and if the
+ * length is nonzero call StringBuffer(). If the length is zero this is the
+ * empty string. If HasStringBuffer() returns false, call AsAString() and get
+ * the value from that.
+ */
+class MOZ_STACK_CLASS DOMString {
+public:
+ DOMString()
+ : mStringBuffer(nullptr)
+ , mLength(0)
+ , mIsNull(false)
+ , mStringBufferOwned(false)
+ {}
+ ~DOMString()
+ {
+ MOZ_ASSERT(!mString || !mStringBuffer,
+ "Shouldn't have both present!");
+ if (mStringBufferOwned) {
+ MOZ_ASSERT(mStringBuffer);
+ mStringBuffer->Release();
+ }
+ }
+
+ operator nsString&()
+ {
+ return AsAString();
+ }
+
+ // It doesn't make any sense to convert a DOMString to a const nsString or
+ // nsAString reference; this class is meant for outparams only.
+ operator const nsString&() = delete;
+ operator const nsAString&() = delete;
+
+ nsString& AsAString()
+ {
+ MOZ_ASSERT(!mStringBuffer, "We already have a stringbuffer?");
+ MOZ_ASSERT(!mIsNull, "We're already set as null");
+ if (!mString) {
+ mString.emplace();
+ }
+ return *mString;
+ }
+
+ bool HasStringBuffer() const
+ {
+ MOZ_ASSERT(!mString || !mStringBuffer,
+ "Shouldn't have both present!");
+ MOZ_ASSERT(!mIsNull, "Caller should have checked IsNull() first");
+ return !mString;
+ }
+
+ // Get the stringbuffer. This can only be called if HasStringBuffer()
+ // returned true and StringBufferLength() is nonzero. If that's true, it will
+ // never return null. Note that constructing a string from this
+ // nsStringBuffer with length given by StringBufferLength() might give you
+ // something that is not null-terminated.
+ nsStringBuffer* StringBuffer() const
+ {
+ MOZ_ASSERT(!mIsNull, "Caller should have checked IsNull() first");
+ MOZ_ASSERT(HasStringBuffer(),
+ "Don't ask for the stringbuffer if we don't have it");
+ MOZ_ASSERT(StringBufferLength() != 0, "Why are you asking for this?");
+ MOZ_ASSERT(mStringBuffer,
+ "If our length is nonzero, we better have a stringbuffer.");
+ return mStringBuffer;
+ }
+
+ // Get the length of the stringbuffer. Can only be called if
+ // HasStringBuffer().
+ uint32_t StringBufferLength() const
+ {
+ MOZ_ASSERT(HasStringBuffer(), "Don't call this if there is no stringbuffer");
+ return mLength;
+ }
+
+ // Tell the DOMString to relinquish ownership of its nsStringBuffer to the
+ // caller. Can only be called if HasStringBuffer().
+ void RelinquishBufferOwnership()
+ {
+ MOZ_ASSERT(HasStringBuffer(), "Don't call this if there is no stringbuffer");
+ if (mStringBufferOwned) {
+ // Just hand that ref over.
+ mStringBufferOwned = false;
+ } else {
+ // Caller should end up holding a ref.
+ mStringBuffer->AddRef();
+ }
+ }
+
+ // Initialize the DOMString to a (nsStringBuffer, length) pair. The length
+ // does NOT have to be the full length of the (null-terminated) string in the
+ // nsStringBuffer.
+ void SetStringBuffer(nsStringBuffer* aStringBuffer, uint32_t aLength)
+ {
+ MOZ_ASSERT(mString.isNothing(), "We already have a string?");
+ MOZ_ASSERT(!mIsNull, "We're already set as null");
+ MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
+ MOZ_ASSERT(aStringBuffer, "Why are we getting null?");
+ mStringBuffer = aStringBuffer;
+ mLength = aLength;
+ }
+
+ // Like SetStringBuffer, but holds a reference to the nsStringBuffer.
+ void SetEphemeralStringBuffer(nsStringBuffer* aStringBuffer, uint32_t aLength)
+ {
+ // We rely on SetStringBuffer to ensure our state invariants.
+ SetStringBuffer(aStringBuffer, aLength);
+ aStringBuffer->AddRef();
+ mStringBufferOwned = true;
+ }
+
+ void SetOwnedString(const nsAString& aString)
+ {
+ MOZ_ASSERT(mString.isNothing(), "We already have a string?");
+ MOZ_ASSERT(!mIsNull, "We're already set as null");
+ MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
+ nsStringBuffer* buf = nsStringBuffer::FromString(aString);
+ if (buf) {
+ SetStringBuffer(buf, aString.Length());
+ } else if (aString.IsVoid()) {
+ SetNull();
+ } else if (!aString.IsEmpty()) {
+ AsAString() = aString;
+ }
+ }
+
+ enum NullHandling
+ {
+ eTreatNullAsNull,
+ eTreatNullAsEmpty,
+ eNullNotExpected
+ };
+
+ void SetOwnedAtom(nsIAtom* aAtom, NullHandling aNullHandling)
+ {
+ MOZ_ASSERT(mString.isNothing(), "We already have a string?");
+ MOZ_ASSERT(!mIsNull, "We're already set as null");
+ MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
+ MOZ_ASSERT(aAtom || aNullHandling != eNullNotExpected);
+ if (aNullHandling == eNullNotExpected || aAtom) {
+ SetStringBuffer(aAtom->GetStringBuffer(), aAtom->GetLength());
+ } else if (aNullHandling == eTreatNullAsNull) {
+ SetNull();
+ }
+ }
+
+ void SetNull()
+ {
+ MOZ_ASSERT(!mStringBuffer, "Should have no stringbuffer if null");
+ MOZ_ASSERT(mString.isNothing(), "Should have no string if null");
+ mIsNull = true;
+ }
+
+ bool IsNull() const
+ {
+ MOZ_ASSERT(!mStringBuffer || mString.isNothing(),
+ "How could we have a stringbuffer and a nonempty string?");
+ return mIsNull || (mString && mString->IsVoid());
+ }
+
+ void ToString(nsAString& aString)
+ {
+ if (IsNull()) {
+ SetDOMStringToNull(aString);
+ } else if (HasStringBuffer()) {
+ if (StringBufferLength() == 0) {
+ aString.Truncate();
+ } else {
+ // Don't share the nsStringBuffer with aString if the result would not
+ // be null-terminated.
+ nsStringBuffer* buf = StringBuffer();
+ uint32_t len = StringBufferLength();
+ auto chars = static_cast<char16_t*>(buf->Data());
+ if (chars[len] == '\0') {
+ // Safe to share the buffer.
+ buf->ToString(len, aString);
+ } else {
+ // We need to copy, unfortunately.
+ aString.Assign(chars, len);
+ }
+ }
+ } else {
+ aString = AsAString();
+ }
+ }
+
+private:
+ // We need to be able to act like a string as needed
+ Maybe<nsAutoString> mString;
+
+ // For callees that know we exist, we can be a stringbuffer/length/null-flag
+ // triple.
+ nsStringBuffer* MOZ_UNSAFE_REF("The ways in which this can be safe are "
+ "documented above and enforced through "
+ "assertions") mStringBuffer;
+ uint32_t mLength;
+ bool mIsNull;
+ bool mStringBufferOwned;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_DOMString_h