Bug 1330759 part 1. Change various bits of DOMString code to work better when it has a stringbuffer which is effectively not null-terminated (in the sense that indexing into it at the DOMString's length doesn't yield '\0'). r=froydnj
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 18 Jan 2017 22:20:14 -0500
changeset 375088 1cc3dbae3b31066949a250f74313d77769956036
parent 375087 0d0db76e4a87c71cf11f66cad001e60025aeeda4
child 375089 3afcfb43c465cd48e8ca859ae21808c84cb52b43
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1330759
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1330759 part 1. Change various bits of DOMString code to work better when it has a stringbuffer which is effectively not null-terminated (in the sense that indexing into it at the DOMString's length doesn't yield '\0'). r=froydnj
dom/bindings/DOMString.h
--- a/dom/bindings/DOMString.h
+++ b/dom/bindings/DOMString.h
@@ -76,17 +76,19 @@ public:
     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.
+  // 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.");
@@ -96,16 +98,19 @@ public:
   // 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;
   }
 
+  // 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;
@@ -163,17 +168,28 @@ public:
   void ToString(nsAString& aString)
   {
     if (IsNull()) {
       SetDOMStringToNull(aString);
     } else if (HasStringBuffer()) {
       if (StringBufferLength() == 0) {
         aString.Truncate();
       } else {
-        StringBuffer()->ToString(StringBufferLength(), aString);
+        // 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