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 251014 27ae894ee4d70e08dea4e335352aa2afd810e82a
parent 251013 e4dd444e5c88da36ef88bc602fcda4f98f83be4f
child 251015 49d0c7894393162134e4ea61d969866fb3ce6b1e
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst, froydnj
bugs1097452
milestone38.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 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 )