LCOV - code coverage report
Current view: top level - dom/bindings - DOMString.h (source / functions) Hit Total Coverage
Test: output.info Lines: 70 95 73.7 %
Date: 2017-07-14 16:53:18 Functions: 13 15 86.7 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13