Bug 943519 - Use moz_malloc/free/realloc instead of nsMemory::Alloc/Free/Realloc (fallible allocations) in nsTextFragment::Append. r=hsivonen
☠☠ backed out by b2c07abf977a ☠ ☠
authorAlessio Placitelli <alessio.placitelli@gmail.com>
Mon, 16 Dec 2013 09:01:30 -0500
changeset 160592 01de929d4b858b78f85ff666239041eeedfa9afe
parent 160591 b9c5750d818a3f3497eee00124c3c9be71c4014b
child 160593 bac23ed0f7f8c16b9258e76a962f56e5b139390e
push id37650
push userryanvm@gmail.com
push dateMon, 16 Dec 2013 14:01:31 +0000
treeherdermozilla-inbound@bac23ed0f7f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershsivonen
bugs943519
milestone29.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 943519 - Use moz_malloc/free/realloc instead of nsMemory::Alloc/Free/Realloc (fallible allocations) in nsTextFragment::Append. r=hsivonen
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsTextFragment.cpp
content/base/src/nsTextFragment.h
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -308,21 +308,23 @@ nsGenericDOMDataNode::SetTextInternal(ui
 
   if (NodeType() == nsIDOMNode::TEXT_NODE) {
     SetDirectionFromChangedTextNode(this, aOffset, aBuffer, aLength, aNotify);
   }
 
   if (aOffset == 0 && endOffset == textLength) {
     // Replacing whole text or old text was empty.  Don't bother to check for
     // bidi in this string if the document already has bidi enabled.
-    mText.SetTo(aBuffer, aLength, !document || !document->GetBidiEnabled());
+    bool ok = mText.SetTo(aBuffer, aLength, !document || !document->GetBidiEnabled());
+    NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
   }
   else if (aOffset == textLength) {
     // Appending to existing
-    mText.Append(aBuffer, aLength, !document || !document->GetBidiEnabled());
+    bool ok = mText.Append(aBuffer, aLength, !document || !document->GetBidiEnabled());
+    NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
   }
   else {
     // Merging old and new
 
     // Allocate new buffer
     int32_t newLength = textLength - aCount + aLength;
     PRUnichar* to = new PRUnichar[newLength];
     NS_ENSURE_TRUE(to, NS_ERROR_OUT_OF_MEMORY);
@@ -333,20 +335,21 @@ nsGenericDOMDataNode::SetTextInternal(ui
     }
     if (aLength) {
       memcpy(to + aOffset, aBuffer, aLength * sizeof(PRUnichar));
     }
     if (endOffset != textLength) {
       mText.CopyTo(to + aOffset + aLength, endOffset, textLength - endOffset);
     }
 
-    // XXX Add OOM checking to this
-    mText.SetTo(to, newLength, !document || !document->GetBidiEnabled());
+    bool ok = mText.SetTo(to, newLength, !document || !document->GetBidiEnabled());
 
     delete [] to;
+
+    NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
   }
 
   UnsetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE);
 
   if (document && mText.IsBidi()) {
     // If we found bidi characters in mText.SetTo() above, indicate that the
     // document contains bidi characters.
     document->SetBidiEnabled();
--- a/content/base/src/nsTextFragment.cpp
+++ b/content/base/src/nsTextFragment.cpp
@@ -79,17 +79,17 @@ nsTextFragment::~nsTextFragment()
   ReleaseText();
   MOZ_COUNT_DTOR(nsTextFragment);
 }
 
 void
 nsTextFragment::ReleaseText()
 {
   if (mState.mLength && m1b && mState.mInHeap) {
-    nsMemory::Free(m2b); // m1b == m2b as far as nsMemory is concerned
+    moz_free(m2b); // m1b == m2b as far as moz_free is concerned
   }
 
   m1b = nullptr;
   mState.mIsBidi = false;
 
   // Set mState.mIs2b, mState.mInHeap, and mState.mLength = 0 with mAllBits;
   mAllBits = 0;
 }
@@ -99,19 +99,30 @@ nsTextFragment::operator=(const nsTextFr
 {
   ReleaseText();
 
   if (aOther.mState.mLength) {
     if (!aOther.mState.mInHeap) {
       m1b = aOther.m1b; // This will work even if aOther is using m2b
     }
     else {
-      m2b = static_cast<PRUnichar*>
-                       (nsMemory::Clone(aOther.m2b, aOther.mState.mLength *
-                                    (aOther.mState.mIs2b ? sizeof(PRUnichar) : sizeof(char))));
+      size_t m2bSize = aOther.mState.mLength *
+        (aOther.mState.mIs2b ? sizeof(PRUnichar) : sizeof(char));
+
+      m2b = static_cast<PRUnichar*>(moz_malloc(m2bSize));
+      if (m2b) {
+        memcpy(m2b, aOther.m2b, m2bSize);
+      } else {
+        // allocate a buffer for a single REPLACEMENT CHARACTER
+        m2b = static_cast<PRUnichar*>(moz_xmalloc(sizeof(PRUnichar)));
+        m2b[0] = 0xFFFD; // REPLACEMENT CHARACTER
+        mState.mIs2b = true;
+        mState.mInHeap = true;
+        mState.mLength = 1;
+      }
     }
 
     if (m1b) {
       mAllBits = aOther.mAllBits;
     }
   }
 
   return *this;
@@ -174,33 +185,33 @@ FirstNon8Bit(const PRUnichar *str, const
   if (mozilla::supports_sse2()) {
     return mozilla::SSE2::FirstNon8Bit(str, end);
   }
 #endif
 
   return FirstNon8BitUnvectorized(str, end);
 }
 
-void
+bool
 nsTextFragment::SetTo(const PRUnichar* aBuffer, int32_t aLength, bool aUpdateBidi)
 {
   ReleaseText();
 
   if (aLength == 0) {
-    return;
+    return true;
   }
   
   PRUnichar firstChar = *aBuffer;
   if (aLength == 1 && firstChar < 256) {
     m1b = sSingleCharSharedString + firstChar;
     mState.mInHeap = false;
     mState.mIs2b = false;
     mState.mLength = 1;
 
-    return;
+    return true;
   }
 
   const PRUnichar *ucp = aBuffer;
   const PRUnichar *uend = aBuffer + aLength;
 
   // Check if we can use a shared string
   if (aLength <= 1 + TEXTFRAG_WHITE_AFTER_NEWLINE + TEXTFRAG_MAX_NEWLINES &&
      (firstChar == ' ' || firstChar == '\n' || firstChar == '\t')) {
@@ -229,53 +240,56 @@ nsTextFragment::SetTo(const PRUnichar* a
       if (firstChar != ' ') {
         ++m1b;
       }
 
       mState.mInHeap = false;
       mState.mIs2b = false;
       mState.mLength = aLength;
 
-      return;        
+      return true;        
     }
   }
 
   // See if we need to store the data in ucs2 or not
   int32_t first16bit = FirstNon8Bit(ucp, uend);
 
   if (first16bit != -1) { // aBuffer contains no non-8bit character
     // Use ucs2 storage because we have to
-    m2b = (PRUnichar *)nsMemory::Clone(aBuffer,
-                                       aLength * sizeof(PRUnichar));
+    size_t m2bSize = aLength * sizeof(PRUnichar);
+    m2b = (PRUnichar *)moz_malloc(m2bSize);
     if (!m2b) {
-      return;
+      return false;
     }
+    memcpy(m2b, aBuffer, m2bSize);
 
     mState.mIs2b = true;
     if (aUpdateBidi) {
       UpdateBidiFlag(aBuffer + first16bit, aLength - first16bit);
     }
 
   } else {
     // Use 1 byte storage because we can
-    char* buff = (char *)nsMemory::Alloc(aLength * sizeof(char));
+    char* buff = (char *)moz_malloc(aLength * sizeof(char));
     if (!buff) {
-      return;
+      return false;
     }
 
     // Copy data
     LossyConvertEncoding16to8 converter(buff);
     copy_string(aBuffer, aBuffer+aLength, converter);
     m1b = buff;
     mState.mIs2b = false;
   }
 
   // Setup our fields
   mState.mInHeap = true;
   mState.mLength = aLength;
+
+  return true;
 }
 
 void
 nsTextFragment::CopyTo(PRUnichar *aDest, int32_t aOffset, int32_t aCount)
 {
   NS_ASSERTION(aOffset >= 0, "Bad offset passed to nsTextFragment::CopyTo()!");
   NS_ASSERTION(aCount >= 0, "Bad count passed to nsTextFragment::CopyTo()!");
 
@@ -294,107 +308,106 @@ nsTextFragment::CopyTo(PRUnichar *aDest,
       const char *cp = m1b + aOffset;
       const char *end = cp + aCount;
       LossyConvertEncoding8to16 converter(aDest);
       copy_string(cp, end, converter);
     }
   }
 }
 
-void
+bool
 nsTextFragment::Append(const PRUnichar* aBuffer, uint32_t aLength, bool aUpdateBidi)
 {
   // This is a common case because some callsites create a textnode
   // with a value by creating the node and then calling AppendData.
   if (mState.mLength == 0) {
-    SetTo(aBuffer, aLength, aUpdateBidi);
-
-    return;
+    return SetTo(aBuffer, aLength, aUpdateBidi);
   }
 
   // Should we optimize for aData.Length() == 0?
 
   if (mState.mIs2b) {
     // Already a 2-byte string so the result will be too
-    PRUnichar* buff = (PRUnichar*)nsMemory::Realloc(m2b, (mState.mLength + aLength) * sizeof(PRUnichar));
+    PRUnichar* buff = (PRUnichar*)moz_realloc(m2b, (mState.mLength + aLength) * sizeof(PRUnichar));
     if (!buff) {
-      return;
+      return false;
     }
 
     memcpy(buff + mState.mLength, aBuffer, aLength * sizeof(PRUnichar));
     mState.mLength += aLength;
     m2b = buff;
 
     if (aUpdateBidi) {
       UpdateBidiFlag(aBuffer, aLength);
     }
 
-    return;
+    return true;
   }
 
   // Current string is a 1-byte string, check if the new data fits in one byte too.
   int32_t first16bit = FirstNon8Bit(aBuffer, aBuffer + aLength);
 
   if (first16bit != -1) { // aBuffer contains no non-8bit character
     // The old data was 1-byte, but the new is not so we have to expand it
     // all to 2-byte
-    PRUnichar* buff = (PRUnichar*)nsMemory::Alloc((mState.mLength + aLength) *
+    PRUnichar* buff = (PRUnichar*)moz_malloc((mState.mLength + aLength) *
                                                   sizeof(PRUnichar));
     if (!buff) {
-      return;
+      return false;
     }
 
     // Copy data into buff
     LossyConvertEncoding8to16 converter(buff);
     copy_string(m1b, m1b+mState.mLength, converter);
 
     memcpy(buff + mState.mLength, aBuffer, aLength * sizeof(PRUnichar));
     mState.mLength += aLength;
     mState.mIs2b = true;
 
     if (mState.mInHeap) {
-      nsMemory::Free(m2b);
+      moz_free(m2b);
     }
     m2b = buff;
 
     mState.mInHeap = true;
 
     if (aUpdateBidi) {
       UpdateBidiFlag(aBuffer + first16bit, aLength - first16bit);
     }
 
-    return;
+    return true;
   }
 
   // The new and the old data is all 1-byte
   char* buff;
   if (mState.mInHeap) {
-    buff = (char*)nsMemory::Realloc(const_cast<char*>(m1b),
+    buff = (char*)moz_realloc(const_cast<char*>(m1b),
                                     (mState.mLength + aLength) * sizeof(char));
     if (!buff) {
-      return;
+      return false;
     }
   }
   else {
-    buff = (char*)nsMemory::Alloc((mState.mLength + aLength) * sizeof(char));
+    buff = (char*)moz_malloc((mState.mLength + aLength) * sizeof(char));
     if (!buff) {
-      return;
+      return false;
     }
 
     memcpy(buff, m1b, mState.mLength);
     mState.mInHeap = true;
   }
 
   // Copy aBuffer into buff.
   LossyConvertEncoding16to8 converter(buff + mState.mLength);
   copy_string(aBuffer, aBuffer + aLength, converter);
 
   m1b = buff;
   mState.mLength += aLength;
 
+  return true;
 }
 
 /* virtual */ size_t
 nsTextFragment::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   if (Is2b()) {
     return aMallocSizeOf(m2b);
   }
--- a/content/base/src/nsTextFragment.h
+++ b/content/base/src/nsTextFragment.h
@@ -48,17 +48,17 @@ public:
     MOZ_COUNT_CTOR(nsTextFragment);
     NS_ASSERTION(sizeof(FragmentBits) == 4, "Bad field packing!");
   }
 
   ~nsTextFragment();
 
   /**
    * Change the contents of this fragment to be a copy of the
-   * the argument fragment.
+   * the argument fragment, or to "" if unable to allocate enough memory.
    */
   nsTextFragment& operator=(const nsTextFragment& aOther);
 
   /**
    * Return true if this fragment is represented by PRUnichar data
    */
   bool Is2b() const
   {
@@ -107,24 +107,24 @@ public:
     return n < (1 << 29) && mState.mLength + n < (1 << 29);
   }
 
   /**
    * Change the contents of this fragment to be a copy of the given
    * buffer. If aUpdateBidi is true, contents of the fragment will be scanned,
    * and mState.mIsBidi will be turned on if it includes any Bidi characters.
    */
-  void SetTo(const PRUnichar* aBuffer, int32_t aLength, bool aUpdateBidi);
+  bool SetTo(const PRUnichar* aBuffer, int32_t aLength, bool aUpdateBidi);
 
   /**
    * Append aData to the end of this fragment. If aUpdateBidi is true, contents
    * of the fragment will be scanned, and mState.mIsBidi will be turned on if
    * it includes any Bidi characters.
    */
-  void Append(const PRUnichar* aBuffer, uint32_t aLength, bool aUpdateBidi);
+  bool Append(const PRUnichar* aBuffer, uint32_t aLength, bool aUpdateBidi);
 
   /**
    * Append the contents of this string fragment to aString
    */
   void AppendTo(nsAString& aString) const {
     if (mState.mIs2b) {
       aString.Append(m2b, mState.mLength);
     } else {