Bug 1097452 - Use fallible allocation when setting the value of an <input> or <textarea> element; r=jst,froydnj
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 07 Jan 2015 20:14:01 -0500
changeset 223909 27ae894ee4d70e08dea4e335352aa2afd810e82a
parent 223908 e4dd444e5c88da36ef88bc602fcda4f98f83be4f
child 223910 49d0c7894393162134e4ea61d969866fb3ce6b1e
push id10836
push usercbook@mozilla.com
push dateThu, 15 Jan 2015 13:41:01 +0000
treeherderfx-team@b60c4d96e3ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst, froydnj
bugs1097452
milestone38.0a1
Bug 1097452 - Use fallible allocation when setting the value of an <input> or <textarea> element; r=jst,froydnj This patch handles most of the call sites for these allocations except for a few where I added TODO comments with some information. Handling those places may require reworking lots of code, so I prefer to not do that here.
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/html/HTMLInputElement.cpp
dom/html/HTMLTextAreaElement.cpp
dom/html/nsTextEditorState.cpp
dom/html/nsTextEditorState.h
xpcom/string/nsReadableUtils.cpp
xpcom/string/nsReadableUtils.h
xpcom/string/nsTString.h
xpcom/string/nsTStringObsolete.cpp
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -6254,25 +6254,41 @@ void nsContentUtils::RemoveNewlines(nsSt
   // strip CR/LF and null
   static const char badChars[] = {'\r', '\n', 0};
   aString.StripChars(badChars);
 }
 
 void
 nsContentUtils::PlatformToDOMLineBreaks(nsString &aString)
 {
+  if (!PlatformToDOMLineBreaks(aString, mozilla::fallible_t())) {
+    aString.AllocFailed(aString.Length());
+  }
+}
+
+bool
+nsContentUtils::PlatformToDOMLineBreaks(nsString& aString, const fallible_t& aFallible)
+{
   if (aString.FindChar(char16_t('\r')) != -1) {
     // Windows linebreaks: Map CRLF to LF:
-    aString.ReplaceSubstring(MOZ_UTF16("\r\n"),
-                             MOZ_UTF16("\n"));
+    if (!aString.ReplaceSubstring(MOZ_UTF16("\r\n"),
+                                  MOZ_UTF16("\n"),
+                                  aFallible)) {
+      return false;
+    }
 
     // Mac linebreaks: Map any remaining CR to LF:
-    aString.ReplaceSubstring(MOZ_UTF16("\r"),
-                             MOZ_UTF16("\n"));
-  }
+    if (!aString.ReplaceSubstring(MOZ_UTF16("\r"),
+                                  MOZ_UTF16("\n"),
+                                  aFallible)) {
+      return false;
+    }
+  }
+
+  return true;
 }
 
 void
 nsContentUtils::PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
                                                nsAString& aResultString)
 {
   MOZ_ASSERT(aBuf, "Expecting a non-null string buffer");
 
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1710,16 +1710,18 @@ public:
    */
   static void RemoveNewlines(nsString &aString);
 
   /**
    * Convert Windows and Mac platform linebreaks to \n.
    * @param aString the string to convert the newlines inside [in/out]
    */
   static void PlatformToDOMLineBreaks(nsString &aString);
+  static NS_WARN_UNUSED_RESULT bool PlatformToDOMLineBreaks(nsString &aString,
+                                                            const mozilla::fallible_t&);
 
   /**
    * Populates aResultString with the contents of the string-buffer aBuf, up
    * to aBuf's null-terminator.  aBuf must not be null. Ownership of the string
    * is not transferred.
    */
   static void PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
                                              nsAString& aResultString);
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -1266,17 +1266,18 @@ HTMLInputElement::Clone(mozilla::dom::No
   switch (GetValueMode()) {
     case VALUE_MODE_VALUE:
       if (mValueChanged) {
         // We don't have our default value anymore.  Set our value on
         // the clone.
         nsAutoString value;
         GetValueInternal(value);
         // SetValueInternal handles setting the VALUE_CHANGED bit for us
-        it->SetValueInternal(value, false, true);
+        rv = it->SetValueInternal(value, false, true);
+        NS_ENSURE_SUCCESS(rv, rv);
       }
       break;
     case VALUE_MODE_FILENAME:
       if (it->OwnerDoc()->IsStaticDocument()) {
         // We're going to be used in print preview.  Since the doc is static
         // we can just grab the pretty string and use it as wallpaper
         GetDisplayFileName(it->mStaticDocFileList);
       } else {
@@ -1441,39 +1442,42 @@ HTMLInputElement::AfterSetAttr(int32_t a
         // prevented there being a valid step in range). Changing @max to/from
         // 1 and a number greater than on equal to 3 should change whether we
         // have a step mismatch or not.
         // The value may also need to change between a value that results in
         // a step mismatch and a value that results in overflow. For example,
         // if @max in the example above were to change from 1 to -1.
         nsAutoString value;
         GetValue(value);
-        SetValueInternal(value, false, false);
+        nsresult rv = SetValueInternal(value, false, false);
+        NS_ENSURE_SUCCESS(rv, rv);
         MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                    "HTML5 spec does not allow this");
       }
     } else if (aName == nsGkAtoms::min) {
       UpdateHasRange();
       UpdateRangeUnderflowValidityState();
       UpdateStepMismatchValidityState();
       if (mType == NS_FORM_INPUT_RANGE) {
         // See @max comment
         nsAutoString value;
         GetValue(value);
-        SetValueInternal(value, false, false);
+        nsresult rv = SetValueInternal(value, false, false);
+        NS_ENSURE_SUCCESS(rv, rv);
         MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                    "HTML5 spec does not allow this");
       }
     } else if (aName == nsGkAtoms::step) {
       UpdateStepMismatchValidityState();
       if (mType == NS_FORM_INPUT_RANGE) {
         // See @max comment
         nsAutoString value;
         GetValue(value);
-        SetValueInternal(value, false, false);
+        nsresult rv = SetValueInternal(value, false, false);
+        NS_ENSURE_SUCCESS(rv, rv);
         MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                    "HTML5 spec does not allow this");
       }
     } else if (aName == nsGkAtoms::dir &&
                aValue && aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) {
       SetDirectionIfAuto(true, aNotify);
     } else if (aName == nsGkAtoms::lang) {
       if (mType == NS_FORM_INPUT_NUMBER) {
@@ -1833,23 +1837,31 @@ HTMLInputElement::SetValue(const nsAStri
       // event, we should keep it that way. Otherwise, we should make sure the
       // element will not fire any event because of the script interaction.
       //
       // NOTE: this is currently quite expensive work (too much string
       // manipulation). We should probably optimize that.
       nsAutoString currentValue;
       GetValue(currentValue);
 
-      SetValueInternal(aValue, false, true);
+      nsresult rv = SetValueInternal(aValue, false, true);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(rv);
+        return;
+      }
 
       if (mFocusedValue.Equals(currentValue)) {
         GetValue(mFocusedValue);
       }
     } else {
-      SetValueInternal(aValue, false, true);
+      nsresult rv = SetValueInternal(aValue, false, true);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(rv);
+        return;
+      }
     }
   }
 }
 
 NS_IMETHODIMP
 HTMLInputElement::SetValue(const nsAString& aValue)
 {
   ErrorResult rv;
@@ -2408,17 +2420,18 @@ HTMLInputElement::SetUserInput(const nsA
 
   if (mType == NS_FORM_INPUT_FILE)
   {
     Sequence<nsString> list;
     list.AppendElement(aValue);
     MozSetFileNameArray(list);
     return NS_OK;
   } else {
-    SetValueInternal(aValue, true, true);
+    nsresult rv = SetValueInternal(aValue, true, true);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
                                        static_cast<nsIDOMHTMLInputElement*>(this),
                                        NS_LITERAL_STRING("input"), true,
                                        true);
 
   // If this element is not currently focused, it won't receive a change event for this
@@ -2834,17 +2847,19 @@ HTMLInputElement::SetValueInternal(const
       }
       // else DoneCreatingElement calls us again once mParserCreating is false
 
       if (aSetValueChanged) {
         SetValueChanged(true);
       }
 
       if (IsSingleLineTextControl(false)) {
-        mInputData.mState->SetValue(value, aUserInput, aSetValueChanged);
+        if (!mInputData.mState->SetValue(value, aUserInput, aSetValueChanged)) {
+          return NS_ERROR_OUT_OF_MEMORY;
+        }
         if (mType == NS_FORM_INPUT_EMAIL) {
           UpdateAllValidityStates(mParserCreating);
         }
       } else {
         nsMemory::Free(mInputData.mValue);
         mInputData.mValue = ToNewUnicode(value);
         if (aSetValueChanged) {
           SetValueChanged(true);
@@ -3429,17 +3444,18 @@ HTMLInputElement::PreHandleEvent(EventCh
   // Fire onchange (if necessary), before we do the blur, bug 357684.
   if (aVisitor.mEvent->message == NS_BLUR_CONTENT) {
     // Experimental mobile types rely on the system UI to prevent users to not
     // set invalid values but we have to be extra-careful. Especially if the
     // option has been enabled on desktop.
     if (IsExperimentalMobileType(mType)) {
       nsAutoString aValue;
       GetValueInternal(aValue);
-      SetValueInternal(aValue, false, false);
+      nsresult rv = SetValueInternal(aValue, false, false);
+      NS_ENSURE_SUCCESS(rv, rv);
     }
     FireChangeEventIfNeeded();
   }
 
   if (mType == NS_FORM_INPUT_RANGE &&
       (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
        aVisitor.mEvent->message == NS_BLUR_CONTENT)) {
     // Just as nsGenericHTMLFormElementWithState::PreHandleEvent calls
@@ -3549,17 +3565,18 @@ HTMLInputElement::PreHandleEvent(EventCh
     }
     if (textControl && aVisitor.mEvent->originalTarget == textControl) {
       if (aVisitor.mEvent->message == NS_EDITOR_INPUT) {
         // Propogate the anon text control's new value to our HTMLInputElement:
         nsAutoString value;
         numberControlFrame->GetValueOfAnonTextControl(value);
         numberControlFrame->HandlingInputEvent(true);
         nsWeakFrame weakNumberControlFrame(numberControlFrame);
-        SetValueInternal(value, true, true);
+        rv = SetValueInternal(value, true, true);
+        NS_ENSURE_SUCCESS(rv, rv);
         if (weakNumberControlFrame.IsAlive()) {
           numberControlFrame->HandlingInputEvent(false);
         }
       }
       else if (aVisitor.mEvent->message == NS_FORM_CHANGE) {
         // We cancel the DOM 'change' event that is fired for any change to our
         // anonymous text control since we fire our own 'change' events and
         // content shouldn't be seeing two 'change' events. Besides that we
@@ -3623,16 +3640,18 @@ HTMLInputElement::CancelRangeThumbDrag(b
   if (aIsForUserEvent) {
     SetValueOfRangeForUserEvent(mRangeThumbDragStartValue);
   } else {
     // Don't dispatch an 'input' event - at least not using
     // DispatchTrustedEvent.
     // TODO: decide what we should do here - bug 851782.
     nsAutoString val;
     ConvertNumberToString(mRangeThumbDragStartValue, val);
+    // TODO: What should we do if SetValueInternal fails?  (The allocation
+    // is small, so we should be fine here.)
     SetValueInternal(val, true, true);
     nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
     if (frame) {
       frame->UpdateForValueChange();
     }
     nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
       new AsyncEventDispatcher(this, NS_LITERAL_STRING("input"), true, false);
     asyncDispatcher->RunDOMEventWhenSafe();
@@ -3641,16 +3660,18 @@ HTMLInputElement::CancelRangeThumbDrag(b
 
 void
 HTMLInputElement::SetValueOfRangeForUserEvent(Decimal aValue)
 {
   MOZ_ASSERT(aValue.isFinite());
 
   nsAutoString val;
   ConvertNumberToString(aValue, val);
+  // TODO: What should we do if SetValueInternal fails?  (The allocation
+  // is small, so we should be fine here.)
   SetValueInternal(val, true, true);
   nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
   if (frame) {
     frame->UpdateForValueChange();
   }
   nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
                                        static_cast<nsIDOMHTMLInputElement*>(this),
                                        NS_LITERAL_STRING("input"), true,
@@ -3732,16 +3753,18 @@ HTMLInputElement::StepNumberControlForUs
   nsresult rv = GetValueIfStepped(aDirection, CALLED_FOR_USER_EVENT, &newValue);
 
   if (NS_FAILED(rv) || !newValue.isFinite()) {
     return; // value should not or will not change
   }
 
   nsAutoString newVal;
   ConvertNumberToString(newValue, newVal);
+  // TODO: What should we do if SetValueInternal fails?  (The allocation
+  // is small, so we should be fine here.)
   SetValueInternal(newVal, true, true);
 
   nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
                                        static_cast<nsIDOMHTMLInputElement*>(this),
                                        NS_LITERAL_STRING("input"), true,
                                        false);
 }
 
@@ -4528,16 +4551,19 @@ HTMLInputElement::HandleTypeChange(uint8
       // SetValueInternal is going to sanitize the value.
       {
         nsAutoString value;
         if (aOldValueMode != VALUE_MODE_VALUE) {
           GetAttr(kNameSpaceID_None, nsGkAtoms::value, value);
         } else {
           value = aOldValue;
         }
+        // TODO: What should we do if SetValueInternal fails?  (The allocation
+        // may potentially be big, but most likely we've failed to allocate
+        // before the type change.)
         SetValueInternal(value, false, false);
       }
       break;
     case VALUE_MODE_FILENAME:
     default:
       // We don't care about the value.
       // There is no value sanitizing algorithm for elements in this mode.
       break;
@@ -5211,17 +5237,21 @@ HTMLInputElement::SetRangeText(const nsA
         aSelectionEnd = state->GetSelectionProperties().mEnd;
         aRv = NS_OK;
       }
     }
   }
 
   if (aStart <= aEnd) {
     value.Replace(aStart, aEnd - aStart, aReplacement);
-    SetValueInternal(value, false, false);
+    nsresult rv = SetValueInternal(value, false, false);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+      return;
+    }
   }
 
   uint32_t newEnd = aStart + aReplacement.Length();
   int32_t delta =  aReplacement.Length() - (aEnd - aStart);
 
   switch (aSelectMode) {
     case mozilla::dom::SelectionMode::Select:
     {
@@ -5770,16 +5800,19 @@ HTMLInputElement::DoneCreatingElement()
     DoSetChecked(DefaultChecked(), false, true);
     DoSetCheckedChanged(false, false);
   }
 
   // Sanitize the value.
   if (GetValueMode() == VALUE_MODE_VALUE) {
     nsAutoString aValue;
     GetValue(aValue);
+    // TODO: What should we do if SetValueInternal fails?  (The allocation
+    // may potentially be big, but most likely we've failed to allocate
+    // before the type change.)
     SetValueInternal(aValue, false, false);
   }
 
   mShouldInitChecked = false;
 }
 
 EventStates
 HTMLInputElement::IntrinsicState() const
@@ -5924,19 +5957,21 @@ HTMLInputElement::RestoreState(nsPresSta
         break;
       case VALUE_MODE_VALUE:
       case VALUE_MODE_DEFAULT:
         if (GetValueMode() == VALUE_MODE_DEFAULT &&
             mType != NS_FORM_INPUT_HIDDEN) {
           break;
         }
 
+        // TODO: What should we do if SetValueInternal fails?  (The allocation
+        // may potentially be big, but most likely we've failed to allocate
+        // before the type change.)
         SetValueInternal(inputState->GetValue(), false, true);
         break;
-        break;
     }
   }
 
   if (aState->IsDisabledSet()) {
     SetDisabled(aState->GetDisabled());
   }
 
   return restoredCheckedState;
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -298,17 +298,19 @@ HTMLTextAreaElement::GetPlaceholderVisib
 nsresult
 HTMLTextAreaElement::SetValueInternal(const nsAString& aValue,
                                       bool aUserInput)
 {
   // Need to set the value changed flag here, so that
   // nsTextControlFrame::UpdateValueDisplay retrieves the correct value
   // if needed.
   SetValueChanged(true);
-  mState.SetValue(aValue, aUserInput, true);
+  if (!mState.SetValue(aValue, aUserInput, true)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 HTMLTextAreaElement::SetValue(const nsAString& aValue)
 {
   // If the value has been set by a script, we basically want to keep the
@@ -316,33 +318,33 @@ HTMLTextAreaElement::SetValue(const nsAS
   // event, we should keep it that way. Otherwise, we should make sure the
   // element will not fire any event because of the script interaction.
   //
   // NOTE: this is currently quite expensive work (too much string
   // manipulation). We should probably optimize that.
   nsAutoString currentValue;
   GetValueInternal(currentValue, true);
 
-  SetValueInternal(aValue, false);
+  nsresult rv = SetValueInternal(aValue, false);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   if (mFocusedValue.Equals(currentValue)) {
     GetValueInternal(mFocusedValue, true);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 HTMLTextAreaElement::SetUserInput(const nsAString& aValue)
 {
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
-  SetValueInternal(aValue, true);
-  return NS_OK;
+  return SetValueInternal(aValue, true);
 }
 
 NS_IMETHODIMP
 HTMLTextAreaElement::SetValueChanged(bool aValueChanged)
 {
   bool previousValue = mValueChanged;
 
   mValueChanged = aValueChanged;
@@ -961,17 +963,21 @@ HTMLTextAreaElement::SetRangeText(const 
         aSelectionEnd = mState.GetSelectionProperties().mEnd;
         aRv = NS_OK;
       }
     }
   }
 
   if (aStart <= aEnd) {
     value.Replace(aStart, aEnd - aStart, aReplacement);
-    SetValueInternal(value, false);
+    nsresult rv = SetValueInternal(value, false);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+      return;
+    }
   }
 
   uint32_t newEnd = aStart + aReplacement.Length();
   int32_t delta =  aReplacement.Length() - (aEnd - aStart);
 
   switch (aSelectMode) {
     case mozilla::dom::SelectionMode::Select:
     {
@@ -1014,17 +1020,18 @@ HTMLTextAreaElement::SetRangeText(const 
 
 nsresult
 HTMLTextAreaElement::Reset()
 {
   nsresult rv;
 
   // To get the initial spellchecking, reset value to
   // empty string before setting the default value.
-  SetValue(EmptyString());
+  rv = SetValue(EmptyString());
+  NS_ENSURE_SUCCESS(rv, rv);
   nsAutoString resetVal;
   GetDefaultValue(resetVal);
   rv = SetValue(resetVal);
   NS_ENSURE_SUCCESS(rv, rv);
 
   SetValueChanged(false);
   return NS_OK;
 }
@@ -1105,17 +1112,18 @@ bool
 HTMLTextAreaElement::RestoreState(nsPresState* aState)
 {
   nsCOMPtr<nsISupportsString> state
     (do_QueryInterface(aState->GetStateProperty()));
   
   if (state) {
     nsAutoString data;
     state->GetData(data);
-    SetValue(data);
+    nsresult rv = SetValue(data);
+    NS_ENSURE_SUCCESS(rv, false);
   }
 
   if (aState->IsDisabledSet()) {
     SetDisabled(aState->GetDisabled());
   }
 
   return false;
 }
@@ -1289,17 +1297,17 @@ nsresult
 HTMLTextAreaElement::CopyInnerTo(Element* aDest)
 {
   nsresult rv = nsGenericHTMLFormElementWithState::CopyInnerTo(aDest);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aDest->OwnerDoc()->IsStaticDocument()) {
     nsAutoString value;
     GetValueInternal(value, true);
-    static_cast<HTMLTextAreaElement*>(aDest)->SetValue(value);
+    return static_cast<HTMLTextAreaElement*>(aDest)->SetValue(value);
   }
   return NS_OK;
 }
 
 bool
 HTMLTextAreaElement::IsMutable() const
 {
   return (!HasAttr(kNameSpaceID_None, nsGkAtoms::readonly) && !IsDisabled());
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -1392,17 +1392,18 @@ nsTextEditorState::PrepareEditor(const n
     // Now call SetValue() which will make the necessary editor calls to set
     // the default value.  Make sure to turn off undo before setting the default
     // value, and turn it back on afterwards. This will make sure we can't undo
     // past the default value.
 
     rv = newEditor->EnableUndo(false);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    SetValue(defaultValue, false, false);
+    bool success = SetValue(defaultValue, false, false);
+    NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
 
     rv = newEditor->EnableUndo(true);
     NS_ASSERTION(NS_SUCCEEDED(rv),"Transaction Manager must have failed");
 
     // Now restore the original editor flags.
     rv = newEditor->SetFlags(editorFlags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
@@ -1650,17 +1651,19 @@ nsTextEditorState::UnbindFromFrame(nsTex
     mTextListener = nullptr;
   }
 
   mBoundFrame = nullptr;
 
   // Now that we don't have a frame any more, store the value in the text buffer.
   // The only case where we don't do this is if a value transfer is in progress.
   if (!mValueTransferInProgress) {
-    SetValue(value, false, false);
+    bool success = SetValue(value, false, false);
+    // TODO Find something better to do if this fails...
+    NS_ENSURE_TRUE_VOID(success);
   }
 
   if (mRootNode && mMutationObserver) {
     mRootNode->RemoveMutationObserver(mMutationObserver);
     mMutationObserver = nullptr;
   }
 
   // Unbind the anonymous content from the tree.
@@ -1863,20 +1866,22 @@ nsTextEditorState::GetValue(nsAString& a
     if (!mTextCtrlElement->ValueChanged() || !mValue) {
       mTextCtrlElement->GetDefaultValueFromContent(aValue);
     } else {
       aValue = NS_ConvertUTF8toUTF16(*mValue);
     }
   }
 }
 
-void
+bool
 nsTextEditorState::SetValue(const nsAString& aValue, bool aUserInput,
                             bool aSetValueChanged)
 {
+  mozilla::fallible_t fallible;
+
   if (mEditor && mBoundFrame) {
     // The InsertText call below might flush pending notifications, which
     // could lead into a scheduled PrepareEditor to be called.  That will
     // lead to crashes (or worse) because we'd be initializing the editor
     // before InsertText returns.  This script blocker makes sure that
     // PrepareEditor cannot be called prematurely.
     nsAutoScriptBlocker scriptBlocker;
 
@@ -1896,26 +1901,31 @@ nsTextEditorState::SetValue(const nsAStr
     if (!currentValue.Equals(aValue))
     {
       ValueSetter valueSetter(mEditor);
 
       // \r is an illegal character in the dom, but people use them,
       // so convert windows and mac platform linebreaks to \n:
       // Unfortunately aValue is declared const, so we have to copy
       // in order to do this substitution.
-      nsString newValue(aValue);
+      nsString newValue;
+      if (!newValue.Assign(aValue, fallible)) {
+        return false;
+      }
       if (aValue.FindChar(char16_t('\r')) != -1) {
-        nsContentUtils::PlatformToDOMLineBreaks(newValue);
+        if (!nsContentUtils::PlatformToDOMLineBreaks(newValue, fallible)) {
+          return false;
+        }
       }
 
       nsCOMPtr<nsIDOMDocument> domDoc;
       mEditor->GetDocument(getter_AddRefs(domDoc));
       if (!domDoc) {
         NS_WARNING("Why don't we have a document?");
-        return;
+        return true;
       }
 
       // Time to mess with our security context... See comments in GetValue()
       // for why this is needed.  Note that we have to do this up here, because
       // otherwise SelectAll() will fail.
       {
         AutoNoJSAPI nojsapi;
 
@@ -1942,17 +1952,17 @@ nsTextEditorState::SetValue(const nsAStr
           // Collapse selection to the end so that we can append data.
           mBoundFrame->SelectAllOrCollapseToEndOfText(false);
         }
         const nsAString& insertValue =
           StringTail(newValue, newlength - currentLength);
         nsCOMPtr<nsIPlaintextEditor> plaintextEditor = do_QueryInterface(mEditor);
         if (!plaintextEditor || !weakFrame.IsAlive()) {
           NS_WARNING("Somehow not a plaintext editor?");
-          return;
+          return true;
         }
 
         valueSetter.Init();
 
         // get the flags, remove readonly and disabled, set the value,
         // restore flags
         uint32_t flags, savedFlags;
         mEditor->GetFlags(&savedFlags);
@@ -1981,50 +1991,61 @@ nsTextEditorState::SetValue(const nsAStr
 
         if (!weakFrame.IsAlive()) {
           // If the frame was destroyed because of a flush somewhere inside
           // InsertText, mBoundFrame here will be false.  But it's also possible
           // for the frame to go away because of another reason (such as deleting
           // the existing selection -- see bug 574558), in which case we don't
           // need to reset the value here.
           if (!mBoundFrame) {
-            SetValue(newValue, false, aSetValueChanged);
+            return SetValue(newValue, false, aSetValueChanged);
           }
-          return;
+          return true;
         }
 
         if (!IsSingleLineTextControl()) {
-          mCachedValue = newValue;
+          if (!mCachedValue.Assign(newValue, fallible)) {
+            return false;
+          }
         }
 
         plaintextEditor->SetMaxTextLength(savedMaxLength);
         mEditor->SetFlags(savedFlags);
         if (selPriv)
           selPriv->EndBatchChanges();
       }
     }
   } else {
     if (!mValue) {
       mValue = new nsCString;
     }
-    nsString value(aValue);
-    nsContentUtils::PlatformToDOMLineBreaks(value);
-    CopyUTF16toUTF8(value, *mValue);
+    nsString value;
+    if (!value.Assign(aValue, fallible)) {
+      return false;
+    }
+    if (!nsContentUtils::PlatformToDOMLineBreaks(value, fallible)) {
+      return false;
+    }
+    if (!CopyUTF16toUTF8(value, *mValue, fallible)) {
+      return false;
+    }
 
     // Update the frame display if needed
     if (mBoundFrame) {
       mBoundFrame->UpdateValueDisplay(true);
     }
   }
 
   // If we've reached the point where the root node has been created, we
   // can assume that it's safe to notify.
   ValueWasChanged(!!mRootNode);
 
   mTextCtrlElement->OnValueChanged(!!mRootNode);
+
+  return true;
 }
 
 void
 nsTextEditorState::InitializeKeyboardEventListeners()
 {
   //register key listeners
   nsCOMPtr<EventTarget> target = do_QueryInterface(mTextCtrlElement);
   EventListenerManager* manager = target->GetOrCreateListenerManager();
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -137,18 +137,19 @@ public:
   nsIEditor* GetEditor();
   nsISelectionController* GetSelectionController() const;
   nsFrameSelection* GetConstFrameSelection();
   nsresult BindToFrame(nsTextControlFrame* aFrame);
   void UnbindFromFrame(nsTextControlFrame* aFrame);
   nsresult PrepareEditor(const nsAString *aValue = nullptr);
   void InitializeKeyboardEventListeners();
 
-  void SetValue(const nsAString& aValue, bool aUserInput,
-                bool aSetValueAsChanged);
+  MOZ_WARN_UNUSED_RESULT bool SetValue(const nsAString& aValue,
+                                       bool aUserInput,
+                                       bool aSetValueAsChanged);
   void GetValue(nsAString& aValue, bool aIgnoreWrap) const;
   void EmptyValue() { if (mValue) mValue->Truncate(); }
   bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; }
 
   nsresult CreatePlaceholderNode();
 
   mozilla::dom::Element* GetRootNode() {
     if (!mRootNode)
--- a/xpcom/string/nsReadableUtils.cpp
+++ b/xpcom/string/nsReadableUtils.cpp
@@ -41,18 +41,32 @@ CopyASCIItoUTF16(const char* aSource, ns
   if (aSource) {
     AppendASCIItoUTF16(nsDependentCString(aSource), aDest);
   }
 }
 
 void
 CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest)
 {
+  if (!CopyUTF16toUTF8(aSource, aDest, mozilla::fallible_t())) {
+    // Note that this may wildly underestimate the allocation that failed, as
+    // we report the length of aSource as UTF-16 instead of UTF-8.
+    aDest.AllocFailed(aDest.Length() + aSource.Length());
+  }
+}
+
+bool
+CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest,
+                const mozilla::fallible_t&)
+{
   aDest.Truncate();
-  AppendUTF16toUTF8(aSource, aDest);
+  if (!AppendUTF16toUTF8(aSource, aDest, mozilla::fallible_t())) {
+    return false;
+  }
+  return true;
 }
 
 void
 CopyUTF8toUTF16(const nsACString& aSource, nsAString& aDest)
 {
   aDest.Truncate();
   AppendUTF8toUTF16(aSource, aDest);
 }
@@ -139,16 +153,18 @@ AppendASCIItoUTF16(const char* aSource, 
     AppendASCIItoUTF16(nsDependentCString(aSource), aDest);
   }
 }
 
 void
 AppendUTF16toUTF8(const nsAString& aSource, nsACString& aDest)
 {
   if (!AppendUTF16toUTF8(aSource, aDest, mozilla::fallible_t())) {
+    // Note that this may wildly underestimate the allocation that failed, as
+    // we report the length of aSource as UTF-16 instead of UTF-8.
     aDest.AllocFailed(aDest.Length() + aSource.Length());
   }
 }
 
 bool
 AppendUTF16toUTF8(const nsAString& aSource, nsACString& aDest,
                   const mozilla::fallible_t&)
 {
--- a/xpcom/string/nsReadableUtils.h
+++ b/xpcom/string/nsReadableUtils.h
@@ -35,16 +35,19 @@ Distance(const nsReadingIterator<char>& 
 
 void LossyCopyUTF16toASCII(const nsAString& aSource, nsACString& aDest);
 void CopyASCIItoUTF16(const nsACString& aSource, nsAString& aDest);
 
 void LossyCopyUTF16toASCII(const char16_t* aSource, nsACString& aDest);
 void CopyASCIItoUTF16(const char* aSource, nsAString& aDest);
 
 void CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest);
+NS_WARN_UNUSED_RESULT bool CopyUTF16toUTF8(const nsAString& aSource,
+                                           nsACString& aDest,
+                                           const mozilla::fallible_t&);
 void CopyUTF8toUTF16(const nsACString& aSource, nsAString& aDest);
 
 void CopyUTF16toUTF8(const char16_t* aSource, nsACString& aDest);
 void CopyUTF8toUTF16(const char* aSource, nsAString& aDest);
 
 void LossyAppendUTF16toASCII(const nsAString& aSource, nsACString& aDest);
 void AppendASCIItoUTF16(const nsACString& aSource, nsAString& aDest);
 NS_WARN_UNUSED_RESULT bool AppendASCIItoUTF16(const nsACString& aSource,
--- a/xpcom/string/nsTString.h
+++ b/xpcom/string/nsTString.h
@@ -388,16 +388,22 @@ public:
 #endif
   /**
    * Replace all occurrences of aTarget with aNewValue.
    * The complexity of this function is O(n+m), n being the length of the string
    * and m being the length of aNewValue.
    */
   void ReplaceSubstring(const self_type& aTarget, const self_type& aNewValue);
   void ReplaceSubstring(const char_type* aTarget, const char_type* aNewValue);
+  NS_WARN_UNUSED_RESULT bool ReplaceSubstring(const self_type& aTarget,
+                                              const self_type& aNewValue,
+                                              const fallible_t&);
+  NS_WARN_UNUSED_RESULT bool ReplaceSubstring(const char_type* aTarget,
+                                              const char_type* aNewValue,
+                                              const fallible_t&);
 
 
   /**
    *  This method trims characters found in aTrimSet from
    *  either end of the underlying string.
    *
    *  @param   aSet -- contains chars to be trimmed from both ends
    *  @param   aEliminateLeading
--- a/xpcom/string/nsTStringObsolete.cpp
+++ b/xpcom/string/nsTStringObsolete.cpp
@@ -449,27 +449,51 @@ nsTString_CharT::ReplaceChar( const char
     data += i;
     lenRemaining -= i;
   }
 }
 
 void ReleaseData(void* aData, uint32_t aFlags);
 
 void
-nsTString_CharT::ReplaceSubstring( const char_type* aTarget, const char_type* aNewValue )
+nsTString_CharT::ReplaceSubstring(const char_type* aTarget,
+                                  const char_type* aNewValue)
 {
   ReplaceSubstring(nsTDependentString_CharT(aTarget),
                    nsTDependentString_CharT(aNewValue));
 }
 
+bool
+nsTString_CharT::ReplaceSubstring(const char_type* aTarget,
+                                  const char_type* aNewValue,
+                                  const fallible_t& fallible)
+{
+  return ReplaceSubstring(nsTDependentString_CharT(aTarget),
+                          nsTDependentString_CharT(aNewValue),
+                          fallible);
+}
+
 void
-nsTString_CharT::ReplaceSubstring( const self_type& aTarget, const self_type& aNewValue )
+nsTString_CharT::ReplaceSubstring(const self_type& aTarget,
+                                  const self_type& aNewValue)
+{
+  if (!ReplaceSubstring(aTarget, aNewValue, fallible_t())) {
+    // Note that this may wildly underestimate the allocation that failed, as
+    // we could have been replacing multiple copies of aTarget.
+    AllocFailed(mLength + (aNewValue.Length() - aTarget.Length()));
+  }
+}
+
+bool
+nsTString_CharT::ReplaceSubstring(const self_type& aTarget,
+                                  const self_type& aNewValue,
+                                  const fallible_t&)
 {
   if (aTarget.Length() == 0)
-    return;
+    return true;
 
   // Remember all of the non-matching parts.
   nsAutoTArray<Segment, 16> nonMatching;
   uint32_t i = 0;
   uint32_t newLength = 0;
   while (true)
   {
     int32_t r = FindSubstring(mData + i, mLength - i, static_cast<const char_type*>(aTarget.Data()), aTarget.Length(), false);
@@ -490,27 +514,27 @@ nsTString_CharT::ReplaceSubstring( const
     }
   }
 
   // If there's only one non-matching segment, then the target string was not
   // found, and there's nothing to do.
   if (nonMatching.Length() == 1) {
     MOZ_ASSERT(nonMatching[0].mBegin == 0 && nonMatching[0].mLength == mLength,
                "We should have the correct non-matching segment.");
-    return;
+    return true;
   }
 
   // Make sure that we can mutate our buffer.
   // Note that we always allocate at least an mLength sized buffer, because the
   // rest of the algorithm relies on having access to all of the original
   // string.  In other words, we over-allocate in the shrinking case.
   char_type* oldData;
   uint32_t oldFlags;
   if (!MutatePrep(XPCOM_MAX(mLength, newLength), &oldData, &oldFlags))
-    return;
+    return false;
   if (oldData) {
     // Copy all of the old data to the new buffer.
     char_traits::copy(mData, oldData, mLength);
     ::ReleaseData(oldData, oldFlags);
   }
 
   if (aTarget.Length() >= aNewValue.Length()) {
     // In the shrinking case, start filling the buffer from the beginning.
@@ -544,16 +568,18 @@ nsTString_CharT::ReplaceSubstring( const
       char_traits::copy(destinationSegmentPtr - aNewValue.Length(),
                         aNewValue.Data(), aNewValue.Length());
     }
   }
 
   // Adjust the length and make sure the string is null terminated.
   mLength = newLength;
   mData[mLength] = char_type(0);
+
+  return true;
 }
 
 /**
  * nsTString::Trim
  */
 
 void
 nsTString_CharT::Trim( const char* aSet, bool aTrimLeading, bool aTrimTrailing, bool aIgnoreQuotes )