michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_dom_DOMString_h michael@0: #define mozilla_dom_DOMString_h michael@0: michael@0: #include "nsStringGlue.h" michael@0: #include "nsStringBuffer.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/Maybe.h" michael@0: #include "nsDOMString.h" michael@0: #include "nsIAtom.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: /** michael@0: * A class for representing string return values. This can be either passed to michael@0: * callees that have an nsString or nsAString out param or passed to a callee michael@0: * that actually knows about this class and can work with it. Such a callee may michael@0: * call SetStringBuffer or SetOwnedString or SetOwnedAtom on this object, but michael@0: * only if it plans to keep holding a strong ref to the internal stringbuffer! michael@0: * michael@0: * The proper way to store a value in this class is to either to do nothing michael@0: * (which leaves this as an empty string), to call SetStringBuffer with a michael@0: * non-null stringbuffer, to call SetOwnedString, to call SetOwnedAtom, to call michael@0: * SetNull(), or to call AsAString() and set the value in the resulting michael@0: * nsString. These options are mutually exclusive! Don't do more than one of michael@0: * them. michael@0: * michael@0: * The proper way to extract a value is to check IsNull(). If not null, then michael@0: * check HasStringBuffer(). If that's true, check for a zero length, and if the michael@0: * length is nonzero call StringBuffer(). If the length is zero this is the michael@0: * empty string. If HasStringBuffer() returns false, call AsAString() and get michael@0: * the value from that. michael@0: */ michael@0: class MOZ_STACK_CLASS DOMString { michael@0: public: michael@0: DOMString() michael@0: : mStringBuffer(nullptr) michael@0: , mLength(0) michael@0: , mIsNull(false) michael@0: {} michael@0: ~DOMString() michael@0: { michael@0: MOZ_ASSERT(mString.empty() || !mStringBuffer, michael@0: "Shouldn't have both present!"); michael@0: } michael@0: michael@0: operator nsString&() michael@0: { michael@0: return AsAString(); michael@0: } michael@0: michael@0: nsString& AsAString() michael@0: { michael@0: MOZ_ASSERT(!mStringBuffer, "We already have a stringbuffer?"); michael@0: MOZ_ASSERT(!mIsNull, "We're already set as null"); michael@0: if (mString.empty()) { michael@0: mString.construct(); michael@0: } michael@0: return mString.ref(); michael@0: } michael@0: michael@0: bool HasStringBuffer() const michael@0: { michael@0: MOZ_ASSERT(mString.empty() || !mStringBuffer, michael@0: "Shouldn't have both present!"); michael@0: MOZ_ASSERT(!mIsNull, "Caller should have checked IsNull() first"); michael@0: return mString.empty(); michael@0: } michael@0: michael@0: // Get the stringbuffer. This can only be called if HasStringBuffer() michael@0: // returned true and StringBufferLength() is nonzero. If that's true, it will michael@0: // never return null. michael@0: nsStringBuffer* StringBuffer() const michael@0: { michael@0: MOZ_ASSERT(!mIsNull, "Caller should have checked IsNull() first"); michael@0: MOZ_ASSERT(HasStringBuffer(), michael@0: "Don't ask for the stringbuffer if we don't have it"); michael@0: MOZ_ASSERT(StringBufferLength() != 0, "Why are you asking for this?"); michael@0: MOZ_ASSERT(mStringBuffer, michael@0: "If our length is nonzero, we better have a stringbuffer."); michael@0: return mStringBuffer; michael@0: } michael@0: michael@0: // Get the length of the stringbuffer. Can only be called if michael@0: // HasStringBuffer(). michael@0: uint32_t StringBufferLength() const michael@0: { michael@0: MOZ_ASSERT(HasStringBuffer(), "Don't call this if there is no stringbuffer"); michael@0: return mLength; michael@0: } michael@0: michael@0: void SetStringBuffer(nsStringBuffer* aStringBuffer, uint32_t aLength) michael@0: { michael@0: MOZ_ASSERT(mString.empty(), "We already have a string?"); michael@0: MOZ_ASSERT(!mIsNull, "We're already set as null"); michael@0: MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?"); michael@0: MOZ_ASSERT(aStringBuffer, "Why are we getting null?"); michael@0: mStringBuffer = aStringBuffer; michael@0: mLength = aLength; michael@0: } michael@0: michael@0: void SetOwnedString(const nsAString& aString) michael@0: { michael@0: MOZ_ASSERT(mString.empty(), "We already have a string?"); michael@0: MOZ_ASSERT(!mIsNull, "We're already set as null"); michael@0: MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?"); michael@0: nsStringBuffer* buf = nsStringBuffer::FromString(aString); michael@0: if (buf) { michael@0: SetStringBuffer(buf, aString.Length()); michael@0: } else if (aString.IsVoid()) { michael@0: SetNull(); michael@0: } else if (!aString.IsEmpty()) { michael@0: AsAString() = aString; michael@0: } michael@0: } michael@0: michael@0: enum NullHandling michael@0: { michael@0: eTreatNullAsNull, michael@0: eTreatNullAsEmpty, michael@0: eNullNotExpected michael@0: }; michael@0: michael@0: void SetOwnedAtom(nsIAtom* aAtom, NullHandling aNullHandling) michael@0: { michael@0: MOZ_ASSERT(mString.empty(), "We already have a string?"); michael@0: MOZ_ASSERT(!mIsNull, "We're already set as null"); michael@0: MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?"); michael@0: MOZ_ASSERT(aAtom || aNullHandling != eNullNotExpected); michael@0: if (aNullHandling == eNullNotExpected || aAtom) { michael@0: SetStringBuffer(aAtom->GetStringBuffer(), aAtom->GetLength()); michael@0: } else if (aNullHandling == eTreatNullAsNull) { michael@0: SetNull(); michael@0: } michael@0: } michael@0: michael@0: void SetNull() michael@0: { michael@0: MOZ_ASSERT(!mStringBuffer, "Should have no stringbuffer if null"); michael@0: MOZ_ASSERT(mString.empty(), "Should have no string if null"); michael@0: mIsNull = true; michael@0: } michael@0: michael@0: bool IsNull() const michael@0: { michael@0: MOZ_ASSERT(!mStringBuffer || mString.empty(), michael@0: "How could we have a stringbuffer and a nonempty string?"); michael@0: return mIsNull || (!mString.empty() && mString.ref().IsVoid()); michael@0: } michael@0: michael@0: void ToString(nsAString& aString) michael@0: { michael@0: if (IsNull()) { michael@0: SetDOMStringToNull(aString); michael@0: } else if (HasStringBuffer()) { michael@0: if (StringBufferLength() == 0) { michael@0: aString.Truncate(); michael@0: } else { michael@0: StringBuffer()->ToString(StringBufferLength(), aString); michael@0: } michael@0: } else { michael@0: aString = AsAString(); michael@0: } michael@0: } michael@0: michael@0: private: michael@0: // We need to be able to act like a string as needed michael@0: Maybe mString; michael@0: michael@0: // For callees that know we exist, we can be a stringbuffer/length/null-flag michael@0: // triple. michael@0: nsStringBuffer* mStringBuffer; michael@0: uint32_t mLength; michael@0: bool mIsNull; michael@0: }; michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: #endif // mozilla_dom_DOMString_h