Merge inbound to mozilla-central. a=merge
authorCiure Andrei <aciure@mozilla.com>
Fri, 02 Mar 2018 12:16:06 +0200
changeset 458748 b2a9a4bb5c94de179ae7a3f52fde58c0e2897498
parent 458746 b5159a80934f39ae18d264ce2bc249ccfdcb5f53 (current diff)
parent 458747 2e11c53b5a51948e8afb845e2c1603cc2ecd8b9f (diff)
child 458749 5889cac267e270e4762fe579e8bec0b4b0920532
child 458754 bddc2ca6492179e4b287c3c05f249bba1350e8ef
child 458795 f1b56646984161b5209383cb48bb82c6e2111be8
push id8808
push userarchaeopteryx@coole-files.de
push dateFri, 02 Mar 2018 22:13:05 +0000
treeherdermozilla-beta@7475508d19db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone60.0a1
first release with
nightly linux32
b2a9a4bb5c94 / 60.0a1 / 20180302104326 / files
nightly linux64
b2a9a4bb5c94 / 60.0a1 / 20180302104326 / files
nightly mac
b2a9a4bb5c94 / 60.0a1 / 20180302104326 / files
nightly win32
b2a9a4bb5c94 / 60.0a1 / 20180302104326 / files
nightly win64
b2a9a4bb5c94 / 60.0a1 / 20180302104326 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
browser/app/profile/firefox.js
toolkit/mozapps/update/tests/unit_aus_update/downloadInterruptedRecoveryChannel.js
toolkit/mozapps/update/tests/unit_aus_update/downloadResumeAfterRestart.js
toolkit/mozapps/update/tests/unit_aus_update/downloadResumeErrorNoEntityID.js
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -697,20 +697,20 @@ DocAccessible::OnPivotChanged(nsIAccessi
 ////////////////////////////////////////////////////////////////////////////////
 // nsIDocumentObserver
 
 NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(DocAccessible)
 NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(DocAccessible)
 NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(DocAccessible)
 
 void
-DocAccessible::AttributeWillChange(nsIDocument* aDocument,
-                                   dom::Element* aElement,
+DocAccessible::AttributeWillChange(dom::Element* aElement,
                                    int32_t aNameSpaceID,
-                                   nsAtom* aAttribute, int32_t aModType,
+                                   nsAtom* aAttribute,
+                                   int32_t aModType,
                                    const nsAttrValue* aNewValue)
 {
   Accessible* accessible = GetAccessible(aElement);
   if (!accessible) {
     if (aElement != mContent)
       return;
 
     accessible = this;
@@ -743,25 +743,23 @@ DocAccessible::AttributeWillChange(nsIDo
   }
 
   if (aAttribute == nsGkAtoms::aria_disabled ||
       aAttribute == nsGkAtoms::disabled)
     mStateBitWasOn = accessible->Unavailable();
 }
 
 void
-DocAccessible::NativeAnonymousChildListChange(nsIDocument* aDocument,
-                                              nsIContent* aContent,
+DocAccessible::NativeAnonymousChildListChange(nsIContent* aContent,
                                               bool aIsRemove)
 {
 }
 
 void
-DocAccessible::AttributeChanged(nsIDocument* aDocument,
-                                dom::Element* aElement,
+DocAccessible::AttributeChanged(dom::Element* aElement,
                                 int32_t aNameSpaceID, nsAtom* aAttribute,
                                 int32_t aModType,
                                 const nsAttrValue* aOldValue)
 {
   NS_ASSERTION(!IsDefunct(),
                "Attribute changed called on defunct document accessible!");
 
   // Proceed even if the element is not accessible because element may become
@@ -1081,19 +1079,17 @@ DocAccessible::ARIAActiveDescendantChang
 #endif
         }
       }
     }
   }
 }
 
 void
-DocAccessible::ContentAppended(nsIDocument* aDocument,
-                               nsIContent* aContainer,
-                               nsIContent* aFirstNewContent)
+DocAccessible::ContentAppended(nsIContent* aFirstNewContent)
 {
 }
 
 void
 DocAccessible::ContentStateChanged(nsIDocument* aDocument,
                                    nsIContent* aContent,
                                    EventStates aStateMask)
 {
@@ -1134,45 +1130,40 @@ DocAccessible::ContentStateChanged(nsIDo
 
 void
 DocAccessible::DocumentStatesChanged(nsIDocument* aDocument,
                                      EventStates aStateMask)
 {
 }
 
 void
-DocAccessible::CharacterDataWillChange(nsIDocument* aDocument,
-                                       nsIContent* aContent,
+DocAccessible::CharacterDataWillChange(nsIContent* aContent,
                                        const CharacterDataChangeInfo&)
 {
 }
 
 void
-DocAccessible::CharacterDataChanged(nsIDocument* aDocument,
-                                    nsIContent* aContent,
+DocAccessible::CharacterDataChanged(nsIContent* aContent,
                                     const CharacterDataChangeInfo&)
 {
 }
 
 void
-DocAccessible::ContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
-                               nsIContent* aChild)
+DocAccessible::ContentInserted(nsIContent* aChild)
 {
 }
 
 void
-DocAccessible::ContentRemoved(nsIDocument* aDocument,
-                              nsIContent* aContainerNode,
-                              nsIContent* aChildNode,
+DocAccessible::ContentRemoved(nsIContent* aChildNode,
                               nsIContent* aPreviousSiblingNode)
 {
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eTree)) {
     logging::MsgBegin("TREE", "DOM content removed; doc: %p", this);
-    logging::Node("container node", aContainerNode);
+    logging::Node("container node", aChildNode->GetParent());
     logging::Node("content node", aChildNode);
     logging::MsgEnd();
   }
 #endif
   // This one and content removal notification from layout may result in
   // double processing of same subtrees. If it pops up in profiling, then
   // consider reusing a document node cache to reject these notifications early.
   ContentRemoved(aChildNode);
--- a/browser/branding/aurora/pref/firefox-branding.js
+++ b/browser/branding/aurora/pref/firefox-branding.js
@@ -4,16 +4,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 pref("startup.homepage_override_url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%a2/whatsnew/");
 pref("startup.homepage_welcome_url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%a2/firstrun/");
 pref("startup.homepage_welcome_url.additional", "");
 // The time interval between checks for a new version (in seconds)
 pref("app.update.interval", 28800); // 8 hours
+// The time interval between the downloading of mar file chunks in the
+// background (in seconds)
+// 0 means "download everything at once"
+pref("app.update.download.backgroundInterval", 0);
 // Give the user x seconds to react before showing the big UI. default=192 hours
 pref("app.update.promptWaitTime", 691200);
 // URL user can browse to manually if for some reason all update installation
 // attempts fail.
 pref("app.update.url.manual", "https://www.mozilla.org/firefox/aurora/");
 // A default value for the "More information about this update" link
 // supplied in the "An update is available" page of the update wizard.
 pref("app.update.url.details", "https://www.mozilla.org/firefox/aurora/");
--- a/browser/branding/nightly/pref/firefox-branding.js
+++ b/browser/branding/nightly/pref/firefox-branding.js
@@ -2,16 +2,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 pref("startup.homepage_override_url", "https://www.mozilla.org/projects/firefox/%VERSION%/whatsnew/?oldversion=%OLD_VERSION%");
 pref("startup.homepage_welcome_url", "https://www.mozilla.org/projects/firefox/%VERSION%/firstrun/");
 pref("startup.homepage_welcome_url.additional", "");
 // The time interval between checks for a new version (in seconds)
 pref("app.update.interval", 7200); // 2 hours
+// The time interval between the downloading of mar file chunks in the
+// background (in seconds)
+// 0 means "download everything at once"
+pref("app.update.download.backgroundInterval", 0);
 // Give the user x seconds to react before showing the big UI. default=12 hours
 pref("app.update.promptWaitTime", 43200);
 // URL user can browse to manually if for some reason all update installation
 // attempts fail.
 pref("app.update.url.manual", "https://www.mozilla.org/%LOCALE%/firefox/nightly/");
 // A default value for the "More information about this update" link
 // supplied in the "An update is available" page of the update wizard.
 pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/nightly/notes/");
--- a/browser/branding/official/pref/firefox-branding.js
+++ b/browser/branding/official/pref/firefox-branding.js
@@ -2,16 +2,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 pref("startup.homepage_override_url", "");
 pref("startup.homepage_welcome_url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/firstrun/");
 pref("startup.homepage_welcome_url.additional", "");
 // Interval: Time between checks for a new version (in seconds)
 pref("app.update.interval", 43200); // 12 hours
+// The time interval between the downloading of mar file chunks in the
+// background (in seconds)
+// 0 means "download everything at once"
+pref("app.update.download.backgroundInterval", 0);
 // Give the user x seconds to react before showing the big UI. default=192 hours
 pref("app.update.promptWaitTime", 691200);
 // app.update.url.manual: URL user can browse to manually if for some reason
 // all update installation attempts fail.
 // app.update.url.details: a default value for the "More information about this
 // update" link supplied in the "An update is available" page of the update
 // wizard.
 #if MOZ_UPDATE_CHANNEL == beta
--- a/browser/branding/unofficial/pref/firefox-branding.js
+++ b/browser/branding/unofficial/pref/firefox-branding.js
@@ -2,16 +2,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 pref("startup.homepage_override_url", "");
 pref("startup.homepage_welcome_url", "");
 pref("startup.homepage_welcome_url.additional", "");
 // The time interval between checks for a new version (in seconds)
 pref("app.update.interval", 86400); // 24 hours
+// The time interval between the downloading of mar file chunks in the
+// background (in seconds)
+pref("app.update.download.backgroundInterval", 60);
 // Give the user x seconds to react before showing the big UI. default=24 hours
 pref("app.update.promptWaitTime", 86400);
 // URL user can browse to manually if for some reason all update installation
 // attempts fail.
 pref("app.update.url.manual", "https://nightly.mozilla.org");
 // A default value for the "More information about this update" link
 // supplied in the "An update is available" page of the update wizard.
 pref("app.update.url.details", "https://nightly.mozilla.org");
--- a/build/sanitizers/ubsan_signed_overflow_blacklist.txt
+++ b/build/sanitizers/ubsan_signed_overflow_blacklist.txt
@@ -243,19 +243,16 @@ src:*/security/nss/lib/dbm/src/h_func.c
 src:*/security/nss/lib/freebl/sha512.c
 src:*/security/nss/lib/freebl/md5.c
 src:*/XorShift128PlusRNG.h
 src:*/xpcom/ds/PLDHashTable.cpp
 
 # Hash/Cache function in Skia
 fun:*GradientShaderCache*Build32bitCache*
 
-# Hash function in js/public/Utility.h
-fun:ScrambleHashCode*
-
 # Hashing functions in Cairo
 fun:*_hash_matrix_fnv*
 fun:*_hash_mix_bits*
 fun:*_cairo_hash_string*
 fun:*_cairo_hash_bytes*
 
 # Hash function in modules/libjar/nsZipArchive.cpp
 fun:*HashName*
--- a/build/sanitizers/ubsan_unsigned_overflow_blacklist.txt
+++ b/build/sanitizers/ubsan_unsigned_overflow_blacklist.txt
@@ -250,19 +250,16 @@ src:*/security/nss/lib/dbm/src/h_func.c
 src:*/security/nss/lib/freebl/sha512.c
 src:*/security/nss/lib/freebl/md5.c
 src:*/XorShift128PlusRNG.h
 src:*/xpcom/ds/PLDHashTable.cpp
 
 # Hash/Cache function in Skia
 fun:*GradientShaderCache*Build32bitCache*
 
-# Hash function in js/public/Utility.h
-fun:ScrambleHashCode*
-
 # Hashing functions in Cairo
 fun:*_hash_matrix_fnv*
 fun:*_hash_mix_bits*
 fun:*_cairo_hash_string*
 fun:*_cairo_hash_bytes*
 
 # Hash function in modules/libjar/nsZipArchive.cpp
 fun:*HashName*
--- a/docshell/shistory/nsSHEntryShared.cpp
+++ b/docshell/shistory/nsSHEntryShared.cpp
@@ -248,78 +248,67 @@ nsSHEntryShared::GetID(uint64_t* aID)
 
 void
 nsSHEntryShared::NodeWillBeDestroyed(const nsINode* aNode)
 {
   NS_NOTREACHED("Document destroyed while we're holding a strong ref to it");
 }
 
 void
-nsSHEntryShared::CharacterDataWillChange(nsIDocument* aDocument,
-                                         nsIContent* aContent,
+nsSHEntryShared::CharacterDataWillChange(nsIContent* aContent,
                                          const CharacterDataChangeInfo&)
 {
 }
 
 void
-nsSHEntryShared::CharacterDataChanged(nsIDocument* aDocument,
-                                      nsIContent* aContent,
+nsSHEntryShared::CharacterDataChanged(nsIContent* aContent,
                                       const CharacterDataChangeInfo&)
 {
   RemoveFromBFCacheAsync();
 }
 
 void
-nsSHEntryShared::AttributeWillChange(nsIDocument* aDocument,
-                                     dom::Element* aContent,
+nsSHEntryShared::AttributeWillChange(dom::Element* aContent,
                                      int32_t aNameSpaceID,
                                      nsAtom* aAttribute,
                                      int32_t aModType,
                                      const nsAttrValue* aNewValue)
 {
 }
 
 void
-nsSHEntryShared::NativeAnonymousChildListChange(nsIDocument* aDocument,
-                                                nsIContent* aContent,
+nsSHEntryShared::NativeAnonymousChildListChange(nsIContent* aContent,
                                                 bool aIsRemove)
 {
 }
 
 void
-nsSHEntryShared::AttributeChanged(nsIDocument* aDocument,
-                                  dom::Element* aElement,
+nsSHEntryShared::AttributeChanged(dom::Element* aElement,
                                   int32_t aNameSpaceID,
                                   nsAtom* aAttribute,
                                   int32_t aModType,
                                   const nsAttrValue* aOldValue)
 {
   RemoveFromBFCacheAsync();
 }
 
 void
-nsSHEntryShared::ContentAppended(nsIDocument* aDocument,
-                                 nsIContent* aContainer,
-                                 nsIContent* aFirstNewContent)
+nsSHEntryShared::ContentAppended(nsIContent* aFirstNewContent)
 {
   RemoveFromBFCacheAsync();
 }
 
 void
-nsSHEntryShared::ContentInserted(nsIDocument* aDocument,
-                                 nsIContent* aContainer,
-                                 nsIContent* aChild)
+nsSHEntryShared::ContentInserted(nsIContent* aChild)
 {
   RemoveFromBFCacheAsync();
 }
 
 void
-nsSHEntryShared::ContentRemoved(nsIDocument* aDocument,
-                                nsIContent* aContainer,
-                                nsIContent* aChild,
+nsSHEntryShared::ContentRemoved(nsIContent* aChild,
                                 nsIContent* aPreviousSibling)
 {
   RemoveFromBFCacheAsync();
 }
 
 void
 nsSHEntryShared::ParentChainChanged(nsIContent* aContent)
 {
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -648,10 +648,99 @@ ChromeUtils::ClearRecentJSDevError(Globa
 {
   auto runtime = CycleCollectedJSRuntime::Get();
   MOZ_ASSERT(runtime);
 
   runtime->ClearRecentDevError();
 }
 #endif // NIGHTLY_BUILD
 
+constexpr auto kSkipSelfHosted = JS::SavedFrameSelfHosted::Exclude;
+
+/* static */ void
+ChromeUtils::GetCallerLocation(const GlobalObject& aGlobal, nsIPrincipal* aPrincipal,
+                               JS::MutableHandle<JSObject*> aRetval)
+{
+  JSContext* cx = aGlobal.Context();
+
+  auto* principals = nsJSPrincipals::get(aPrincipal);
+
+  JS::StackCapture captureMode(JS::FirstSubsumedFrame(cx, principals));
+
+  JS::RootedObject frame(cx);
+  if (!JS::CaptureCurrentStack(cx, &frame, mozilla::Move(captureMode))) {
+    JS_ClearPendingException(cx);
+    aRetval.set(nullptr);
+    return;
+  }
+
+  // FirstSubsumedFrame gets us a stack which stops at the first principal which
+  // is subsumed by the given principal. That means that we may have a lot of
+  // privileged frames that we don't care about at the top of the stack, though.
+  // We need to filter those out to get the frame we actually want.
+  aRetval.set(js::GetFirstSubsumedSavedFrame(cx, principals, frame, kSkipSelfHosted));
+}
+
+/* static */ void
+ChromeUtils::CreateError(const GlobalObject& aGlobal, const nsAString& aMessage,
+                         JS::Handle<JSObject*> aStack,
+                         JS::MutableHandle<JSObject*> aRetVal, ErrorResult& aRv)
+{
+  if (aStack && !JS::IsSavedFrame(aStack)) {
+    aRv.Throw(NS_ERROR_INVALID_ARG);
+    return;
+  }
+
+  JSContext* cx = aGlobal.Context();
+
+  auto cleanup = MakeScopeExit([&]() {
+    aRv.NoteJSContextException(cx);
+  });
+
+  JS::RootedObject retVal(cx);
+  {
+    JS::RootedString fileName(cx, JS_GetEmptyString(cx));
+    uint32_t line = 0;
+    uint32_t column = 0;
+
+    Maybe<JSAutoCompartment> ac;
+    JS::RootedObject stack(cx);
+    if (aStack) {
+      stack = UncheckedUnwrap(aStack);
+      ac.emplace(cx, stack);
+
+      if (JS::GetSavedFrameLine(cx, stack, &line) != JS::SavedFrameResult::Ok ||
+          JS::GetSavedFrameColumn(cx, stack, &column) != JS::SavedFrameResult::Ok ||
+          JS::GetSavedFrameSource(cx, stack, &fileName) != JS::SavedFrameResult::Ok) {
+        return;
+      }
+    }
+
+    JS::RootedString message(cx);
+    {
+      JS::RootedValue msgVal(cx);
+      if (!xpc::NonVoidStringToJsval(cx, aMessage, &msgVal)) {
+        return;
+      }
+      message = msgVal.toString();
+    }
+
+    JS::Rooted<JS::Value> err(cx);
+    if (!JS::CreateError(cx, JSEXN_ERR, stack,
+                         fileName, line, column,
+                         nullptr, message, &err)) {
+      return;
+    }
+
+    MOZ_ASSERT(err.isObject());
+    retVal = &err.toObject();
+  }
+
+  if (aStack && !JS_WrapObject(cx, &retVal)) {
+    return;
+  }
+
+  cleanup.release();
+  aRetVal.set(retVal);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -161,14 +161,23 @@ public:
                      JS::MutableHandle<JSObject*> aRetval,
                      ErrorResult& aRv);
 
   static void DefineModuleGetter(const GlobalObject& global,
                                  JS::Handle<JSObject*> target,
                                  const nsAString& id,
                                  const nsAString& resourceURI,
                                  ErrorResult& aRv);
+
+  static void
+  GetCallerLocation(const GlobalObject& global, nsIPrincipal* principal,
+                    JS::MutableHandle<JSObject*> aRetval);
+
+  static void
+  CreateError(const GlobalObject& global, const nsAString& message,
+              JS::Handle<JSObject*> stack,
+              JS::MutableHandle<JSObject*> aRetVal, ErrorResult& aRv);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ChromeUtils__
--- a/dom/base/NodeIterator.cpp
+++ b/dom/base/NodeIterator.cpp
@@ -219,22 +219,20 @@ NodeIterator::Detach()
         mRoot->OwnerDoc()->WarnOnceAbout(nsIDocument::eNodeIteratorDetach);
     }
 }
 
 /*
  * nsIMutationObserver interface
  */
 
-void NodeIterator::ContentRemoved(nsIDocument *aDocument,
-                                  nsIContent *aContainer,
-                                  nsIContent *aChild,
-                                  nsIContent *aPreviousSibling)
+void NodeIterator::ContentRemoved(nsIContent* aChild,
+                                  nsIContent* aPreviousSibling)
 {
-    nsINode *container = NODE_FROM(aContainer, aDocument);
+    nsINode* container = aChild->GetParentNode();
 
     mPointer.AdjustAfterRemoval(mRoot, container, aChild, aPreviousSibling);
     mWorkingPointer.AdjustAfterRemoval(mRoot, container, aChild, aPreviousSibling);
 }
 
 bool
 NodeIterator::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector)
 {
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -433,18 +433,17 @@ ShadowRoot::GetInnerHTML(nsAString& aInn
 
 void
 ShadowRoot::SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError)
 {
   SetInnerHTMLInternal(aInnerHTML, aError);
 }
 
 void
-ShadowRoot::AttributeChanged(nsIDocument* aDocument,
-                             Element* aElement,
+ShadowRoot::AttributeChanged(Element* aElement,
                              int32_t aNameSpaceID,
                              nsAtom* aAttribute,
                              int32_t aModType,
                              const nsAttrValue* aOldValue)
 {
   if (aNameSpaceID != kNameSpaceID_None || aAttribute != nsGkAtoms::slot) {
     return;
   }
@@ -463,90 +462,83 @@ ShadowRoot::AttributeChanged(nsIDocument
     return;
   }
 
   // FIXME(emilio): We could be more granular in a bunch of cases.
   shell->DestroyFramesForAndRestyle(aElement);
 }
 
 void
-ShadowRoot::ContentAppended(nsIDocument* aDocument,
-                            nsIContent* aContainer,
-                            nsIContent* aFirstNewContent)
+ShadowRoot::ContentAppended(nsIContent* aFirstNewContent)
 {
   for (nsIContent* content = aFirstNewContent;
        content;
        content = content->GetNextSibling()) {
-    ContentInserted(aDocument, aContainer, content);
+    ContentInserted(content);
   }
 }
 
 void
-ShadowRoot::ContentInserted(nsIDocument* aDocument,
-                            nsIContent* aContainer,
-                            nsIContent* aChild)
+ShadowRoot::ContentInserted(nsIContent* aChild)
 {
-  // Check to ensure that the content is in the same anonymous tree
-  // as the container because anonymous content may report its container
-  // as the host but it may not be in the host's child list.
-  if (!nsContentUtils::IsInSameAnonymousTree(aContainer, aChild)) {
+  // Check to ensure that the child not an anonymous subtree root because
+  // even though its parent could be the host it may not be in the host's child
+  // list.
+  if (aChild->IsRootOfAnonymousSubtree()) {
     return;
   }
 
   if (!aChild->IsSlotable()) {
     return;
   }
 
-  if (aContainer && aContainer == GetHost()) {
+  if (aChild->GetParent() == GetHost()) {
     if (const HTMLSlotElement* slot = AssignSlotFor(aChild)) {
       slot->EnqueueSlotChangeEvent();
     }
     return;
   }
 
   // If parent's root is a shadow root, and parent is a slot whose assigned
   // nodes is the empty list, then run signal a slot change for parent.
-  HTMLSlotElement* slot = HTMLSlotElement::FromContentOrNull(aContainer);
+  HTMLSlotElement* slot = HTMLSlotElement::FromContentOrNull(aChild->GetParent());
   if (slot && slot->GetContainingShadow() == this &&
       slot->AssignedNodes().IsEmpty()) {
     slot->EnqueueSlotChangeEvent();
   }
 }
 
 void
-ShadowRoot::ContentRemoved(nsIDocument* aDocument,
-                           nsIContent* aContainer,
-                           nsIContent* aChild,
-                           nsIContent* aPreviousSibling)
+ShadowRoot::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
 {
-  // Check to ensure that the content is in the same anonymous tree
-  // as the container because anonymous content may report its container
-  // as the host but it may not be in the host's child list.
- if (!nsContentUtils::IsInSameAnonymousTree(aContainer, aChild)) {
+  // Check to ensure that the child not an anonymous subtree root because
+  // even though its parent could be the host it may not be in the host's child
+  // list.
+ if (aChild->IsRootOfAnonymousSubtree()) {
     return;
   }
 
   if (!aChild->IsSlotable()) {
     return;
   }
 
-  if (aContainer && aContainer == GetHost()) {
+  if (aChild->GetParent() == GetHost()) {
     nsAutoString slotName;
     if (aChild->IsElement()) {
       aChild->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::slot, slotName);
     }
     if (const HTMLSlotElement* slot = UnassignSlotFor(aChild, slotName)) {
       slot->EnqueueSlotChangeEvent();
     }
     return;
   }
 
   // If parent's root is a shadow root, and parent is a slot whose assigned
   // nodes is the empty list, then run signal a slot change for parent.
-  HTMLSlotElement* slot = HTMLSlotElement::FromContentOrNull(aContainer);
+  HTMLSlotElement* slot = HTMLSlotElement::FromContentOrNull(aChild->GetParent());
   if (slot && slot->GetContainingShadow() == this &&
       slot->AssignedNodes().IsEmpty()) {
     slot->EnqueueSlotChangeEvent();
   }
 }
 
 ServoStyleRuleMap&
 ShadowRoot::ServoStyleRuleMap()
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -676,18 +676,17 @@ nsContentList::GetElementAt(uint32_t aIn
 
 nsIContent*
 nsContentList::Item(uint32_t aIndex)
 {
   return GetElementAt(aIndex);
 }
 
 void
-nsContentList::AttributeChanged(nsIDocument* aDocument,
-                                Element* aElement,
+nsContentList::AttributeChanged(Element* aElement,
                                 int32_t aNameSpaceID,
                                 nsAtom* aAttribute,
                                 int32_t aModType,
                                 const nsAttrValue* aOldValue)
 {
   NS_PRECONDITION(aElement, "Must have a content node to work with");
 
   if (!mFunc || !mFuncMayDependOnAttr || mState == LIST_DIRTY ||
@@ -710,51 +709,50 @@ nsContentList::AttributeChanged(nsIDocum
     // already not there, this is a no-op (though a potentially
     // expensive one).  Either way, no change of mState is required
     // here.
     mElements.RemoveElement(aElement);
   }
 }
 
 void
-nsContentList::ContentAppended(nsIDocument* aDocument,
-                               nsIContent* aContainer,
-                               nsIContent* aFirstNewContent)
+nsContentList::ContentAppended(nsIContent* aFirstNewContent)
 {
-  NS_PRECONDITION(aContainer, "Can't get at the new content if no container!");
+  nsIContent* container = aFirstNewContent->GetParent();
+  NS_PRECONDITION(container, "Can't get at the new content if no container!");
 
   /*
    * If the state is LIST_DIRTY then we have no useful information in our list
    * and we want to put off doing work as much as possible.
    *
-   * Also, if aContainer is anonymous from our point of view, we know that we
+   * Also, if container is anonymous from our point of view, we know that we
    * can't possibly be matching any of the kids.
    *
    * Optimize out also the common case when just one new node is appended and
    * it doesn't match us.
    */
   if (mState == LIST_DIRTY ||
-      !nsContentUtils::IsInSameAnonymousTree(mRootNode, aContainer) ||
-      !MayContainRelevantNodes(aContainer) ||
+      !nsContentUtils::IsInSameAnonymousTree(mRootNode, container) ||
+      !MayContainRelevantNodes(container) ||
       (!aFirstNewContent->HasChildren() &&
        !aFirstNewContent->GetNextSibling() &&
        !MatchSelf(aFirstNewContent))) {
     return;
   }
 
   /*
    * We want to handle the case of ContentAppended by sometimes
    * appending the content to our list, not just setting state to
    * LIST_DIRTY, since most of our ContentAppended notifications
    * should come during pageload and be at the end of the document.
    * Do a bit of work to see whether we could just append to what we
    * already have.
    */
 
-  int32_t count = aContainer->GetChildCount();
+  int32_t count = container->GetChildCount();
 
   if (count > 0) {
     uint32_t ourCount = mElements.Length();
     bool appendToList = false;
     if (ourCount == 0) {
       appendToList = true;
     } else {
       nsIContent* ourLastContent = mElements[ourCount - 1];
@@ -795,17 +793,17 @@ nsContentList::ContentAppended(nsIDocume
 
     /*
      * We're up to date.  That means someone's actively using us; we
      * may as well grab this content....
      */
     if (mDeep) {
       for (nsIContent* cur = aFirstNewContent;
            cur;
-           cur = cur->GetNextNode(aContainer)) {
+           cur = cur->GetNextNode(container)) {
         if (cur->IsElement() && Match(cur->AsElement())) {
           mElements.AppendElement(cur);
         }
       }
     } else {
       for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
         if (cur->IsElement() && Match(cur->AsElement())) {
           mElements.AppendElement(cur);
@@ -813,44 +811,37 @@ nsContentList::ContentAppended(nsIDocume
       }
     }
 
     ASSERT_IN_SYNC;
   }
 }
 
 void
-nsContentList::ContentInserted(nsIDocument *aDocument,
-                               nsIContent* aContainer,
-                               nsIContent* aChild)
+nsContentList::ContentInserted(nsIContent* aChild)
 {
-  // Note that aContainer can be null here if we are inserting into
-  // the document itself; any attempted optimizations to this method
-  // should deal with that.
+  // Note that aChild->GetParentNode() can be null here if we are inserting into
+  // the document itself; any attempted optimizations to this method should deal
+  // with that.
   if (mState != LIST_DIRTY &&
-      MayContainRelevantNodes(NODE_FROM(aContainer, aDocument)) &&
+      MayContainRelevantNodes(aChild->GetParentNode()) &&
       nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
       MatchSelf(aChild)) {
     SetDirty();
   }
 
   ASSERT_IN_SYNC;
 }
 
 void
-nsContentList::ContentRemoved(nsIDocument *aDocument,
-                              nsIContent* aContainer,
-                              nsIContent* aChild,
+nsContentList::ContentRemoved(nsIContent* aChild,
                               nsIContent* aPreviousSibling)
 {
-  // Note that aContainer can be null here if we are removing from
-  // the document itself; any attempted optimizations to this method
-  // should deal with that.
   if (mState != LIST_DIRTY &&
-      MayContainRelevantNodes(NODE_FROM(aContainer, aDocument)) &&
+      MayContainRelevantNodes(aChild->GetParentNode()) &&
       nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
       MatchSelf(aChild)) {
     SetDirty();
   }
 
   ASSERT_IN_SYNC;
 }
 
@@ -1108,30 +1099,29 @@ nsContentList::AssertInSync()
 
 JSObject*
 nsCachableElementsByNameNodeList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return NodeListBinding::Wrap(cx, this, aGivenProto);
 }
 
 void
-nsCachableElementsByNameNodeList::AttributeChanged(nsIDocument* aDocument,
-                                                   Element* aElement,
+nsCachableElementsByNameNodeList::AttributeChanged(Element* aElement,
                                                    int32_t aNameSpaceID,
                                                    nsAtom* aAttribute,
                                                    int32_t aModType,
                                                    const nsAttrValue* aOldValue)
 {
   // No need to rebuild the list if the changed attribute is not the name
   // attribute.
   if (aAttribute != nsGkAtoms::name) {
     return;
   }
 
-  nsCacheableFuncStringContentList::AttributeChanged(aDocument, aElement,
+  nsCacheableFuncStringContentList::AttributeChanged(aElement,
                                                      aNameSpaceID, aAttribute,
                                                      aModType, aOldValue);
 }
 
 //-----------------------------------------------------
 // nsCacheableFuncStringHTMLCollection
 
 JSObject*
@@ -1145,17 +1135,17 @@ nsCacheableFuncStringHTMLCollection::Wra
 
 JSObject*
 nsLabelsNodeList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return NodeListBinding::Wrap(cx, this, aGivenProto);
 }
 
 void
-nsLabelsNodeList::AttributeChanged(nsIDocument* aDocument, Element* aElement,
+nsLabelsNodeList::AttributeChanged(Element* aElement,
                                    int32_t aNameSpaceID, nsAtom* aAttribute,
                                    int32_t aModType,
                                    const nsAttrValue* aOldValue)
 {
   MOZ_ASSERT(aElement, "Must have a content node to work with");
   if (mState == LIST_DIRTY ||
       !nsContentUtils::IsInSameAnonymousTree(mRootNode, aElement)) {
     return;
@@ -1165,49 +1155,44 @@ nsLabelsNodeList::AttributeChanged(nsIDo
   if (aElement->IsHTMLElement(nsGkAtoms::input) &&
       aAttribute == nsGkAtoms::type && aNameSpaceID == kNameSpaceID_None) {
     SetDirty();
     return;
   }
 }
 
 void
-nsLabelsNodeList::ContentAppended(nsIDocument* aDocument,
-                                  nsIContent* aContainer,
-                                  nsIContent* aFirstNewContent)
+nsLabelsNodeList::ContentAppended(nsIContent* aFirstNewContent)
 {
+  nsIContent* container = aFirstNewContent->GetParent();
   // If a labelable element is moved to outside or inside of
   // nested associated labels, we're gonna have to modify
   // the content list.
   if (mState != LIST_DIRTY ||
-      nsContentUtils::IsInSameAnonymousTree(mRootNode, aContainer)) {
+      nsContentUtils::IsInSameAnonymousTree(mRootNode, container)) {
     SetDirty();
     return;
   }
 }
 
 void
-nsLabelsNodeList::ContentInserted(nsIDocument* aDocument,
-                                  nsIContent* aContainer,
-                                  nsIContent* aChild)
+nsLabelsNodeList::ContentInserted(nsIContent* aChild)
 {
   // If a labelable element is moved to outside or inside of
   // nested associated labels, we're gonna have to modify
   // the content list.
   if (mState != LIST_DIRTY ||
       nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild)) {
     SetDirty();
     return;
   }
 }
 
 void
-nsLabelsNodeList::ContentRemoved(nsIDocument* aDocument,
-                                 nsIContent* aContainer,
-                                 nsIContent* aChild,
+nsLabelsNodeList::ContentRemoved(nsIContent* aChild,
                                  nsIContent* aPreviousSibling)
 {
   // If a labelable element is removed, we're gonna have to clean
   // the content list.
   if (mState != LIST_DIRTY ||
       nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild)) {
     SetDirty();
     return;
--- a/dom/base/nsDOMMutationObserver.cpp
+++ b/dom/base/nsDOMMutationObserver.cpp
@@ -115,18 +115,17 @@ nsMutationReceiver::Disconnect(bool aRem
       static_cast<nsDOMMutationObserver*>(observer)->RemoveReceiver(this);
     }
     // UnbindObject may delete 'this'!
     target->UnbindObject(observer);
   }
 }
 
 void
-nsMutationReceiver::NativeAnonymousChildListChange(nsIDocument* aDocument,
-                                                   nsIContent* aContent,
+nsMutationReceiver::NativeAnonymousChildListChange(nsIContent* aContent,
                                                    bool aIsRemove) {
   if (!NativeAnonymousChildList()) {
     return;
   }
 
   nsINode* parent = aContent->GetParentNode();
   if (!parent ||
       (!Subtree() && Target() != parent) ||
@@ -147,18 +146,17 @@ nsMutationReceiver::NativeAnonymousChild
     m->mRemovedNodes->AppendElement(aContent);
   } else {
     m->mAddedNodes = new nsSimpleContentList(parent);
     m->mAddedNodes->AppendElement(aContent);
   }
 }
 
 void
-nsMutationReceiver::AttributeWillChange(nsIDocument* aDocument,
-                                        mozilla::dom::Element* aElement,
+nsMutationReceiver::AttributeWillChange(mozilla::dom::Element* aElement,
                                         int32_t aNameSpaceID,
                                         nsAtom* aAttribute,
                                         int32_t aModType,
                                         const nsAttrValue* aNewValue)
 {
   if (nsAutoMutationBatch::IsBatching() ||
       !ObservesAttr(RegisterTarget(), aElement, aNameSpaceID, aAttribute)) {
     return;
@@ -185,18 +183,17 @@ nsMutationReceiver::AttributeWillChange(
   if (AttributeOldValue() && m->mPrevValue.IsVoid()) {
     if (!aElement->GetAttr(aNameSpaceID, aAttribute, m->mPrevValue)) {
       m->mPrevValue.SetIsVoid(true);
     }
   }
 }
 
 void
-nsMutationReceiver::CharacterDataWillChange(nsIDocument* aDocument,
-                                            nsIContent* aContent,
+nsMutationReceiver::CharacterDataWillChange(nsIContent* aContent,
                                             const CharacterDataChangeInfo&)
 {
   if (nsAutoMutationBatch::IsBatching() ||
       !CharacterData() ||
       (!Subtree() && aContent != Target()) ||
       (Subtree() && RegisterTarget()->SubtreeRoot() != aContent->SubtreeRoot()) ||
       !IsObservable(aContent)) {
     return;
@@ -212,21 +209,19 @@ nsMutationReceiver::CharacterDataWillCha
     m->mTarget = aContent;
   }
   if (CharacterDataOldValue() && m->mPrevValue.IsVoid()) {
     aContent->GetText()->AppendTo(m->mPrevValue);
   }
 }
 
 void
-nsMutationReceiver::ContentAppended(nsIDocument* aDocument,
-                                    nsIContent* aContainer,
-                                    nsIContent* aFirstNewContent)
+nsMutationReceiver::ContentAppended(nsIContent* aFirstNewContent)
 {
-  nsINode* parent = NODE_FROM(aContainer, aDocument);
+  nsINode* parent = aFirstNewContent->GetParentNode();
   bool wantsChildList =
     ChildList() &&
     ((Subtree() && RegisterTarget()->SubtreeRoot() == parent->SubtreeRoot()) ||
      parent == Target());
   if (!wantsChildList || !IsObservable(aFirstNewContent)) {
     return;
   }
 
@@ -252,21 +247,19 @@ nsMutationReceiver::ContentAppended(nsID
   while (n) {
     m->mAddedNodes->AppendElement(static_cast<nsIContent*>(n));
     n = n->GetNextSibling();
   }
   m->mPreviousSibling = aFirstNewContent->GetPreviousSibling();
 }
 
 void
-nsMutationReceiver::ContentInserted(nsIDocument* aDocument,
-                                    nsIContent* aContainer,
-                                    nsIContent* aChild)
+nsMutationReceiver::ContentInserted(nsIContent* aChild)
 {
-  nsINode* parent = NODE_FROM(aContainer, aDocument);
+  nsINode* parent = aChild->GetParentNode();
   bool wantsChildList =
     ChildList() &&
     ((Subtree() && RegisterTarget()->SubtreeRoot() == parent->SubtreeRoot()) ||
      parent == Target());
   if (!wantsChildList || !IsObservable(aChild)) {
     return;
   }
 
@@ -286,26 +279,24 @@ nsMutationReceiver::ContentInserted(nsID
   m->mTarget = parent;
   m->mAddedNodes = new nsSimpleContentList(parent);
   m->mAddedNodes->AppendElement(aChild);
   m->mPreviousSibling = aChild->GetPreviousSibling();
   m->mNextSibling = aChild->GetNextSibling();
 }
 
 void
-nsMutationReceiver::ContentRemoved(nsIDocument* aDocument,
-                                   nsIContent* aContainer,
-                                   nsIContent* aChild,
+nsMutationReceiver::ContentRemoved(nsIContent* aChild,
                                    nsIContent* aPreviousSibling)
 {
   if (!IsObservable(aChild)) {
     return;
   }
 
-  nsINode* parent = NODE_FROM(aContainer, aDocument);
+  nsINode* parent = aChild->GetParentNode();
   if (Subtree() && parent->SubtreeRoot() != RegisterTarget()->SubtreeRoot()) {
     return;
   }
   if (nsAutoMutationBatch::IsBatching()) {
     if (nsAutoMutationBatch::IsRemovalDone()) {
       // This can happen for example if HTML parser parses to
       // context node, but needs to move elements around.
       return;
--- a/dom/base/nsDOMMutationObserver.h
+++ b/dom/base/nsDOMMutationObserver.h
@@ -399,23 +399,22 @@ public:
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
   NS_DECL_NSIMUTATIONOBSERVER_NATIVEANONYMOUSCHILDLISTCHANGE
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
   NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
 
-  virtual void AttributeSetToCurrentValue(nsIDocument* aDocument,
-                                          mozilla::dom::Element* aElement,
+  virtual void AttributeSetToCurrentValue(mozilla::dom::Element* aElement,
                                           int32_t aNameSpaceID,
                                           nsAtom* aAttribute) override
   {
     // We can reuse AttributeWillChange implementation.
-    AttributeWillChange(aDocument, aElement, aNameSpaceID, aAttribute,
+    AttributeWillChange(aElement, aNameSpaceID, aAttribute,
                         mozilla::dom::MutationEventBinding::MODIFICATION, nullptr);
   }
 
 protected:
   nsMutationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver);
 
   nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
   : nsMutationReceiverBase(aRegisterTarget, aParent)
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -3211,21 +3211,20 @@ nsFrameLoader::ApplySandboxFlags(uint32_
     if (!presentationURL.IsEmpty()) {
       sandboxFlags |= SANDBOXED_AUXILIARY_NAVIGATION;
     }
     mDocShell->SetSandboxFlags(sandboxFlags);
   }
 }
 
 /* virtual */ void
-nsFrameLoader::AttributeChanged(nsIDocument* aDocument,
-                                mozilla::dom::Element* aElement,
-                                int32_t      aNameSpaceID,
-                                nsAtom*     aAttribute,
-                                int32_t      aModType,
+nsFrameLoader::AttributeChanged(mozilla::dom::Element* aElement,
+                                int32_t aNameSpaceID,
+                                nsAtom* aAttribute,
+                                int32_t aModType,
                                 const nsAttrValue* aOldValue)
 {
   MOZ_ASSERT(mObservingOwnerContent);
 
   if (aNameSpaceID != kNameSpaceID_None ||
       (aAttribute != TypeAttrName() && aAttribute != nsGkAtoms::primary)) {
     return;
   }
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -274,17 +274,17 @@ public:
   void ReallyCloseWindow();
 
   // nsISupports
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   // nsWrapperCache
   virtual JSObject *WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override
   {
-    return GetWrapper();
+    MOZ_CRASH("We should never get here!");
   }
 
   // nsIGlobalJSObjectHolder
   virtual JSObject* GetGlobalJSObject() override;
 
   // nsIScriptGlobalObject
   JSObject *FastGetGlobalJSObject() const
   {
--- a/dom/base/nsIMutationObserver.h
+++ b/dom/base/nsIMutationObserver.h
@@ -103,201 +103,165 @@ public:
   /**
    * Notification that the node value of a data node (text, cdata, pi, comment)
    * will be changed.
    *
    * This notification is not sent when a piece of content is
    * added/removed from the document (the other notifications are used
    * for that).
    *
-   * @param aDocument The owner-document of aContent. Can be null.
    * @param aContent  The piece of content that changed. Is never null.
    * @param aInfo     The structure with information details about the change.
    *
    * @note Callers of this method might not hold a strong reference to the
    *       observer.  The observer is responsible for making sure it stays
    *       alive for the duration of the call as needed.  The observer may
    *       assume that this call will happen when there are script blockers on
    *       the stack.
    */
-  virtual void CharacterDataWillChange(nsIDocument* aDocument,
-                                       nsIContent* aContent,
+  virtual void CharacterDataWillChange(nsIContent* aContent,
                                        const CharacterDataChangeInfo&) = 0;
 
   /**
    * Notification that the node value of a data node (text, cdata, pi, comment)
    * has changed.
    *
    * This notification is not sent when a piece of content is
    * added/removed from the document (the other notifications are used
    * for that).
    *
-   * @param aDocument The owner-document of aContent. Can be null.
    * @param aContent  The piece of content that changed. Is never null.
    * @param aInfo     The structure with information details about the change.
    *
    * @note Callers of this method might not hold a strong reference to the
    *       observer.  The observer is responsible for making sure it stays
    *       alive for the duration of the call as needed.  The observer may
    *       assume that this call will happen when there are script blockers on
    *       the stack.
    */
-  virtual void CharacterDataChanged(nsIDocument* aDocument,
-                                    nsIContent* aContent,
+  virtual void CharacterDataChanged(nsIContent* aContent,
                                     const CharacterDataChangeInfo&) = 0;
 
   /**
    * Notification that an attribute of an element will change.  This
    * can happen before the BeginUpdate for the change and may not
    * always be followed by an AttributeChanged (in particular, if the
    * attribute doesn't actually change there will be no corresponding
    * AttributeChanged).
    *
-   * @param aDocument    The owner-document of aContent. Can be null.
    * @param aContent     The element whose attribute will change
    * @param aNameSpaceID The namespace id of the changing attribute
    * @param aAttribute   The name of the changing attribute
    * @param aModType     Whether or not the attribute will be added, changed, or
    *                     removed. The constants are defined in
    *                     MutationEvent.webidl.
    * @param aNewValue    The new value, IF it has been preparsed by
    *                     BeforeSetAttr, otherwise null.
    *
    * @note Callers of this method might not hold a strong reference to the
    *       observer.  The observer is responsible for making sure it stays
    *       alive for the duration of the call as needed.  The observer may
    *       assume that this call will happen when there are script blockers on
    *       the stack.
    */
-  virtual void AttributeWillChange(nsIDocument* aDocument,
-                                   mozilla::dom::Element* aElement,
-                                   int32_t      aNameSpaceID,
-                                   nsAtom*     aAttribute,
-                                   int32_t      aModType,
+  virtual void AttributeWillChange(mozilla::dom::Element* aElement,
+                                   int32_t aNameSpaceID,
+                                   nsAtom* aAttribute,
+                                   int32_t aModType,
                                    const nsAttrValue* aNewValue) = 0;
 
   /**
    * Notification that an attribute of an element has changed.
    *
-   * @param aDocument    The owner-document of aContent. Can be null.
    * @param aElement     The element whose attribute changed
    * @param aNameSpaceID The namespace id of the changed attribute
    * @param aAttribute   The name of the changed attribute
    * @param aModType     Whether or not the attribute was added, changed, or
    *                     removed. The constants are defined in
    *                     MutationEvent.webidl.
    * @param aOldValue    The old value, if either the old value or the new
    *                     value are StoresOwnData() (or absent); null otherwise.
    *
    * @note Callers of this method might not hold a strong reference to the
    *       observer.  The observer is responsible for making sure it stays
    *       alive for the duration of the call as needed.  The observer may
    *       assume that this call will happen when there are script blockers on
    *       the stack.
    */
-  virtual void AttributeChanged(nsIDocument* aDocument,
-                                mozilla::dom::Element* aElement,
-                                int32_t      aNameSpaceID,
-                                nsAtom*     aAttribute,
-                                int32_t      aModType,
+  virtual void AttributeChanged(mozilla::dom::Element* aElement,
+                                int32_t aNameSpaceID,
+                                nsAtom* aAttribute,
+                                int32_t aModType,
                                 const nsAttrValue* aOldValue) = 0;
 
   /**
    * Notification that the root of a native anonymous has been added
    * or removed.
    *
-   * @param aDocument    Owner doc of aContent
    * @param aContent     Anonymous node that's been added or removed
    * @param aIsRemove    True if it's a removal, false if an addition
    */
-  virtual void NativeAnonymousChildListChange(nsIDocument* aDocument,
-                                              nsIContent* aContent,
+  virtual void NativeAnonymousChildListChange(nsIContent* aContent,
                                               bool aIsRemove) {}
 
   /**
    * Notification that an attribute of an element has been
    * set to the value it already had.
    *
-   * @param aDocument    The owner-document of aContent.
    * @param aElement     The element whose attribute changed
    * @param aNameSpaceID The namespace id of the changed attribute
    * @param aAttribute   The name of the changed attribute
    */
-  virtual void AttributeSetToCurrentValue(nsIDocument* aDocument,
-                                          mozilla::dom::Element* aElement,
+  virtual void AttributeSetToCurrentValue(mozilla::dom::Element* aElement,
                                           int32_t aNameSpaceID,
                                           nsAtom* aAttribute) {}
 
   /**
    * Notification that one or more content nodes have been appended to the
    * child list of another node in the tree.
    *
-   * @param aDocument  The owner-document of aContent. Can be null.
-   * @param aContainer The container that had new children appended. Is never
-   *                   null.
-   * @param aFirstNewContent the node at aIndexInContainer in aContainer.
+   * @param aFirstNewContent the first node appended.
    *
    * @note Callers of this method might not hold a strong reference to the
    *       observer.  The observer is responsible for making sure it stays
    *       alive for the duration of the call as needed.  The observer may
    *       assume that this call will happen when there are script blockers on
    *       the stack.
    */
-  virtual void ContentAppended(nsIDocument* aDocument,
-                               nsIContent* aContainer,
-                               nsIContent* aFirstNewContent) = 0;
+  virtual void ContentAppended(nsIContent* aFirstNewContent) = 0;
 
   /**
    * Notification that a content node has been inserted as child to another
    * node in the tree.
    *
-   * @param aDocument  The owner-document of aContent, or, when aContainer
-   *                   is null, the container that had the child inserted.
-   *                   Can be null.
-   * @param aContainer The container that had new a child inserted. Can be
-   *                   null to indicate that the child was inserted into
-   *                   aDocument
-   * @param aChild     The newly inserted child.
-   * @param aIndexInContainer The index in the container of the new child.
+   * @param aChild The newly inserted child.
    *
    * @note Callers of this method might not hold a strong reference to the
    *       observer.  The observer is responsible for making sure it stays
    *       alive for the duration of the call as needed.  The observer may
    *       assume that this call will happen when there are script blockers on
    *       the stack.
    */
-  virtual void ContentInserted(nsIDocument* aDocument,
-                               nsIContent* aContainer,
-                               nsIContent* aChild) = 0;
+  virtual void ContentInserted(nsIContent* aChild) = 0;
 
   /**
    * Notification that a content node has been removed from the child list of
    * another node in the tree.
    *
-   * @param aDocument  The owner-document of aContent, or, when aContainer
-   *                   is null, the container that had the child removed.
-   *                   Can be null.
-   * @param aContainer The container that had new a child removed. Can be
-   *                   null to indicate that the child was removed from
-   *                   aDocument.
    * @param aChild     The child that was removed.
-   * @param aIndexInContainer The index in the container which the child used
-   *                          to have.
    * @param aPreviousSibling The previous sibling to the child that was removed.
    *                         Can be null if there was no previous sibling.
    *
    * @note Callers of this method might not hold a strong reference to the
    *       observer.  The observer is responsible for making sure it stays
    *       alive for the duration of the call as needed.  The observer may
    *       assume that this call will happen when there are script blockers on
    *       the stack.
    */
-  virtual void ContentRemoved(nsIDocument* aDocument,
-                              nsIContent* aContainer,
-                              nsIContent* aChild,
+  virtual void ContentRemoved(nsIContent* aChild,
                               nsIContent* aPreviousSibling) = 0;
 
  /**
    * The node is in the process of being destroyed. Calling QI on the node is
    * not supported, however it is possible to get children and flags through
    * nsINode as well as calling IsContent and casting to nsIContent to get
    * attributes.
    *
@@ -331,60 +295,49 @@ public:
    */
 
   virtual void ParentChainChanged(nsIContent *aContent) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIMutationObserver, NS_IMUTATION_OBSERVER_IID)
 
 #define NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE                  \
-    virtual void CharacterDataWillChange(nsIDocument* aDocument,             \
-                                         nsIContent* aContent,               \
+    virtual void CharacterDataWillChange(nsIContent* aContent,               \
                                          const CharacterDataChangeInfo& aInfo) override;
 
 #define NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED                     \
-    virtual void CharacterDataChanged(nsIDocument* aDocument,                \
-                                      nsIContent* aContent,                  \
+    virtual void CharacterDataChanged(nsIContent* aContent,                  \
                                       const CharacterDataChangeInfo& aInfo) override;
 
 #define NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE                      \
-    virtual void AttributeWillChange(nsIDocument* aDocument,                 \
-                                     mozilla::dom::Element* aElement,        \
+    virtual void AttributeWillChange(mozilla::dom::Element* aElement,        \
                                      int32_t aNameSpaceID,                   \
                                      nsAtom* aAttribute,                    \
                                      int32_t aModType,                       \
                                      const nsAttrValue* aNewValue) override;
 
 #define NS_DECL_NSIMUTATIONOBSERVER_NATIVEANONYMOUSCHILDLISTCHANGE           \
-    virtual void NativeAnonymousChildListChange(nsIDocument* aDocument,      \
-                                                nsIContent* aContent,        \
+    virtual void NativeAnonymousChildListChange(nsIContent* aContent,        \
                                                 bool aIsRemove) override;
 
 #define NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED                         \
-    virtual void AttributeChanged(nsIDocument* aDocument,                    \
-                                  mozilla::dom::Element* aElement,           \
+    virtual void AttributeChanged(mozilla::dom::Element* aElement,           \
                                   int32_t aNameSpaceID,                      \
-                                  nsAtom* aAttribute,                       \
+                                  nsAtom* aAttribute,                        \
                                   int32_t aModType,                          \
                                   const nsAttrValue* aOldValue) override;
 
 #define NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED                          \
-    virtual void ContentAppended(nsIDocument* aDocument,                     \
-                                 nsIContent* aContainer,                     \
-                                 nsIContent* aFirstNewContent) override;
+    virtual void ContentAppended(nsIContent* aFirstNewContent) override;
 
 #define NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED                          \
-    virtual void ContentInserted(nsIDocument* aDocument,                     \
-                                 nsIContent* aContainer,                     \
-                                 nsIContent* aChild) override;
+    virtual void ContentInserted(nsIContent* aChild) override;
 
 #define NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED                           \
-    virtual void ContentRemoved(nsIDocument* aDocument,                      \
-                                nsIContent* aContainer,                      \
-                                nsIContent* aChild,                          \
+    virtual void ContentRemoved(nsIContent* aChild,                          \
                                 nsIContent* aPreviousSibling) override;
 
 #define NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED                      \
     virtual void NodeWillBeDestroyed(const nsINode* aNode) override;
 
 #define NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED                       \
     virtual void ParentChainChanged(nsIContent *aContent) override;
 
@@ -397,74 +350,62 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIMutatio
     NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED                              \
     NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED                              \
     NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED                               \
     NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED                          \
     NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
 
 #define NS_IMPL_NSIMUTATIONOBSERVER_CORE_STUB(_class)                     \
 void                                                                      \
-_class::NodeWillBeDestroyed(const nsINode* aNode)                               \
+_class::NodeWillBeDestroyed(const nsINode* aNode)                         \
 {                                                                         \
 }
 
 #define NS_IMPL_NSIMUTATIONOBSERVER_CONTENT(_class)                       \
 void                                                                      \
-_class::CharacterDataWillChange(nsIDocument* aDocument,                   \
-                                nsIContent* aContent,                     \
+_class::CharacterDataWillChange(nsIContent* aContent,                     \
                                 const CharacterDataChangeInfo& aInfo)     \
 {                                                                         \
 }                                                                         \
 void                                                                      \
-_class::CharacterDataChanged(nsIDocument* aDocument,                      \
-                             nsIContent* aContent,                        \
+_class::CharacterDataChanged(nsIContent* aContent,                        \
                              const CharacterDataChangeInfo& aInfo)        \
 {                                                                         \
 }                                                                         \
 void                                                                      \
-_class::AttributeWillChange(nsIDocument* aDocument,                       \
-                            mozilla::dom::Element* aElement,              \
+_class::AttributeWillChange(mozilla::dom::Element* aElement,              \
                             int32_t aNameSpaceID,                         \
                             nsAtom* aAttribute,                          \
                             int32_t aModType,                             \
                             const nsAttrValue* aNewValue)                 \
 {                                                                         \
 }                                                                         \
 void                                                                      \
-_class::NativeAnonymousChildListChange(nsIDocument* aDocument,            \
-                                       nsIContent* aContent,              \
+_class::NativeAnonymousChildListChange(nsIContent* aContent,              \
                                        bool aIsRemove)                    \
 {                                                                         \
 }                                                                         \
 void                                                                      \
-_class::AttributeChanged(nsIDocument* aDocument,                          \
-                         mozilla::dom::Element* aElement,                 \
+_class::AttributeChanged(mozilla::dom::Element* aElement,                 \
                          int32_t aNameSpaceID,                            \
                          nsAtom* aAttribute,                             \
                          int32_t aModType,                                \
                          const nsAttrValue* aOldValue)                    \
 {                                                                         \
 }                                                                         \
 void                                                                      \
-_class::ContentAppended(nsIDocument* aDocument,                           \
-                        nsIContent* aContainer,                           \
-                        nsIContent* aFirstNewContent)                     \
+_class::ContentAppended(nsIContent* aFirstNewContent)                     \
 {                                                                         \
 }                                                                         \
 void                                                                      \
-_class::ContentInserted(nsIDocument* aDocument,                           \
-                        nsIContent* aContainer,                           \
-                        nsIContent* aChild)                               \
+_class::ContentInserted(nsIContent* aChild)                               \
 {                                                                         \
 }                                                                         \
 void                                                                      \
-_class::ContentRemoved(nsIDocument* aDocument,                            \
-                       nsIContent* aContainer,                            \
-                       nsIContent* aChild,                                \
-                       nsIContent* aPreviousSibling)                      \
+_class::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)  \
 {                                                                         \
 }                                                                         \
 void                                                                      \
 _class::ParentChainChanged(nsIContent *aContent)                          \
 {                                                                         \
 }
 
 
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -129,132 +129,113 @@ enum class IsRemoveNotification
   PR_END_MACRO
 
 void
 nsNodeUtils::CharacterDataWillChange(nsIContent* aContent,
                                      const CharacterDataChangeInfo& aInfo)
 {
   nsIDocument* doc = aContent->OwnerDoc();
   IMPL_MUTATION_NOTIFICATION(CharacterDataWillChange, aContent,
-                             (doc, aContent, aInfo), IsRemoveNotification::No);
+                             (aContent, aInfo), IsRemoveNotification::No);
 }
 
 void
 nsNodeUtils::CharacterDataChanged(nsIContent* aContent,
                                   const CharacterDataChangeInfo& aInfo)
 {
   nsIDocument* doc = aContent->OwnerDoc();
   IMPL_MUTATION_NOTIFICATION(CharacterDataChanged, aContent,
-                             (doc, aContent, aInfo), IsRemoveNotification::No);
+                             (aContent, aInfo), IsRemoveNotification::No);
 }
 
 void
 nsNodeUtils::AttributeWillChange(Element* aElement,
                                  int32_t aNameSpaceID,
                                  nsAtom* aAttribute,
                                  int32_t aModType,
                                  const nsAttrValue* aNewValue)
 {
   nsIDocument* doc = aElement->OwnerDoc();
   IMPL_MUTATION_NOTIFICATION(AttributeWillChange, aElement,
-                             (doc, aElement, aNameSpaceID, aAttribute,
+                             (aElement, aNameSpaceID, aAttribute,
                               aModType, aNewValue), IsRemoveNotification::No);
 }
 
 void
 nsNodeUtils::AttributeChanged(Element* aElement,
                               int32_t aNameSpaceID,
                               nsAtom* aAttribute,
                               int32_t aModType,
                               const nsAttrValue* aOldValue)
 {
   nsIDocument* doc = aElement->OwnerDoc();
   IMPL_MUTATION_NOTIFICATION(AttributeChanged, aElement,
-                             (doc, aElement, aNameSpaceID, aAttribute,
+                             (aElement, aNameSpaceID, aAttribute,
                               aModType, aOldValue), IsRemoveNotification::No);
 }
 
 void
 nsNodeUtils::AttributeSetToCurrentValue(Element* aElement,
                                         int32_t aNameSpaceID,
                                         nsAtom* aAttribute)
 {
   nsIDocument* doc = aElement->OwnerDoc();
   IMPL_MUTATION_NOTIFICATION(AttributeSetToCurrentValue, aElement,
-                             (doc, aElement, aNameSpaceID, aAttribute),
+                             (aElement, aNameSpaceID, aAttribute),
                              IsRemoveNotification::No);
 }
 
 void
 nsNodeUtils::ContentAppended(nsIContent* aContainer,
                              nsIContent* aFirstNewContent)
 {
   nsIDocument* doc = aContainer->OwnerDoc();
 
   IMPL_MUTATION_NOTIFICATION(ContentAppended, aContainer,
-                             (doc, aContainer, aFirstNewContent),
+                             (aFirstNewContent),
                              IsRemoveNotification::No);
 }
 
 void
 nsNodeUtils::NativeAnonymousChildListChange(nsIContent* aContent,
                                             bool aIsRemove)
 {
   nsIDocument* doc = aContent->OwnerDoc();
   auto isRemove = aIsRemove
     ? IsRemoveNotification::Yes : IsRemoveNotification::No;
   IMPL_MUTATION_NOTIFICATION(NativeAnonymousChildListChange, aContent,
-                            (doc, aContent, aIsRemove),
+                            (aContent, aIsRemove),
                             isRemove);
 }
 
 void
 nsNodeUtils::ContentInserted(nsINode* aContainer,
                              nsIContent* aChild)
 {
   NS_PRECONDITION(aContainer->IsContent() ||
                   aContainer->IsNodeOfType(nsINode::eDOCUMENT),
                   "container must be an nsIContent or an nsIDocument");
-  nsIContent* container;
   nsIDocument* doc = aContainer->OwnerDoc();
-  nsIDocument* document;
-  if (aContainer->IsContent()) {
-    container = aContainer->AsContent();
-    document = doc;
-  } else {
-    container = nullptr;
-    document = static_cast<nsIDocument*>(aContainer);
-  }
-
-  IMPL_MUTATION_NOTIFICATION(ContentInserted, aContainer,
-                             (document, container, aChild),
+  IMPL_MUTATION_NOTIFICATION(ContentInserted, aContainer, (aChild),
                              IsRemoveNotification::No);
 }
 
 void
 nsNodeUtils::ContentRemoved(nsINode* aContainer,
                             nsIContent* aChild,
                             nsIContent* aPreviousSibling)
 {
   NS_PRECONDITION(aContainer->IsContent() ||
                   aContainer->IsNodeOfType(nsINode::eDOCUMENT),
                   "container must be an nsIContent or an nsIDocument");
-  nsIContent* container;
   nsIDocument* doc = aContainer->OwnerDoc();
-  nsIDocument* document;
-  if (aContainer->IsContent()) {
-    container = static_cast<nsIContent*>(aContainer);
-    document = doc;
-  } else {
-    container = nullptr;
-    document = static_cast<nsIDocument*>(aContainer);
-  }
-
+  MOZ_ASSERT(aChild->GetParentNode() == aContainer,
+             "We expect the parent link to be still around at this point");
   IMPL_MUTATION_NOTIFICATION(ContentRemoved, aContainer,
-                             (document, container, aChild, aPreviousSibling),
+                             (aChild, aPreviousSibling),
                              IsRemoveNotification::Yes);
 }
 
 Maybe<NonOwningAnimationTarget>
 nsNodeUtils::GetTargetForAnimation(const Animation* aAnimation)
 {
   AnimationEffectReadOnly* effect = aAnimation->GetEffect();
   if (!effect || !effect->AsKeyframeEffect()) {
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -512,18 +512,17 @@ nsRange::UnregisterCommonAncestor(nsINod
     UnmarkDescendants(aNode);
   }
 }
 
 /******************************************************
  * nsIMutationObserver implementation
  ******************************************************/
 void
-nsRange::CharacterDataChanged(nsIDocument* aDocument,
-                              nsIContent* aContent,
+nsRange::CharacterDataChanged(nsIContent* aContent,
                               const CharacterDataChangeInfo& aInfo)
 {
   MOZ_ASSERT(!mNextEndRef);
   MOZ_ASSERT(!mNextStartRef);
   MOZ_ASSERT(mIsPositioned, "shouldn't be notified if not positioned");
 
   nsINode* newRoot = nullptr;
   RawRangeBoundary newStart;
@@ -668,25 +667,22 @@ nsRange::CharacterDataChanged(nsIDocumen
     }
     DoSetRange(newStart, newEnd,
                newRoot ? newRoot : mRoot.get(),
                !newEnd.Container()->GetParentNode() || !newStart.Container()->GetParentNode());
   }
 }
 
 void
-nsRange::ContentAppended(nsIDocument* aDocument,
-                         nsIContent*  aContainer,
-                         nsIContent*  aFirstNewContent)
+nsRange::ContentAppended(nsIContent*  aFirstNewContent)
 {
   NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
 
-  nsINode* container = NODE_FROM(aContainer, aDocument);
+  nsINode* container = aFirstNewContent->GetParentNode();
   MOZ_ASSERT(container);
-  MOZ_ASSERT(aFirstNewContent->GetParentNode() == container);
   if (container->IsSelectionDescendant() && IsInSelection()) {
     nsINode* child = aFirstNewContent;
     while (child) {
       if (!child->IsDescendantOfCommonAncestorForRangeInSelection()) {
         MarkDescendants(child);
         child->SetDescendantOfCommonAncestorForRangeInSelection();
       }
       child = child->GetNextSibling();
@@ -706,24 +702,22 @@ nsRange::ContentAppended(nsIDocument* aD
       MOZ_ASSERT(mNextEndRef == aFirstNewContent);
       mNextEndRef = nullptr;
     }
     DoSetRange(mStart.AsRaw(), mEnd.AsRaw(), mRoot, true);
   }
 }
 
 void
-nsRange::ContentInserted(nsIDocument* aDocument,
-                         nsIContent* aContainer,
-                         nsIContent* aChild)
+nsRange::ContentInserted(nsIContent* aChild)
 {
   MOZ_ASSERT(mIsPositioned, "shouldn't be notified if not positioned");
 
   bool updateBoundaries = false;
-  nsINode* container = NODE_FROM(aContainer, aDocument);
+  nsINode* container = aChild->GetParentNode();
   MOZ_ASSERT(container);
   RawRangeBoundary newStart(mStart);
   RawRangeBoundary newEnd(mEnd);
   MOZ_ASSERT(aChild->GetParentNode() == container);
 
   // Invalidate boundary offsets if a child that may have moved them was
   // inserted.
   if (container == mStart.Container()) {
@@ -758,23 +752,22 @@ nsRange::ContentInserted(nsIDocument* aD
   }
 
   if (updateBoundaries) {
     DoSetRange(newStart, newEnd, mRoot);
   }
 }
 
 void
-nsRange::ContentRemoved(nsIDocument* aDocument,
-                        nsIContent* aContainer,
-                        nsIContent* aChild,
-                        nsIContent* aPreviousSibling)
+nsRange::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
 {
   MOZ_ASSERT(mIsPositioned, "shouldn't be notified if not positioned");
-  nsINode* container = NODE_FROM(aContainer, aDocument);
+  nsINode* container = aChild->GetParentNode();
+  MOZ_ASSERT(container);
+
   RawRangeBoundary newStart;
   RawRangeBoundary newEnd;
   Maybe<bool> gravitateStart;
   bool gravitateEnd;
 
   // Adjust position if a sibling was removed...
   if (container == mStart.Container()) {
     // We're only interested if our boundary reference was removed, otherwise
--- a/dom/base/nsTextNode.cpp
+++ b/dom/base/nsTextNode.cpp
@@ -268,18 +268,17 @@ nsAttributeTextNode::UnbindFromTree(bool
     // in the document.
     mGrandparent->RemoveMutationObserver(this);
     mGrandparent = nullptr;
   }
   nsTextNode::UnbindFromTree(aDeep, aNullParent);
 }
 
 void
-nsAttributeTextNode::AttributeChanged(nsIDocument* aDocument,
-                                      Element* aElement,
+nsAttributeTextNode::AttributeChanged(Element* aElement,
                                       int32_t aNameSpaceID,
                                       nsAtom* aAttribute,
                                       int32_t aModType,
                                       const nsAttrValue* aOldValue)
 {
   if (aNameSpaceID == mNameSpaceID && aAttribute == mAttrName &&
       aElement == mGrandparent) {
     // Since UpdateText notifies, do it when it's safe to run script.  Note
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -920,18 +920,17 @@ IMEContentObserver::OnMouseButtonEvent(n
   bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
   if (consumed) {
     aMouseEvent->PreventDefault();
   }
   return consumed;
 }
 
 void
-IMEContentObserver::CharacterDataWillChange(nsIDocument* aDocument,
-                                            nsIContent* aContent,
+IMEContentObserver::CharacterDataWillChange(nsIContent* aContent,
                                             const CharacterDataChangeInfo& aInfo)
 {
   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
                "character data changed for non-text node");
   MOZ_ASSERT(mPreCharacterDataChangeLength < 0,
              "CharacterDataChanged() should've reset "
              "mPreCharacterDataChangeLength");
 
@@ -953,18 +952,17 @@ IMEContentObserver::CharacterDataWillCha
     ContentEventHandler::GetNativeTextLength(aContent, aInfo.mChangeStart,
                                              aInfo.mChangeEnd);
   MOZ_ASSERT(mPreCharacterDataChangeLength >=
                aInfo.mChangeEnd - aInfo.mChangeStart,
              "The computed length must be same as or larger than XP length");
 }
 
 void
-IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument,
-                                         nsIContent* aContent,
+IMEContentObserver::CharacterDataChanged(nsIContent* aContent,
                                          const CharacterDataChangeInfo& aInfo)
 {
   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
                "character data changed for non-text node");
 
   if (!NeedsTextChangeNotification() ||
       !nsContentUtils::IsInSameAnonymousTree(mRootContent, aContent)) {
     return;
@@ -1102,50 +1100,43 @@ IMEContentObserver::NotifyContentAdded(n
 
   TextChangeData data(offset, offset, offset + addingLength,
                       IsEditorHandlingEventForComposition(),
                       IsEditorComposing());
   MaybeNotifyIMEOfTextChange(data);
 }
 
 void
-IMEContentObserver::ContentAppended(nsIDocument* aDocument,
-                                    nsIContent* aContainer,
-                                    nsIContent* aFirstNewContent)
+IMEContentObserver::ContentAppended(nsIContent* aFirstNewContent)
 {
-  NotifyContentAdded(NODE_FROM(aContainer, aDocument),
-                     aFirstNewContent, aContainer->GetLastChild());
+  nsIContent* parent = aFirstNewContent->GetParent();
+  MOZ_ASSERT(parent);
+  NotifyContentAdded(parent, aFirstNewContent, parent->GetLastChild());
 }
 
 void
-IMEContentObserver::ContentInserted(nsIDocument* aDocument,
-                                    nsIContent* aContainer,
-                                    nsIContent* aChild)
+IMEContentObserver::ContentInserted(nsIContent* aChild)
 {
   MOZ_ASSERT(aChild);
-  NotifyContentAdded(NODE_FROM(aContainer, aDocument),
-                     aChild, aChild);
+  NotifyContentAdded(aChild->GetParentNode(), aChild, aChild);
 }
 
 void
-IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
-                                   nsIContent* aContainer,
-                                   nsIContent* aChild,
+IMEContentObserver::ContentRemoved(nsIContent* aChild,
                                    nsIContent* aPreviousSibling)
 {
   if (!NeedsTextChangeNotification() ||
       !nsContentUtils::IsInSameAnonymousTree(mRootContent, aChild)) {
     return;
   }
 
   mEndOfAddedTextCache.Clear();
   MaybeNotifyIMEOfAddedTextDuringDocumentChange();
 
-  nsINode* containerNode = NODE_FROM(aContainer, aDocument);
-
+  nsINode* containerNode = aChild->GetParentNode();
   MOZ_ASSERT(containerNode);
 
   uint32_t offset = 0;
   nsresult rv = NS_OK;
   if (!mStartOfRemovingTextRangeCache.Match(containerNode, aPreviousSibling)) {
     // At removing a child node of aContainer, we need the line break caused
     // by open tag of aContainer.  Be careful when aPreviousSibling is nullptr.
 
@@ -1186,34 +1177,32 @@ IMEContentObserver::ContentRemoved(nsIDo
 
   TextChangeData data(offset, offset + textLength, offset,
                       IsEditorHandlingEventForComposition(),
                       IsEditorComposing());
   MaybeNotifyIMEOfTextChange(data);
 }
 
 void
-IMEContentObserver::AttributeWillChange(nsIDocument* aDocument,
-                                        dom::Element* aElement,
+IMEContentObserver::AttributeWillChange(dom::Element* aElement,
                                         int32_t aNameSpaceID,
                                         nsAtom* aAttribute,
                                         int32_t aModType,
                                         const nsAttrValue* aNewValue)
 {
   if (!NeedsTextChangeNotification()) {
     return;
   }
 
   mPreAttrChangeLength =
     ContentEventHandler::GetNativeTextLengthBefore(aElement, mRootContent);
 }
 
 void
-IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
-                                     dom::Element* aElement,
+IMEContentObserver::AttributeChanged(dom::Element* aElement,
                                      int32_t aNameSpaceID,
                                      nsAtom* aAttribute,
                                      int32_t aModType,
                                      const nsAttrValue* aOldValue)
 {
   if (!NeedsTextChangeNotification()) {
     return;
   }
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1526,19 +1526,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPlayPromises)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSeekDOMPromise)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSetMediaKeysDOMPromise)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLMediaElement, nsGenericHTMLElement)
 
 void
-HTMLMediaElement::ContentRemoved(nsIDocument* aDocument,
-                                 nsIContent* aContainer,
-                                 nsIContent* aChild,
+HTMLMediaElement::ContentRemoved(nsIContent* aChild,
                                  nsIContent* aPreviousSibling)
 {
   if (aChild == mSourcePointer) {
     mSourcePointer = aPreviousSibling;
   }
 }
 
 already_AddRefed<MediaSource>
--- a/dom/html/HTMLOutputElement.cpp
+++ b/dom/html/HTMLOutputElement.cpp
@@ -174,40 +174,33 @@ void HTMLOutputElement::DescendantsChang
 {
   if (mIsDoneAddingChildren && mValueModeFlag == eModeDefault) {
     nsContentUtils::GetNodeTextContent(this, true, mDefaultValue);
   }
 }
 
 // nsIMutationObserver
 
-void HTMLOutputElement::CharacterDataChanged(nsIDocument* aDocument,
-                                             nsIContent* aContent,
+void HTMLOutputElement::CharacterDataChanged(nsIContent* aContent,
                                              const CharacterDataChangeInfo&)
 {
   DescendantsChanged();
 }
 
-void HTMLOutputElement::ContentAppended(nsIDocument* aDocument,
-                                        nsIContent* aContainer,
-                                        nsIContent* aFirstNewContent)
+void HTMLOutputElement::ContentAppended(nsIContent* aFirstNewContent)
 {
   DescendantsChanged();
 }
 
-void HTMLOutputElement::ContentInserted(nsIDocument* aDocument,
-                                        nsIContent* aContainer,
-                                        nsIContent* aChild)
+void HTMLOutputElement::ContentInserted(nsIContent* aChild)
 {
   DescendantsChanged();
 }
 
-void HTMLOutputElement::ContentRemoved(nsIDocument* aDocument,
-                                       nsIContent* aContainer,
-                                       nsIContent* aChild,
+void HTMLOutputElement::ContentRemoved(nsIContent* aChild,
                                        nsIContent* aPreviousSibling)
 {
   DescendantsChanged();
 }
 
 JSObject*
 HTMLOutputElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
--- a/dom/html/HTMLStyleElement.cpp
+++ b/dom/html/HTMLStyleElement.cpp
@@ -59,43 +59,36 @@ void
 HTMLStyleElement::SetDisabled(bool aDisabled)
 {
   if (StyleSheet* ss = GetSheet()) {
     ss->SetDisabled(aDisabled);
   }
 }
 
 void
-HTMLStyleElement::CharacterDataChanged(nsIDocument* aDocument,
-                                       nsIContent* aContent,
+HTMLStyleElement::CharacterDataChanged(nsIContent* aContent,
                                        const CharacterDataChangeInfo&)
 {
   ContentChanged(aContent);
 }
 
 void
-HTMLStyleElement::ContentAppended(nsIDocument* aDocument,
-                                  nsIContent* aContainer,
-                                  nsIContent* aFirstNewContent)
+HTMLStyleElement::ContentAppended(nsIContent* aFirstNewContent)
 {
-  ContentChanged(aContainer);
+  ContentChanged(aFirstNewContent->GetParent());
 }
 
 void
-HTMLStyleElement::ContentInserted(nsIDocument* aDocument,
-                                  nsIContent* aContainer,
-                                  nsIContent* aChild)
+HTMLStyleElement::ContentInserted(nsIContent* aChild)
 {
   ContentChanged(aChild);
 }
 
 void
-HTMLStyleElement::ContentRemoved(nsIDocument* aDocument,
-                                 nsIContent* aContainer,
-                                 nsIContent* aChild,
+HTMLStyleElement::ContentRemoved(nsIContent* aChild,
                                  nsIContent* aPreviousSibling)
 {
   ContentChanged(aChild);
 }
 
 void
 HTMLStyleElement::ContentChanged(nsIContent* aContent)
 {
--- a/dom/html/HTMLTableElement.cpp
+++ b/dom/html/HTMLTableElement.cpp
@@ -442,60 +442,55 @@ TableRowsCollection::HandleInsert(nsICon
 
   mRows.InsertElementAt(index, aChild);
   return index + 1;
 }
 
 // nsIMutationObserver
 
 void
-TableRowsCollection::ContentAppended(nsIDocument* aDocument,
-                                     nsIContent* aContainer,
-                                     nsIContent* aFirstNewContent)
+TableRowsCollection::ContentAppended(nsIContent* aFirstNewContent)
 {
+  nsIContent* container = aFirstNewContent->GetParent();
   if (!nsContentUtils::IsInSameAnonymousTree(mParent, aFirstNewContent) ||
-      !InterestingContainer(aContainer)) {
+      !InterestingContainer(container)) {
     return;
   }
 
   // We usually can't guess where we need to start inserting, unless we're
   // appending into mParent, in which case we can provide the guess that we
   // should insert at the end of the body, which can help us avoid potentially
   // expensive work in the common case.
-  int32_t indexGuess = mParent == aContainer ? mFootStart : -1;
+  int32_t indexGuess = mParent == container ? mFootStart : -1;
 
   // Insert each of the newly added content one at a time. The indexGuess should
   // make insertions of a large number of elements cheaper.
   for (nsIContent* content = aFirstNewContent;
        content; content = content->GetNextSibling()) {
-    indexGuess = HandleInsert(aContainer, content, indexGuess);
+    indexGuess = HandleInsert(container, content, indexGuess);
   }
 }
 
 void
-TableRowsCollection::ContentInserted(nsIDocument* aDocument,
-                                     nsIContent* aContainer,
-                                     nsIContent* aChild)
+TableRowsCollection::ContentInserted(nsIContent* aChild)
 {
   if (!nsContentUtils::IsInSameAnonymousTree(mParent, aChild) ||
-      !InterestingContainer(aContainer)) {
+      !InterestingContainer(aChild->GetParent())) {
     return;
   }
 
-  HandleInsert(aContainer, aChild);
+  HandleInsert(aChild->GetParent(), aChild);
 }
 
 void
-TableRowsCollection::ContentRemoved(nsIDocument* aDocument,
-                                    nsIContent* aContainer,
-                                    nsIContent* aChild,
+TableRowsCollection::ContentRemoved(nsIContent* aChild,
                                     nsIContent* aPreviousSibling)
 {
   if (!nsContentUtils::IsInSameAnonymousTree(mParent, aChild) ||
-      !InterestingContainer(aContainer)) {
+      !InterestingContainer(aChild->GetParent())) {
     return;
   }
 
   // If the element being removed is a `tr`, we can just remove it from our
   // list. It shouldn't change the order of anything.
   if (aChild->IsHTMLElement(nsGkAtoms::tr)) {
     size_t index = mRows.IndexOf(aChild);
     if (index != nsTArray<nsCOMPtr<nsIContent>>::NoIndex) {
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -983,43 +983,36 @@ HTMLTextAreaElement::BeforeSetAttr(int32
     mDisabledChanged = true;
   }
 
   return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName,
                                                  aValue, aNotify);
 }
 
 void
-HTMLTextAreaElement::CharacterDataChanged(nsIDocument* aDocument,
-                                          nsIContent* aContent,
+HTMLTextAreaElement::CharacterDataChanged(nsIContent* aContent,
                                           const CharacterDataChangeInfo&)
 {
   ContentChanged(aContent);
 }
 
 void
-HTMLTextAreaElement::ContentAppended(nsIDocument* aDocument,
-                                     nsIContent* aContainer,
-                                     nsIContent* aFirstNewContent)
+HTMLTextAreaElement::ContentAppended(nsIContent* aFirstNewContent)
 {
   ContentChanged(aFirstNewContent);
 }
 
 void
-HTMLTextAreaElement::ContentInserted(nsIDocument* aDocument,
-                                     nsIContent* aContainer,
-                                     nsIContent* aChild)
+HTMLTextAreaElement::ContentInserted(nsIContent* aChild)
 {
   ContentChanged(aChild);
 }
 
 void
-HTMLTextAreaElement::ContentRemoved(nsIDocument* aDocument,
-                                    nsIContent* aContainer,
-                                    nsIContent* aChild,
+HTMLTextAreaElement::ContentRemoved(nsIContent* aChild,
                                     nsIContent* aPreviousSibling)
 {
   ContentChanged(aChild);
 }
 
 void
 HTMLTextAreaElement::ContentChanged(nsIContent* aContent)
 {
--- a/dom/html/HTMLTitleElement.cpp
+++ b/dom/html/HTMLTitleElement.cpp
@@ -49,44 +49,37 @@ HTMLTitleElement::GetText(DOMString& aTe
 
 void
 HTMLTitleElement::SetText(const nsAString& aText, ErrorResult& aError)
 {
   aError = nsContentUtils::SetNodeTextContent(this, aText, true);
 }
 
 void
-HTMLTitleElement::CharacterDataChanged(nsIDocument *aDocument,
-                                       nsIContent *aContent,
+HTMLTitleElement::CharacterDataChanged(nsIContent* aContent,
                                        const CharacterDataChangeInfo&)
 {
   SendTitleChangeEvent(false);
 }
 
 void
-HTMLTitleElement::ContentAppended(nsIDocument *aDocument,
-                                  nsIContent *aContainer,
-                                  nsIContent *aFirstNewContent)
+HTMLTitleElement::ContentAppended(nsIContent* aFirstNewContent)
 {
   SendTitleChangeEvent(false);
 }
 
 void
-HTMLTitleElement::ContentInserted(nsIDocument *aDocument,
-                                  nsIContent *aContainer,
-                                  nsIContent *aChild)
+HTMLTitleElement::ContentInserted(nsIContent* aChild)
 {
   SendTitleChangeEvent(false);
 }
 
 void
-HTMLTitleElement::ContentRemoved(nsIDocument *aDocument,
-                                 nsIContent *aContainer,
-                                 nsIContent *aChild,
-                                 nsIContent *aPreviousSibling)
+HTMLTitleElement::ContentRemoved(nsIContent* aChild,
+                                 nsIContent* aPreviousSibling)
 {
   SendTitleChangeEvent(false);
 }
 
 nsresult
 HTMLTitleElement::BindToTree(nsIDocument *aDocument,
                              nsIContent *aParent,
                              nsIContent *aBindingParent,
--- a/dom/html/nsDOMStringMap.cpp
+++ b/dom/html/nsDOMStringMap.cpp
@@ -246,18 +246,19 @@ bool nsDOMStringMap::AttrToDataProp(cons
       aResult.Append(*cur);
     }
   }
 
   return true;
 }
 
 void
-nsDOMStringMap::AttributeChanged(nsIDocument *aDocument, Element* aElement,
-                                 int32_t aNameSpaceID, nsAtom* aAttribute,
+nsDOMStringMap::AttributeChanged(Element* aElement,
+                                 int32_t aNameSpaceID,
+                                 nsAtom* aAttribute,
                                  int32_t aModType,
                                  const nsAttrValue* aOldValue)
 {
   if ((aModType == MutationEventBinding::ADDITION ||
        aModType == MutationEventBinding::REMOVAL) &&
       aNameSpaceID == kNameSpaceID_None &&
       StringBeginsWith(nsDependentAtomString(aAttribute),
                        NS_LITERAL_STRING("data-"))) {
--- a/dom/media/platforms/omx/OmxDataDecoder.cpp
+++ b/dom/media/platforms/omx/OmxDataDecoder.cpp
@@ -615,16 +615,19 @@ OmxDataDecoder::FillCodecConfigDataToOmx
   } else if (mTrackInfo->IsVideo()) {
     csc = mTrackInfo->GetAsVideoInfo()->mExtraData;
   }
 
   MOZ_RELEASE_ASSERT(csc);
 
   // Some codecs like h264, its codec specific data is at the first packet, not in container.
   if (csc->Length()) {
+    // Buffer size should large enough for raw data.
+    MOZ_RELEASE_ASSERT(inbuf->mBuffer->nAllocLen >= csc->Length());
+
     memcpy(inbuf->mBuffer->pBuffer,
            csc->Elements(),
            csc->Length());
     inbuf->mBuffer->nFilledLen = csc->Length();
     inbuf->mBuffer->nOffset = 0;
     inbuf->mBuffer->nFlags = (OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG);
 
     LOG("Feed codec configure data to OMX component");
--- a/dom/media/test/AutoplayTestUtils.js
+++ b/dom/media/test/AutoplayTestUtils.js
@@ -1,14 +1,12 @@
-function playAndPostResult(test_case, parent_window) {
-  log("runTest " + test_case.name);
-
+function playAndPostResult(muted, parent_window) {
   let element = document.createElement("video");
   element.preload = "auto";
-  element.muted = test_case.muted;
+  element.muted = muted;
   element.src = "short.mp4";
   element.id = "video";
   document.body.appendChild(element);
 
   element.play().then(
       () => {
         parent_window.postMessage({played: true}, "*");
       },
--- a/dom/media/test/file_autoplay_policy_activation_frame.html
+++ b/dom/media/test/file_autoplay_policy_activation_frame.html
@@ -1,21 +1,28 @@
 <!DOCTYPE HTML>
 <html>
   <head>
     <title>Autoplay policy frame</title>
     <script type="text/javascript" src="AutoplayTestUtils.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <style>
       video {
         width: 50%;
         height: 50%;
       }
     </style>
   </head>
   <body>
     <script>
-      nextWindowMessage().then(
+      window.addEventListener("message",
         (event) => {
-          playAndPostResult(event.data, event.source);
-        });
+          if (event.data == "click") {
+            synthesizeMouseAtCenter(document.body, {});
+          } else if (event.data == "play-audible") {
+            playAndPostResult(false, event.source);
+          } else if (event.data == "play-muted") {
+            playAndPostResult(true, event.source);
+          }
+        }, false);
     </script>
   </body>
 </html>
--- a/dom/media/test/file_autoplay_policy_activation_window.html
+++ b/dom/media/test/file_autoplay_policy_activation_window.html
@@ -14,35 +14,36 @@
     <script type="text/javascript" src="AutoplayTestUtils.js"></script>
   </head>
   <body>
     <pre id="test">
       <script>
 
         function testAutoplayInWindow(test_case, parent_window) {
           log("testAutoplayInWindow: " + test_case.name);
-          playAndPostResult(test_case, parent_window);
+          playAndPostResult(test_case.muted, parent_window);
         }
 
         async function testAutoplayInChildFrame(test_case, parent_window) {
           log("testAutoplayInChildFrame: " + test_case.name);
           // Create a child iframe...
           var frame = document.createElement("iframe");
           var origin = test_case.same_origin_child
             ? "http://mochi.test:8888" : "http://example.org";
           frame.src = origin + "/tests/dom/media/test/file_autoplay_policy_activation_frame.html";
           // Wait for it to load...
           document.body.appendChild(frame);
           await once(frame, "load");
           // Click the iframe to activate if appropriate.
           if (test_case.activated_child) {
-            synthesizeMouseAtCenter(frame, {});
+            frame.contentWindow.postMessage("click", "*");
           }
           // Ask the child iframe to try to play video.
-          frame.contentWindow.postMessage(test_case, "*");
+          let play_message = test_case.muted ? "play-muted" : "play-audible";
+          frame.contentWindow.postMessage(play_message, "*");
           // Wait for the iframe to tell us whether it could play video.
           let result = await nextWindowMessage();
           // Report whether the iframe could play to the parent.
           parent_window.postMessage(result.data, "*");
         }
 
         nextWindowMessage().then(
           (event) => {
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -683,18 +683,17 @@ skip-if = android_version == '15' # andr
 [test_audioDocumentTitle.html]
 skip-if = true # bug 475110 - disabled since we don't play Wave files standalone
 [test_autoplay.html]
 [test_autoplay_contentEditable.html]
 skip-if = android_version == '15' || android_version == '17' || android_version == '22' # android(bug 1232305, bug 1232318, bug 1372457)
 [test_autoplay_policy.html]
 skip-if = android_version == '23' # bug 1424903
 [test_autoplay_policy_activation.html]
-#skip-if = android_version == '23' # bug 1424903
-skip-if = true # bug 1441424 - temporarily disabled to land bug 1193394
+skip-if = android_version == '23' # bug 1424903
 [test_buffered.html]
 skip-if = android_version == '15' || android_version == '22' # bug 1308388, android(bug 1232305)
 [test_bug448534.html]
 [test_bug463162.xhtml]
 [test_bug465498.html]
 skip-if = toolkit == 'android' # android(bug 1232305)
 [test_bug495145.html]
 skip-if = (os == 'mac' && os_version == '10.6') || (toolkit == 'android')  # bug 1311229, android(bug 1232305)
--- a/dom/media/test/test_autoplay_policy_activation.html
+++ b/dom/media/test/test_autoplay_policy_activation.html
@@ -71,25 +71,24 @@
             name: "inaudible playback in unactivated cross-origin iframe in unactivated parent allowed",
             muted: true,
             same_origin_child: false,
             activated_child: false,
             activated_parent: false,
             should_play: true,
           },
 
-          // TODO: This case fails, Firefox's behaviour needs to be fixed.
-          // {
-          //   name: "audible playback in unactivated cross-origin iframe in activated parent blocked",
-          //   muted: false,
-          //   same_origin_child: false,
-          //   activated_child: false,
-          //   activated_parent: true,
-          //   should_play: false,
-          // },
+          {
+            name: "audible playback in unactivated cross-origin iframe in activated parent blocked",
+            muted: false,
+            same_origin_child: false,
+            activated_child: false,
+            activated_parent: true,
+            should_play: false,
+          },
 
           {
             name: "audible playback in unactivated cross-origin iframe in unactivated parent blocked",
             muted: false,
             same_origin_child: false,
             activated_child: false,
             activated_parent: false,
             should_play: false,
--- a/dom/script/ScriptElement.cpp
+++ b/dom/script/ScriptElement.cpp
@@ -73,46 +73,40 @@ ScriptElement::ScriptEvaluated(nsresult 
 
     EventDispatcher::Dispatch(cont, presContext, &event, nullptr, &status);
   }
 
   return rv;
 }
 
 void
-ScriptElement::CharacterDataChanged(nsIDocument* aDocument,
-                                    nsIContent* aContent,
+ScriptElement::CharacterDataChanged(nsIContent* aContent,
                                     const CharacterDataChangeInfo&)
 {
   MaybeProcessScript();
 }
 
 void
-ScriptElement::AttributeChanged(nsIDocument* aDocument,
-                                Element* aElement,
+ScriptElement::AttributeChanged(Element* aElement,
                                 int32_t aNameSpaceID,
                                 nsAtom* aAttribute,
                                 int32_t aModType,
                                 const nsAttrValue* aOldValue)
 {
   MaybeProcessScript();
 }
 
 void
-ScriptElement::ContentAppended(nsIDocument* aDocument,
-                               nsIContent* aContainer,
-                               nsIContent* aFirstNewContent)
+ScriptElement::ContentAppended(nsIContent* aFirstNewContent)
 {
   MaybeProcessScript();
 }
 
 void
-ScriptElement::ContentInserted(nsIDocument* aDocument,
-                               nsIContent* aContainer,
-                               nsIContent* aChild)
+ScriptElement::ContentInserted(nsIContent* aChild)
 {
   MaybeProcessScript();
 }
 
 bool
 ScriptElement::MaybeProcessScript()
 {
   nsCOMPtr<nsIContent> cont =
--- a/dom/svg/SVGMPathElement.cpp
+++ b/dom/svg/SVGMPathElement.cpp
@@ -183,18 +183,17 @@ SVGMPathElement::GetStringInfo()
   return StringAttributesInfo(mStringAttributes, sStringInfo,
                               ArrayLength(sStringInfo));
 }
 
 //----------------------------------------------------------------------
 // nsIMutationObserver methods
 
 void
-SVGMPathElement::AttributeChanged(nsIDocument* aDocument,
-                                  Element* aElement,
+SVGMPathElement::AttributeChanged(Element* aElement,
                                   int32_t aNameSpaceID,
                                   nsAtom* aAttribute,
                                   int32_t aModType,
                                   const nsAttrValue* aOldValue)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::d) {
       NotifyParentOfMpathChange(GetParent());
--- a/dom/svg/SVGStyleElement.cpp
+++ b/dom/svg/SVGStyleElement.cpp
@@ -128,43 +128,36 @@ SVGStyleElement::ParseAttribute(int32_t 
   return SVGStyleElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                              aMaybeScriptedPrincipal, aResult);
 }
 
 //----------------------------------------------------------------------
 // nsIMutationObserver methods
 
 void
-SVGStyleElement::CharacterDataChanged(nsIDocument* aDocument,
-                                      nsIContent* aContent,
+SVGStyleElement::CharacterDataChanged(nsIContent* aContent,
                                       const CharacterDataChangeInfo&)
 {
   ContentChanged(aContent);
 }
 
 void
-SVGStyleElement::ContentAppended(nsIDocument* aDocument,
-                                 nsIContent* aContainer,
-                                 nsIContent* aFirstNewContent)
+SVGStyleElement::ContentAppended(nsIContent* aFirstNewContent)
 {
-  ContentChanged(aContainer);
+  ContentChanged(aFirstNewContent->GetParent());
 }
 
 void
-SVGStyleElement::ContentInserted(nsIDocument* aDocument,
-                                 nsIContent* aContainer,
-                                 nsIContent* aChild)
+SVGStyleElement::ContentInserted(nsIContent* aChild)
 {
   ContentChanged(aChild);
 }
 
 void
-SVGStyleElement::ContentRemoved(nsIDocument* aDocument,
-                                nsIContent* aContainer,
-                                nsIContent* aChild,
+SVGStyleElement::ContentRemoved(nsIContent* aChild,
                                 nsIContent* aPreviousSibling)
 {
   ContentChanged(aChild);
 }
 
 void
 SVGStyleElement::ContentChanged(nsIContent* aContent)
 {
--- a/dom/svg/SVGTitleElement.cpp
+++ b/dom/svg/SVGTitleElement.cpp
@@ -34,44 +34,37 @@ SVGTitleElement::SVGTitleElement(already
   AddMutationObserver(this);
 }
 
 SVGTitleElement::~SVGTitleElement()
 {
 }
 
 void
-SVGTitleElement::CharacterDataChanged(nsIDocument* aDocument,
-                                      nsIContent* aContent,
+SVGTitleElement::CharacterDataChanged(nsIContent* aContent,
                                       const CharacterDataChangeInfo&)
 {
   SendTitleChangeEvent(false);
 }
 
 void
-SVGTitleElement::ContentAppended(nsIDocument *aDocument,
-                                 nsIContent *aContainer,
-                                 nsIContent *aFirstNewContent)
+SVGTitleElement::ContentAppended(nsIContent* aFirstNewContent)
 {
   SendTitleChangeEvent(false);
 }
 
 void
-SVGTitleElement::ContentInserted(nsIDocument *aDocument,
-                                 nsIContent *aContainer,
-                                 nsIContent *aChild)
+SVGTitleElement::ContentInserted(nsIContent* aChild)
 {
   SendTitleChangeEvent(false);
 }
 
 void
-SVGTitleElement::ContentRemoved(nsIDocument *aDocument,
-                                nsIContent *aContainer,
-                                nsIContent *aChild,
-                                nsIContent *aPreviousSibling)
+SVGTitleElement::ContentRemoved(nsIContent* aChild,
+                                nsIContent* aPreviousSibling)
 {
   SendTitleChangeEvent(false);
 }
 
 nsresult
 SVGTitleElement::BindToTree(nsIDocument *aDocument,
                              nsIContent *aParent,
                              nsIContent *aBindingParent,
--- a/dom/svg/SVGUseElement.cpp
+++ b/dom/svg/SVGUseElement.cpp
@@ -142,63 +142,58 @@ SVGUseElement::Height()
 {
   return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
 }
 
 //----------------------------------------------------------------------
 // nsIMutationObserver methods
 
 void
-SVGUseElement::CharacterDataChanged(nsIDocument* aDocument,
-                                    nsIContent* aContent,
+SVGUseElement::CharacterDataChanged(nsIContent* aContent,
                                     const CharacterDataChangeInfo&)
 {
   if (nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
     TriggerReclone();
   }
 }
 
 void
-SVGUseElement::AttributeChanged(nsIDocument* aDocument,
-                                Element* aElement,
+SVGUseElement::AttributeChanged(Element* aElement,
                                 int32_t aNameSpaceID,
                                 nsAtom* aAttribute,
                                 int32_t aModType,
                                 const nsAttrValue* aOldValue)
 {
   if (nsContentUtils::IsInSameAnonymousTree(this, aElement)) {
     TriggerReclone();
   }
 }
 
 void
-SVGUseElement::ContentAppended(nsIDocument *aDocument,
-                               nsIContent *aContainer,
-                               nsIContent *aFirstNewContent)
+SVGUseElement::ContentAppended(nsIContent* aFirstNewContent)
 {
-  if (nsContentUtils::IsInSameAnonymousTree(this, aContainer)) {
+  // FIXME(emilio, bug 1442336): Why does this check the parent but
+  // ContentInserted the child?
+  if (nsContentUtils::IsInSameAnonymousTree(this, aFirstNewContent->GetParent())) {
     TriggerReclone();
   }
 }
 
 void
-SVGUseElement::ContentInserted(nsIDocument *aDocument,
-                               nsIContent *aContainer,
-                               nsIContent *aChild)
+SVGUseElement::ContentInserted(nsIContent* aChild)
 {
+  // FIXME(emilio, bug 1442336): Why does this check the child but
+  // ContentAppended the parent?
   if (nsContentUtils::IsInSameAnonymousTree(this, aChild)) {
     TriggerReclone();
   }
 }
 
 void
-SVGUseElement::ContentRemoved(nsIDocument *aDocument,
-                              nsIContent *aContainer,
-                              nsIContent *aChild,
-                              nsIContent *aPreviousSibling)
+SVGUseElement::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
 {
   if (nsContentUtils::IsInSameAnonymousTree(this, aChild)) {
     TriggerReclone();
   }
 }
 
 void
 SVGUseElement::NodeWillBeDestroyed(const nsINode *aNode)
--- a/dom/webidl/ChromeUtils.webidl
+++ b/dom/webidl/ChromeUtils.webidl
@@ -289,16 +289,32 @@ partial namespace ChromeUtils {
    * @param target The target object on which to define the property.
    * @param id The name of the property to define, and of the symbol to
    *           import.
    * @param resourceURI The resource URI of the module, as passed to
    *                    ChromeUtils.import.
    */
   [Throws]
   void defineModuleGetter(object target, DOMString id, DOMString resourceURI);
+
+  /**
+   * Returns the scripted location of the first ancestor stack frame with a
+   * principal which is subsumed by the given principal. If no such frame
+   * exists on the call stack, returns null.
+   */
+  object? getCallerLocation(Principal principal);
+
+  /**
+   * Creates a JS Error object with the given message and stack.
+   *
+   * If a stack object is provided, the error object is created in the global
+   * that it belongs to.
+   */
+  [Throws]
+  object createError(DOMString message, optional object? stack = null);
 };
 
 /**
  * Used by principals and the script security manager to represent origin
  * attributes. The first dictionary is designed to contain the full set of
  * OriginAttributes, the second is used for pattern-matching (i.e. does this
  * OriginAttributesDictionary match the non-empty attributes in this pattern).
  *
--- a/dom/webidl/WorkerGlobalScope.webidl
+++ b/dom/webidl/WorkerGlobalScope.webidl
@@ -38,9 +38,12 @@ WorkerGlobalScope implements WindowOrWor
 // Mozilla extensions
 partial interface WorkerGlobalScope {
 
   void dump(optional DOMString str);
 
   // XXXbz no spec for this yet, because the webperf WG is a bit dysfunctional
   [Constant, Cached]
   readonly attribute Performance performance;
+
+  [Func="WorkerGlobalScope::IsInAutomation", Throws]
+  object getJSTestingFunctions();
 };
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2612,31 +2612,34 @@ WorkerPrivate::WorkerPrivate(WorkerPriva
   , mWorkerScriptExecutedSuccessfully(false)
   , mFetchHandlerWasAdded(false)
   , mOnLine(false)
   , mMainThreadObjectsForgotten(false)
   , mIsChromeWorker(aIsChromeWorker)
   , mParentFrozen(false)
   , mIsSecureContext(false)
   , mDebuggerRegistered(false)
+  , mIsInAutomation(false)
 {
   MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
   mLoadInfo.StealFrom(aLoadInfo);
 
   if (aParent) {
     aParent->AssertIsOnWorkerThread();
 
     // Note that this copies our parent's secure context state into mJSSettings.
     aParent->CopyJSSettings(mJSSettings);
 
     // And manually set our mIsSecureContext, though it's not really relevant to
     // dedicated workers...
     mIsSecureContext = aParent->IsSecureContext();
     MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext);
 
+    mIsInAutomation = aParent->IsInAutomation();
+
     MOZ_ASSERT(IsDedicatedWorker());
 
     if (aParent->mParentFrozen) {
       Freeze(nullptr);
     }
 
     mOnLine = aParent->OnLine();
   }
@@ -2660,16 +2663,18 @@ WorkerPrivate::WorkerPrivate(WorkerPriva
 
     if (mIsSecureContext) {
       mJSSettings.chrome.compartmentOptions
                  .creationOptions().setSecureContext(true);
       mJSSettings.content.compartmentOptions
                  .creationOptions().setSecureContext(true);
     }
 
+    mIsInAutomation = xpc::IsInAutomation();
+
     // Our parent can get suspended after it initiates the async creation
     // of a new worker thread.  In this case suspend the new worker as well.
     if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsSuspended()) {
       ParentWindowPaused();
     }
 
     if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsFrozen()) {
       Freeze(mLoadInfo.mWindow);
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -628,16 +628,22 @@ public:
   // compartment of the worker global.  The only reason we don't
   // AssertIsOnParentThread() here is so we can assert that this value matches
   // the one on the compartment, which has to be done from the worker thread.
   bool IsSecureContext() const
   {
     return mIsSecureContext;
   }
 
+  // Check whether we're running in automation.
+  bool IsInAutomation() const
+  {
+    return mIsInAutomation;
+  }
+
   TimeStamp CreationTimeStamp() const
   {
     return mCreationTimeStamp;
   }
 
   DOMHighResTimeStamp CreationTime() const
   {
     return mCreationTimeHighRes;
@@ -1474,16 +1480,20 @@ private:
   // of state (loadinfo, worker type, parent).
   //
   // It's a bit unfortunate that we have to have an out-of-band boolean for
   // this, but we need access to this state from the parent thread, and we can't
   // use our global object's secure state there.
   bool mIsSecureContext;
 
   bool mDebuggerRegistered;
+
+  // mIsInAutomation is true when we're running in test automation.
+  // We expose some extra testing functions in that case.
+  bool mIsInAutomation;
 };
 
 class AutoSyncLoopHolder
 {
   WorkerPrivate* mWorkerPrivate;
   nsCOMPtr<nsIEventTarget> mTarget;
   uint32_t mIndex;
 
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WorkerScope.h"
 
 #include "jsapi.h"
+#include "jsfriendapi.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Clients.h"
 #include "mozilla/dom/ClientState.h"
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/Fetch.h"
@@ -395,16 +396,36 @@ WorkerGlobalScope::GetPerformance()
 
   if (!mPerformance) {
     mPerformance = Performance::CreateForWorker(mWorkerPrivate);
   }
 
   return mPerformance;
 }
 
+bool
+WorkerGlobalScope::IsInAutomation(JSContext* aCx, JSObject* /* unused */)
+{
+  return GetWorkerPrivateFromContext(aCx)->IsInAutomation();
+}
+
+void
+WorkerGlobalScope::GetJSTestingFunctions(JSContext* aCx,
+                                         JS::MutableHandle<JSObject*> aFunctions,
+                                         ErrorResult& aRv)
+{
+  JSObject* obj = js::GetTestingFunctions(aCx);
+  if (!obj) {
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
+
+  aFunctions.set(obj);
+}
+
 already_AddRefed<Promise>
 WorkerGlobalScope::Fetch(const RequestOrUSVString& aInput,
                          const RequestInit& aInit,
                          CallerType aCallerType, ErrorResult& aRv)
 {
   return FetchRequest(this, aInput, aInit, aCallerType, aRv);
 }
 
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -156,16 +156,21 @@ public:
 
   Performance* GetPerformance();
 
   Performance* GetPerformanceIfExists() const
   {
     return mPerformance;
   }
 
+  static bool IsInAutomation(JSContext* aCx, JSObject* /* unused */);
+  void GetJSTestingFunctions(JSContext* aCx,
+                             JS::MutableHandle<JSObject*> aFunctions,
+                             ErrorResult& aRv);
+
   already_AddRefed<Promise>
   Fetch(const RequestOrUSVString& aInput, const RequestInit& aInit,
         CallerType aCallerType, ErrorResult& aRv);
 
   already_AddRefed<IDBFactory>
   GetIndexedDB(ErrorResult& aErrorResult);
 
   already_AddRefed<cache::CacheStorage>
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -71,21 +71,17 @@ var ecmaGlobals =
     {name: "TypeError", insecureContext: true},
     {name: "Uint16Array", insecureContext: true},
     {name: "Uint32Array", insecureContext: true},
     {name: "Uint8Array", insecureContext: true},
     {name: "Uint8ClampedArray", insecureContext: true},
     {name: "URIError", insecureContext: true},
     {name: "WeakMap", insecureContext: true},
     {name: "WeakSet", insecureContext: true},
-    // WebAssembly is not supported on some hardware configurations,
-    // but we have no way to check that from here.  Just give up for
-    // now and don't check for it at all.  Do NOT add any other uses
-    // of "optional"!
-    {name: "WebAssembly", insecureContext: true, optional: true},
+    {name: "WebAssembly", insecureContext: true, disabled: !getJSTestingFunctions().wasmIsSupportedByHardware()},
   ];
 // IMPORTANT: Do not change the list above without review from
 //            a JavaScript Engine peer!
 
 // IMPORTANT: Do not change the list below without review from a DOM peer!
 var interfaceNamesInGlobalScope =
   [
 // IMPORTANT: Do not change this list without review from a DOM peer!
@@ -286,18 +282,16 @@ function createInterfaceMap(version, use
 	    // The insecureContext test is very purposefully converting
 	    // entry.insecureContext to boolean, so undefined will convert to
 	    // false.  That way entries without an insecureContext annotation
 	    // will get treated as "insecureContext: false", which means exposed
 	    // only in secure contexts.
             (isInsecureContext && !Boolean(entry.insecureContext)) ||
             entry.disabled) {
           interfaceMap[entry.name] = false;
-        } else if (entry.optional) {
-          interfaceMap[entry.name] = "optional";
         } else {
           interfaceMap[entry.name] = true;
         }
       }
     }
   }
 
   addInterfaces(ecmaGlobals);
@@ -308,31 +302,27 @@ function createInterfaceMap(version, use
 
 function runTest(version, userAgent) {
   var interfaceMap = createInterfaceMap(version, userAgent);
   for (var name of Object.getOwnPropertyNames(self)) {
     // An interface name should start with an upper case character.
     if (!/^[A-Z]/.test(name)) {
       continue;
     }
-    ok(interfaceMap[name] === "optional" || interfaceMap[name],
+    ok(interfaceMap[name],
        "If this is failing: DANGER, are you sure you want to expose the new interface " + name +
        " to all webpages as a property on the worker? Do not make a change to this file without a " +
        " review from a DOM peer for that specific change!!! (or a JS peer for changes to ecmaGlobals)");
     delete interfaceMap[name];
   }
   for (var name of Object.keys(interfaceMap)) {
-    if (interfaceMap[name] === "optional") {
+    ok(name in self === interfaceMap[name],
+       name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope");
+    if (!interfaceMap[name]) {
       delete interfaceMap[name];
-    } else {
-      ok(name in self === interfaceMap[name],
-         name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope");
-      if (!interfaceMap[name]) {
-        delete interfaceMap[name];
-      }
     }
   }
   is(Object.keys(interfaceMap).length, 0,
      "The following interface(s) are not enumerated: " + Object.keys(interfaceMap).join(", "));
 }
 
 workerTestGetVersion(function(version) {
   workerTestGetUserAgent(function(userAgent) {
--- a/dom/xbl/nsBindingManager.cpp
+++ b/dom/xbl/nsBindingManager.cpp
@@ -823,23 +823,22 @@ InsertAppendedContent(XBLChildrenElement
   for (nsIContent* currentChild = aFirstNewContent;
        currentChild;
        currentChild = currentChild->GetNextSibling()) {
     aPoint->InsertInsertedChildAt(currentChild, insertionIndex++);
   }
 }
 
 void
-nsBindingManager::ContentAppended(nsIDocument* aDocument,
-                                  nsIContent* aContainer,
-                                  nsIContent* aFirstNewContent)
+nsBindingManager::ContentAppended(nsIContent* aFirstNewContent)
 {
   // Try to find insertion points for all the new kids.
   XBLChildrenElement* point = nullptr;
-  nsIContent* parent = aContainer;
+  nsIContent* container = aFirstNewContent->GetParent();
+  nsIContent* parent = container;
 
   // Handle appending of default content.
   if (parent && parent->IsActiveChildrenElement()) {
     XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent);
     if (childrenEl->HasInsertedChildren()) {
       // Appending default content that isn't being used. Ignore.
       return;
     }
@@ -858,17 +857,17 @@ nsBindingManager::ContentAppended(nsIDoc
     if (binding->HasFilteredInsertionPoints()) {
       // There are filtered insertion points involved, handle each child
       // separately.
       // We could optimize this in the case when we've nested a few levels
       // deep already, without hitting bindings that have filtered insertion
       // points.
       for (nsIContent* currentChild = aFirstNewContent; currentChild;
            currentChild = currentChild->GetNextSibling()) {
-        HandleChildInsertion(aContainer, currentChild, true);
+        HandleChildInsertion(container, currentChild, true);
       }
 
       return;
     }
 
     point = binding->GetDefaultInsertionPoint();
     if (!point) {
       break;
@@ -891,33 +890,29 @@ nsBindingManager::ContentAppended(nsIDoc
     if (newParent == parent) {
       break;
     }
     parent = newParent;
   } while (parent);
 }
 
 void
-nsBindingManager::ContentInserted(nsIDocument* aDocument,
-                                  nsIContent* aContainer,
-                                  nsIContent* aChild)
+nsBindingManager::ContentInserted(nsIContent* aChild)
 {
-  HandleChildInsertion(aContainer, aChild, false);
+  HandleChildInsertion(aChild->GetParent(), aChild, false);
 }
 
 void
-nsBindingManager::ContentRemoved(nsIDocument* aDocument,
-                                 nsIContent* aContainer,
-                                 nsIContent* aChild,
+nsBindingManager::ContentRemoved(nsIContent* aChild,
                                  nsIContent* aPreviousSibling)
 {
   aChild->SetXBLInsertionPoint(nullptr);
 
   XBLChildrenElement* point = nullptr;
-  nsIContent* parent = aContainer;
+  nsIContent* parent = aChild->GetParent();
 
   // Handle appending of default content.
   if (parent && parent->IsActiveChildrenElement()) {
     XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent);
     if (childrenEl->HasInsertedChildren()) {
       // Removing default content that isn't being used. Ignore.
       return;
     }
--- a/dom/xml/nsXMLPrettyPrinter.cpp
+++ b/dom/xml/nsXMLPrettyPrinter.cpp
@@ -216,49 +216,42 @@ nsXMLPrettyPrinter::Unhook()
     }
 
     mDocument = nullptr;
 
     NS_RELEASE_THIS();
 }
 
 void
-nsXMLPrettyPrinter::AttributeChanged(nsIDocument* aDocument,
-                                     Element* aElement,
+nsXMLPrettyPrinter::AttributeChanged(Element* aElement,
                                      int32_t aNameSpaceID,
                                      nsAtom* aAttribute,
                                      int32_t aModType,
                                      const nsAttrValue* aOldValue)
 {
     MaybeUnhook(aElement);
 }
 
 void
-nsXMLPrettyPrinter::ContentAppended(nsIDocument* aDocument,
-                                    nsIContent* aContainer,
-                                    nsIContent* aFirstNewContent)
+nsXMLPrettyPrinter::ContentAppended(nsIContent* aFirstNewContent)
 {
-    MaybeUnhook(aContainer);
+    MaybeUnhook(aFirstNewContent->GetParent());
 }
 
 void
-nsXMLPrettyPrinter::ContentInserted(nsIDocument* aDocument,
-                                    nsIContent* aContainer,
-                                    nsIContent* aChild)
+nsXMLPrettyPrinter::ContentInserted(nsIContent* aChild)
 {
-    MaybeUnhook(aContainer);
+    MaybeUnhook(aChild->GetParent());
 }
 
 void
-nsXMLPrettyPrinter::ContentRemoved(nsIDocument* aDocument,
-                                   nsIContent* aContainer,
-                                   nsIContent* aChild,
+nsXMLPrettyPrinter::ContentRemoved(nsIContent* aChild,
                                    nsIContent* aPreviousSibling)
 {
-    MaybeUnhook(aContainer);
+    MaybeUnhook(aChild->GetParent());
 }
 
 void
 nsXMLPrettyPrinter::NodeWillBeDestroyed(const nsINode* aNode)
 {
     mDocument = nullptr;
     NS_RELEASE_THIS();
 }
--- a/dom/xslt/xpath/XPathResult.cpp
+++ b/dom/xslt/xpath/XPathResult.cpp
@@ -117,57 +117,48 @@ XPathResult::NodeWillBeDestroyed(const n
 {
     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
     // Set to null to avoid unregistring unnecessarily
     mDocument = nullptr;
     Invalidate(aNode->IsContent() ? aNode->AsContent() : nullptr);
 }
 
 void
-XPathResult::CharacterDataChanged(nsIDocument* aDocument,
-                                  nsIContent* aContent,
+XPathResult::CharacterDataChanged(nsIContent* aContent,
                                   const CharacterDataChangeInfo&)
 {
     Invalidate(aContent);
 }
 
 void
-XPathResult::AttributeChanged(nsIDocument* aDocument,
-                              Element* aElement,
+XPathResult::AttributeChanged(Element* aElement,
                               int32_t aNameSpaceID,
                               nsAtom* aAttribute,
                               int32_t aModType,
                               const nsAttrValue* aOldValue)
 {
     Invalidate(aElement);
 }
 
 void
-XPathResult::ContentAppended(nsIDocument* aDocument,
-                             nsIContent* aContainer,
-                             nsIContent* aFirstNewContent)
+XPathResult::ContentAppended(nsIContent* aFirstNewContent)
 {
-    Invalidate(aContainer);
+    Invalidate(aFirstNewContent->GetParent());
 }
 
 void
-XPathResult::ContentInserted(nsIDocument* aDocument,
-                             nsIContent* aContainer,
-                             nsIContent* aChild)
+XPathResult::ContentInserted(nsIContent* aChild)
 {
-    Invalidate(aContainer);
+    Invalidate(aChild->GetParent());
 }
 
 void
-XPathResult::ContentRemoved(nsIDocument* aDocument,
-                            nsIContent* aContainer,
-                            nsIContent* aChild,
-                            nsIContent* aPreviousSibling)
+XPathResult::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
 {
-    Invalidate(aContainer);
+    Invalidate(aChild->GetParent());
 }
 
 nsresult
 XPathResult::SetExprResult(txAExprResult* aExprResult, uint16_t aResultType,
                            nsINode* aContextNode)
 {
     MOZ_ASSERT(aExprResult);
 
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -1230,54 +1230,46 @@ txMozillaXSLTProcessor::NodeWillBeDestro
     }
 
     mCompileResult = ensureStylesheet();
     mStylesheetDocument = nullptr;
     mEmbeddedStylesheetRoot = nullptr;
 }
 
 void
-txMozillaXSLTProcessor::CharacterDataChanged(nsIDocument* aDocument,
-                                             nsIContent* aContent,
+txMozillaXSLTProcessor::CharacterDataChanged(nsIContent* aContent,
                                              const CharacterDataChangeInfo&)
 {
     mStylesheet = nullptr;
 }
 
 void
-txMozillaXSLTProcessor::AttributeChanged(nsIDocument* aDocument,
-                                         Element* aElement,
+txMozillaXSLTProcessor::AttributeChanged(Element* aElement,
                                          int32_t aNameSpaceID,
                                          nsAtom* aAttribute,
                                          int32_t aModType,
                                          const nsAttrValue* aOldValue)
 {
     mStylesheet = nullptr;
 }
 
 void
-txMozillaXSLTProcessor::ContentAppended(nsIDocument* aDocument,
-                                        nsIContent* aContainer,
-                                        nsIContent* aFirstNewContent)
+txMozillaXSLTProcessor::ContentAppended(nsIContent* aFirstNewContent)
 {
     mStylesheet = nullptr;
 }
 
 void
-txMozillaXSLTProcessor::ContentInserted(nsIDocument* aDocument,
-                                        nsIContent* aContainer,
-                                        nsIContent* aChild)
+txMozillaXSLTProcessor::ContentInserted(nsIContent* aChild)
 {
     mStylesheet = nullptr;
 }
 
 void
-txMozillaXSLTProcessor::ContentRemoved(nsIDocument* aDocument,
-                                       nsIContent* aContainer,
-                                       nsIContent* aChild,
+txMozillaXSLTProcessor::ContentRemoved(nsIContent* aChild,
                                        nsIContent* aPreviousSibling)
 {
     mStylesheet = nullptr;
 }
 
 /* virtual */ JSObject*
 txMozillaXSLTProcessor::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -850,22 +850,21 @@ ShouldPersistAttribute(Element* aElement
             aAttribute == nsGkAtoms::sizemode) {
             return false;
         }
     }
     return true;
 }
 
 void
-XULDocument::AttributeChanged(nsIDocument* aDocument,
-                              Element* aElement, int32_t aNameSpaceID,
+XULDocument::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
                               nsAtom* aAttribute, int32_t aModType,
                               const nsAttrValue* aOldValue)
 {
-    NS_ASSERTION(aDocument == this, "unexpected doc");
+    NS_ASSERTION(aElement->OwnerDoc() == this, "unexpected doc");
 
     // Might not need this, but be safe for now.
     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
 
     // Synchronize broadcast listeners
     if (mBroadcasterMap &&
         CanBroadcast(aNameSpaceID, aAttribute)) {
         auto entry = static_cast<BroadcasterMapEntry*>
@@ -938,53 +937,46 @@ XULDocument::AttributeChanged(nsIDocumen
           &XULDocument::DoPersist,
           aElement,
           kNameSpaceID_None,
           aAttribute));
     }
 }
 
 void
-XULDocument::ContentAppended(nsIDocument* aDocument,
-                             nsIContent* aContainer,
-                             nsIContent* aFirstNewContent)
+XULDocument::ContentAppended(nsIContent* aFirstNewContent)
 {
-    NS_ASSERTION(aDocument == this, "unexpected doc");
+    NS_ASSERTION(aFirstNewContent->OwnerDoc() == this, "unexpected doc");
 
     // Might not need this, but be safe for now.
     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
 
     // Update our element map
     nsresult rv = NS_OK;
     for (nsIContent* cur = aFirstNewContent; cur && NS_SUCCEEDED(rv);
          cur = cur->GetNextSibling()) {
         rv = AddSubtreeToDocument(cur);
     }
 }
 
 void
-XULDocument::ContentInserted(nsIDocument* aDocument,
-                             nsIContent* aContainer,
-                             nsIContent* aChild)
+XULDocument::ContentInserted(nsIContent* aChild)
 {
-    NS_ASSERTION(aDocument == this, "unexpected doc");
+    NS_ASSERTION(aChild->OwnerDoc() == this, "unexpected doc");
 
     // Might not need this, but be safe for now.
     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
 
     AddSubtreeToDocument(aChild);
 }
 
 void
-XULDocument::ContentRemoved(nsIDocument* aDocument,
-                            nsIContent* aContainer,
-                            nsIContent* aChild,
-                            nsIContent* aPreviousSibling)
+XULDocument::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
 {
-    NS_ASSERTION(aDocument == this, "unexpected doc");
+    NS_ASSERTION(aChild->OwnerDoc() == this, "unexpected doc");
 
     // Might not need this, but be safe for now.
     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
 
     RemoveSubtreeFromDocument(aChild);
 }
 
 nsresult
--- a/editor/libeditor/HTMLAnonymousNodeEditor.cpp
+++ b/editor/libeditor/HTMLAnonymousNodeEditor.cpp
@@ -309,18 +309,17 @@ HTMLEditor::DeleteRefToAnonymousNode(Man
       aShell->BeginUpdate(document, UPDATE_CONTENT_MODEL);
     }
 
     MOZ_ASSERT(aContent->IsRootOfAnonymousSubtree());
     MOZ_ASSERT(!aContent->GetPreviousSibling(), "NAC has no siblings");
 
     // FIXME(emilio): This is the only caller to PresShell::ContentRemoved that
     // passes NAC into it. This is not great!
-    aShell->ContentRemoved(
-      aContent->GetComposedDoc(), parentContent, aContent, nullptr);
+    aShell->ContentRemoved(aContent, nullptr);
 
     if (document) {
       aShell->EndUpdate(document, UPDATE_CONTENT_MODEL);
     }
   }
 
   // The ManualNACPtr destructor will invoke UnbindFromTree.
 }
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -3131,35 +3131,29 @@ HTMLEditor::InsertTextImpl(nsIDocument& 
     return NS_ERROR_FAILURE;
   }
 
   return EditorBase::InsertTextImpl(aDocument, aStringToInsert, aPointToInsert,
                                     aPointAfterInsertedString);
 }
 
 void
-HTMLEditor::ContentAppended(nsIDocument* aDocument,
-                            nsIContent* aContainer,
-                            nsIContent* aFirstNewContent)
+HTMLEditor::ContentAppended(nsIContent* aFirstNewContent)
 {
-  DoContentInserted(aDocument, aContainer, aFirstNewContent, eAppended);
+  DoContentInserted(aFirstNewContent, eAppended);
 }
 
 void
-HTMLEditor::ContentInserted(nsIDocument* aDocument,
-                            nsIContent* aContainer,
-                            nsIContent* aChild)
+HTMLEditor::ContentInserted(nsIContent* aChild)
 {
-  DoContentInserted(aDocument, aContainer, aChild, eInserted);
+  DoContentInserted(aChild, eInserted);
 }
 
 bool
-HTMLEditor::IsInObservedSubtree(nsIDocument* aDocument,
-                                nsIContent* aContainer,
-                                nsIContent* aChild)
+HTMLEditor::IsInObservedSubtree(nsIContent* aChild)
 {
   if (!aChild) {
     return false;
   }
 
   Element* root = GetRoot();
   // To be super safe here, check both ChromeOnlyAccess and GetBindingParent.
   // That catches (also unbound) native anonymous content, XBL and ShadowDOM.
@@ -3168,26 +3162,24 @@ HTMLEditor::IsInObservedSubtree(nsIDocum
        root->GetBindingParent() != aChild->GetBindingParent())) {
     return false;
   }
 
   return !aChild->ChromeOnlyAccess() && !aChild->GetBindingParent();
 }
 
 void
-HTMLEditor::DoContentInserted(nsIDocument* aDocument,
-                              nsIContent* aContainer,
-                              nsIContent* aChild,
+HTMLEditor::DoContentInserted(nsIContent* aChild,
                               InsertedOrAppended aInsertedOrAppended)
 {
   MOZ_ASSERT(aChild);
-  nsINode* container = NODE_FROM(aContainer, aDocument);
+  nsINode* container = aChild->GetParentNode();
   MOZ_ASSERT(container);
 
-  if (!IsInObservedSubtree(aDocument, aContainer, aChild)) {
+  if (!IsInObservedSubtree(aChild)) {
     return;
   }
 
   // XXX Why do we need this? This method is a helper of mutation observer.
   //     So, the callers of mutation observer should guarantee that this won't
   //     be deleted at least during the call.
   RefPtr<HTMLEditor> kungFuDeathGrip(this);
 
@@ -3218,39 +3210,36 @@ HTMLEditor::DoContentInserted(nsIDocumen
       }
       range->SelectNodesInContainer(container, aChild, endContent);
       mInlineSpellChecker->SpellCheckRange(range);
     }
   }
 }
 
 void
-HTMLEditor::ContentRemoved(nsIDocument* aDocument,
-                           nsIContent* aContainer,
-                           nsIContent* aChild,
+HTMLEditor::ContentRemoved(nsIContent* aChild,
                            nsIContent* aPreviousSibling)
 {
-  if (!IsInObservedSubtree(aDocument, aContainer, aChild)) {
+  if (!IsInObservedSubtree(aChild)) {
     return;
   }
 
   // XXX Why do we need to do this?  This method is a mutation observer's
   //     method.  Therefore, the caller should guarantee that this won't be
   //     deleted during the call.
   RefPtr<HTMLEditor> kungFuDeathGrip(this);
 
   if (SameCOMIdentity(aChild, mRootElement)) {
     mRootElement = nullptr;
     nsContentUtils::AddScriptRunner(
       NewRunnableMethod("HTMLEditor::NotifyRootChanged",
                         this,
                         &HTMLEditor::NotifyRootChanged));
-  }
   // We don't need to handle our own modifications
-  else if (!mAction && (aContainer ? aContainer->IsEditable() : aDocument->IsEditable())) {
+  } else if (!mAction && aChild->GetParentNode()->IsEditable()) {
     if (aChild && IsMozEditorBogusNode(aChild)) {
       // Ignore removal of the bogus node
       return;
     }
     // Protect the edit rules object from dying
     RefPtr<TextEditRules> rules(mRules);
     rules->DocumentModified();
   }
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -1158,19 +1158,17 @@ protected:
   nsresult GetPositionAndDimensions(Element& aElement,
                                     int32_t& aX, int32_t& aY,
                                     int32_t& aW, int32_t& aH,
                                     int32_t& aBorderLeft,
                                     int32_t& aBorderTop,
                                     int32_t& aMarginLeft,
                                     int32_t& aMarginTop);
 
-  bool IsInObservedSubtree(nsIDocument* aDocument,
-                           nsIContent* aContainer,
-                           nsIContent* aChild);
+  bool IsInObservedSubtree(nsIContent* aChild);
 
   void UpdateRootElement();
 
   // resizing
   bool mIsObjectResizingEnabled;
   bool mIsResizing;
   bool mPreserveRatio;
   bool mResizedObjectIsAnImage;
@@ -1354,19 +1352,17 @@ private:
                               nsAtom* aProperty,
                               nsAtom* aAttribute,
                               const nsAString* aValue);
   nsresult SetInlinePropertyOnNodeImpl(nsIContent& aNode,
                                        nsAtom& aProperty,
                                        nsAtom* aAttribute,
                                        const nsAString& aValue);
   typedef enum { eInserted, eAppended } InsertedOrAppended;
-  void DoContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
-                         nsIContent* aChild,
-                         InsertedOrAppended aInsertedOrAppended);
+  void DoContentInserted(nsIContent* aChild, InsertedOrAppended);
   already_AddRefed<Element> GetElementOrParentByTagName(
                               const nsAString& aTagName, nsINode* aNode);
   already_AddRefed<Element> CreateElementWithDefaults(
                               const nsAString& aTagName);
   /**
    * Returns an anonymous Element of type aTag,
    * child of aParentContent. If aIsCreatedHidden is true, the class
    * "hidden" is added to the created element. If aAnonClass is not
--- a/image/DecoderFactory.cpp
+++ b/image/DecoderFactory.cpp
@@ -224,20 +224,26 @@ DecoderFactory::CreateAnimationDecoder(D
   task.forget(aOutTask);
   return NS_OK;
 }
 
 /* static */ already_AddRefed<Decoder>
 DecoderFactory::CloneAnimationDecoder(Decoder* aDecoder)
 {
   MOZ_ASSERT(aDecoder);
-  MOZ_ASSERT(aDecoder->HasAnimation());
 
-  RefPtr<Decoder> decoder = GetDecoder(aDecoder->GetType(), nullptr,
-                                       /* aIsRedecode = */ true);
+  // In an ideal world, we would assert aDecoder->HasAnimation() but we cannot.
+  // The decoder may not have detected it is animated yet (e.g. it did not even
+  // get scheduled yet, or it has only decoded the first frame and has yet to
+  // rediscover it is animated).
+  DecoderType type = aDecoder->GetType();
+  MOZ_ASSERT(type == DecoderType::GIF || type == DecoderType::PNG,
+             "Calling CloneAnimationDecoder for non-animating DecoderType");
+
+  RefPtr<Decoder> decoder = GetDecoder(type, nullptr, /* aIsRedecode = */ true);
   MOZ_ASSERT(decoder, "Should have a decoder now");
 
   // Initialize the decoder.
   decoder->SetMetadataDecode(false);
   decoder->SetIterator(aDecoder->GetSourceBuffer()->Iterator());
   decoder->SetDecoderFlags(aDecoder->GetDecoderFlags());
   decoder->SetSurfaceFlags(aDecoder->GetSurfaceFlags());
 
--- a/js/public/Conversions.h
+++ b/js/public/Conversions.h
@@ -8,16 +8,17 @@
 
 #ifndef js_Conversions_h
 #define js_Conversions_h
 
 #include "mozilla/Casting.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/TypeTraits.h"
+#include "mozilla/WrappingOperations.h"
 
 #include <math.h>
 
 #include "jspubtd.h"
 
 #include "js/RootingAPI.h"
 #include "js/Value.h"
 
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -10,16 +10,17 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Compiler.h"
 #include "mozilla/Move.h"
 #include "mozilla/Scoped.h"
 #include "mozilla/TemplateLib.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/WrappingOperations.h"
 
 #include <stdlib.h>
 #include <string.h>
 
 #ifdef JS_OOM_DO_BACKTRACES
 #include <execinfo.h>
 #include <stdio.h>
 #endif
@@ -692,17 +693,17 @@ ScrambleHashCode(HashNumber h)
      * Programming, 6.4. This mixes all the bits of the input hash code h.
      *
      * The value of goldenRatio is taken from the hex
      * expansion of the golden ratio, which starts 1.9E3779B9....
      * This value is especially good if values with consecutive hash codes
      * are stored in a hash table; see Knuth for details.
      */
     static const HashNumber goldenRatio = 0x9E3779B9U;
-    return h * goldenRatio;
+    return mozilla::WrappingMultiply(h, goldenRatio);
 }
 
 } /* namespace detail */
 
 } /* namespace js */
 
 /* sixgill annotation defines */
 #ifndef HAVE_STATIC_ANNOTATIONS
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -115,17 +115,17 @@ CreateRegExpSearchResult(const MatchPair
 }
 
 /*
  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
  * steps 3, 9-14, except 12.a.i, 12.c.i.1.
  */
 static RegExpRunStatus
 ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, MutableHandleRegExpShared re,
-                  HandleLinearString input, size_t searchIndex, MatchPairs* matches,
+                  HandleLinearString input, size_t searchIndex, VectorMatchPairs* matches,
                   size_t* endIndex)
 {
     RegExpRunStatus status = RegExpShared::execute(cx, re, input, searchIndex, matches, endIndex);
 
     /* Out of spec: Update RegExpStatics. */
     if (status == RegExpRunStatus_Success && res) {
         if (matches) {
             if (!res->updateFromMatchPairs(cx, input, *matches))
@@ -142,17 +142,17 @@ bool
 js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*> reobj,
                         HandleLinearString input, size_t* lastIndex, bool test,
                         MutableHandleValue rval)
 {
     RootedRegExpShared shared(cx, RegExpObject::getShared(cx, reobj));
     if (!shared)
         return false;
 
-    ScopedMatchPairs matches(&cx->tempLifoAlloc());
+    VectorMatchPairs matches;
 
     RegExpRunStatus status = ExecuteRegExpImpl(cx, res, &shared, input, *lastIndex,
                                                &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success_NotFound) {
         /* ExecuteRegExp() previously returned an array or null. */
@@ -899,17 +899,17 @@ IsTrailSurrogateWithLeadSurrogate(Handle
 }
 
 /*
  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
  * steps 3, 9-14, except 12.a.i, 12.c.i.1.
  */
 static RegExpRunStatus
 ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string, int32_t lastIndex,
-              MatchPairs* matches, size_t* endIndex)
+              VectorMatchPairs* matches, size_t* endIndex)
 {
     /*
      * WARNING: Despite the presence of spec step comment numbers, this
      *          algorithm isn't consistent with any ES6 version, draft or
      *          otherwise.  YOU HAVE BEEN WARNED.
      */
 
     /* Steps 1-2 performed by the caller. */
@@ -974,17 +974,17 @@ ExecuteRegExp(JSContext* cx, HandleObjec
  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
  * steps 3, 9-25, except 12.a.i, 12.c.i.1, 15.
  */
 static bool
 RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string, int32_t lastIndex,
                   MutableHandleValue rval)
 {
     /* Execute regular expression and gather matches. */
-    ScopedMatchPairs matches(&cx->tempLifoAlloc());
+    VectorMatchPairs matches;
 
     /* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
     RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex, &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     /* Steps 12.a, 12.c. */
     if (status == RegExpRunStatus_Success_NotFound) {
@@ -1043,17 +1043,17 @@ js::RegExpMatcherRaw(JSContext* cx, Hand
  * This code is inlined in CodeGenerator.cpp generateRegExpSearcherStub,
  * changes to this code need to get reflected in there too.
  */
 static bool
 RegExpSearcherImpl(JSContext* cx, HandleObject regexp, HandleString string, int32_t lastIndex,
                    int32_t* result)
 {
     /* Execute regular expression and gather matches. */
-    ScopedMatchPairs matches(&cx->tempLifoAlloc());
+    VectorMatchPairs matches;
 
     /* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
     RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex, &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     /* Steps 12.a, 12.c. */
     if (status == RegExpRunStatus_Success_NotFound) {
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -684,16 +684,22 @@ class FullParseHandler
     }
 
     bool isExpressionClosure(ParseNode* node) const {
         return node->isKind(ParseNodeKind::Function) &&
                node->pn_funbox->isExprBody() &&
                !node->pn_funbox->isArrow();
     }
 
+    void noteExpressionClosure(Node* funcNode) const {
+        // No need to do anything: |funcNode->pn_funbox| modifications
+        // performed elsewhere in the relevant code path will assure
+        // |isExpressionClosure| above tests true on |*funcNode|.
+    }
+
     ParseNode* newObjectMethodOrPropertyDefinition(ParseNode* key, ParseNode* fn, AccessorType atype) {
         MOZ_ASSERT(isUsableAsObjectPropertyName(key));
 
         return newBinary(ParseNodeKind::Colon, key, fn, AccessorTypeToJSOp(atype));
     }
 
     bool setComprehensionLambdaBody(ParseNode* pn, ParseNode* body) {
         MOZ_ASSERT(body->isKind(ParseNodeKind::StatementList));
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2594,17 +2594,17 @@ Parser<FullParseHandler, CharT>::standal
     SourceParseContext funpc(this, funbox, newDirectives);
     if (!funpc.init())
         return null();
     funpc.setIsStandaloneFunctionBody();
 
     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
     AwaitHandling awaitHandling = GetAwaitHandling(asyncKind);
     AutoAwaitIsKeyword<FullParseHandler, CharT> awaitIsKeyword(this, awaitHandling);
-    if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement,
+    if (!functionFormalParametersAndBody(InAllowed, yieldHandling, &fn, Statement,
                                          parameterListEnd, /* isStandaloneFunction = */ true))
     {
         return null();
     }
 
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     if (tt != TokenKind::Eof) {
@@ -3409,17 +3409,17 @@ GeneralParser<ParseHandler, CharT>::func
 
     Position start(keepAtoms);
     tokenStream.tell(&start);
 
     // Parse the inner function. The following is a loop as we may attempt to
     // reparse a function due to failed syntax parsing and encountering new
     // "use foo" directives.
     while (true) {
-        if (trySyntaxParseInnerFunction(funcNode, fun, toStringStart, inHandling, yieldHandling,
+        if (trySyntaxParseInnerFunction(&funcNode, fun, toStringStart, inHandling, yieldHandling,
                                         kind, generatorKind, asyncKind, tryAnnexB, directives,
                                         &newDirectives))
         {
             break;
         }
 
         // Return on error.
         if (anyChars.hadError() || directives == newDirectives)
@@ -3437,17 +3437,17 @@ GeneralParser<ParseHandler, CharT>::func
         handler.setFunctionFormalParametersAndBody(funcNode, null());
     }
 
     return funcNode;
 }
 
 template <typename CharT>
 bool
-Parser<FullParseHandler, CharT>::trySyntaxParseInnerFunction(ParseNode* funcNode,
+Parser<FullParseHandler, CharT>::trySyntaxParseInnerFunction(ParseNode** funcNode,
                                                              HandleFunction fun,
                                                              uint32_t toStringStart,
                                                              InHandling inHandling,
                                                              YieldHandling yieldHandling,
                                                              FunctionSyntaxKind kind,
                                                              GeneratorKind generatorKind,
                                                              FunctionAsyncKind asyncKind,
                                                              bool tryAnnexB,
@@ -3455,17 +3455,17 @@ Parser<FullParseHandler, CharT>::trySynt
                                                              Directives* newDirectives)
 {
     // Try a syntax parse for this inner function.
     do {
         // If we're assuming this function is an IIFE, always perform a full
         // parse to avoid the overhead of a lazy syntax-only parse. Although
         // the prediction may be incorrect, IIFEs are common enough that it
         // pays off for lots of code.
-        if (funcNode->isLikelyIIFE() &&
+        if ((*funcNode)->isLikelyIIFE() &&
             generatorKind == GeneratorKind::NotGenerator &&
             asyncKind == FunctionAsyncKind::SyncFunction)
         {
             break;
         }
 
         SyntaxParser* syntaxParser = getSyntaxParser();
         if (!syntaxParser)
@@ -3477,26 +3477,27 @@ Parser<FullParseHandler, CharT>::trySynt
         Position position(keepAtoms);
         tokenStream.tell(&position);
         if (!syntaxParser->tokenStream.seek(position, anyChars))
             return false;
 
         // Make a FunctionBox before we enter the syntax parser, because |pn|
         // still expects a FunctionBox to be attached to it during BCE, and
         // the syntax parser cannot attach one to it.
-        FunctionBox* funbox = newFunctionBox(funcNode, fun, toStringStart, inheritedDirectives,
+        FunctionBox* funbox = newFunctionBox(*funcNode, fun, toStringStart, inheritedDirectives,
                                              generatorKind, asyncKind);
         if (!funbox)
             return false;
         funbox->initWithEnclosingParseContext(pc, kind);
 
-        if (!syntaxParser->innerFunctionForFunctionBox(SyntaxParseHandler::NodeGeneric,
-                                                       pc, funbox, inHandling, yieldHandling,
-                                                       kind, newDirectives))
-        {
+        SyntaxParseHandler::Node syntaxNode =
+            syntaxParser->innerFunctionForFunctionBox(SyntaxParseHandler::NodeGeneric, pc, funbox,
+                                                      inHandling, yieldHandling, kind,
+                                                      newDirectives);
+        if (!syntaxNode) {
             if (syntaxParser->hadAbortedSyntaxParse()) {
                 // Try again with a full parse. UsedNameTracker needs to be
                 // rewound to just before we tried the syntax parse for
                 // correctness.
                 syntaxParser->clearAbortedSyntaxParse();
                 usedNames.rewind(token);
                 MOZ_ASSERT_IF(!syntaxParser->context->helperThread(),
                               !syntaxParser->context->isExceptionPending());
@@ -3506,53 +3507,66 @@ Parser<FullParseHandler, CharT>::trySynt
         }
 
         // Advance this parser over tokens processed by the syntax parser.
         syntaxParser->tokenStream.tell(&position);
         if (!tokenStream.seek(position, syntaxParser->anyChars))
             return false;
 
         // Update the end position of the parse node.
-        funcNode->pn_pos.end = anyChars.currentToken().pos.end;
+        (*funcNode)->pn_pos.end = anyChars.currentToken().pos.end;
 
         // Append possible Annex B function box only upon successfully parsing.
         if (tryAnnexB) {
             if (!pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
                 return false;
         }
 
         return true;
     } while (false);
 
     // We failed to do a syntax parse above, so do the full parse.
-    return innerFunction(funcNode, pc, fun, toStringStart, inHandling, yieldHandling, kind,
-                         generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives);
+    Node innerFunc =
+        innerFunction(*funcNode, pc, fun, toStringStart, inHandling, yieldHandling, kind,
+                      generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives);
+    if (!innerFunc)
+        return false;
+
+    *funcNode = innerFunc;
+    return true;
 }
 
 template <typename CharT>
 bool
-Parser<SyntaxParseHandler, CharT>::trySyntaxParseInnerFunction(Node funcNode, HandleFunction fun,
+Parser<SyntaxParseHandler, CharT>::trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun,
                                                                uint32_t toStringStart,
                                                                InHandling inHandling,
                                                                YieldHandling yieldHandling,
                                                                FunctionSyntaxKind kind,
                                                                GeneratorKind generatorKind,
                                                                FunctionAsyncKind asyncKind,
                                                                bool tryAnnexB,
                                                                Directives inheritedDirectives,
                                                                Directives* newDirectives)
 {
     // This is already a syntax parser, so just parse the inner function.
-    return innerFunction(funcNode, pc, fun, toStringStart, inHandling, yieldHandling, kind,
-                         generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives);
+    Node innerFunc =
+        innerFunction(*funcNode, pc, fun, toStringStart, inHandling, yieldHandling, kind,
+                      generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives);
+
+    if (!innerFunc)
+        return false;
+
+    *funcNode = innerFunc;
+    return true;
 }
 
 template <class ParseHandler, typename CharT>
 inline bool
-GeneralParser<ParseHandler, CharT>::trySyntaxParseInnerFunction(Node funcNode, HandleFunction fun,
+GeneralParser<ParseHandler, CharT>::trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun,
                                                                 uint32_t toStringStart,
                                                                 InHandling inHandling,
                                                                 YieldHandling yieldHandling,
                                                                 FunctionSyntaxKind kind,
                                                                 GeneratorKind generatorKind,
                                                                 FunctionAsyncKind asyncKind,
                                                                 bool tryAnnexB,
                                                                 Directives inheritedDirectives,
@@ -3560,43 +3574,46 @@ GeneralParser<ParseHandler, CharT>::tryS
 {
     return asFinalParser()->trySyntaxParseInnerFunction(funcNode, fun, toStringStart, inHandling,
                                                         yieldHandling, kind, generatorKind,
                                                         asyncKind, tryAnnexB, inheritedDirectives,
                                                         newDirectives);
 }
 
 template <class ParseHandler, typename CharT>
-bool
+typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::innerFunctionForFunctionBox(Node funcNode,
                                                                 ParseContext* outerpc,
                                                                 FunctionBox* funbox,
                                                                 InHandling inHandling,
                                                                 YieldHandling yieldHandling,
                                                                 FunctionSyntaxKind kind,
                                                                 Directives* newDirectives)
 {
     // Note that it is possible for outerpc != this->pc, as we may be
     // attempting to syntax parse an inner function from an outer full
     // parser. In that case, outerpc is a SourceParseContext from the full parser
     // instead of the current top of the stack of the syntax parser.
 
     // Push a new ParseContext.
     SourceParseContext funpc(this, funbox, newDirectives);
     if (!funpc.init())
-        return false;
-
-    if (!functionFormalParametersAndBody(inHandling, yieldHandling, funcNode, kind))
-        return false;
-
-    return leaveInnerFunction(outerpc);
-}
-
-template <class ParseHandler, typename CharT>
-bool
+        return null();
+
+    if (!functionFormalParametersAndBody(inHandling, yieldHandling, &funcNode, kind))
+        return null();
+
+    if (!leaveInnerFunction(outerpc))
+        return null();
+
+    return funcNode;
+}
+
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::innerFunction(Node funcNode, ParseContext* outerpc,
                                                   HandleFunction fun, uint32_t toStringStart,
                                                   InHandling inHandling,
                                                   YieldHandling yieldHandling,
                                                   FunctionSyntaxKind kind,
                                                   GeneratorKind generatorKind,
                                                   FunctionAsyncKind asyncKind, bool tryAnnexB,
                                                   Directives inheritedDirectives,
@@ -3605,32 +3622,32 @@ GeneralParser<ParseHandler, CharT>::inne
     // Note that it is possible for outerpc != this->pc, as we may be
     // attempting to syntax parse an inner function from an outer full
     // parser. In that case, outerpc is a SourceParseContext from the full parser
     // instead of the current top of the stack of the syntax parser.
 
     FunctionBox* funbox = newFunctionBox(funcNode, fun, toStringStart, inheritedDirectives,
                                          generatorKind, asyncKind);
     if (!funbox)
-        return false;
+        return null();
     funbox->initWithEnclosingParseContext(outerpc, kind);
 
-    if (!innerFunctionForFunctionBox(funcNode, outerpc, funbox, inHandling, yieldHandling, kind,
-                                     newDirectives))
-    {
-        return false;
-    }
+    Node innerFunc =
+        innerFunctionForFunctionBox(funcNode, outerpc, funbox, inHandling, yieldHandling, kind,
+                                     newDirectives);
+    if (!innerFunc)
+        return null();
 
     // Append possible Annex B function box only upon successfully parsing.
     if (tryAnnexB) {
         if (!pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
-            return false;
-    }
-
-    return true;
+            return null();
+    }
+
+    return innerFunc;
 }
 
 template <class ParseHandler, typename CharT>
 bool
 GeneralParser<ParseHandler, CharT>::appendToCallSiteObj(Node callSiteObj)
 {
     Node cookedNode = noSubstitutionTaggedTemplate();
     if (!cookedNode)
@@ -3690,32 +3707,32 @@ Parser<FullParseHandler, CharT>::standal
         syntaxKind = Method;
     else if (fun->isGetter())
         syntaxKind = Getter;
     else if (fun->isSetter())
         syntaxKind = Setter;
     else if (fun->isArrow())
         syntaxKind = Arrow;
 
-    if (!functionFormalParametersAndBody(InAllowed, yieldHandling, pn, syntaxKind)) {
+    if (!functionFormalParametersAndBody(InAllowed, yieldHandling, &pn, syntaxKind)) {
         MOZ_ASSERT(directives == newDirectives);
         return null();
     }
 
     if (!FoldConstants(context, &pn, this))
         return null();
 
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 bool
 GeneralParser<ParseHandler, CharT>::functionFormalParametersAndBody(InHandling inHandling,
                                                                     YieldHandling yieldHandling,
-                                                                    Node pn,
+                                                                    Node* pn,
                                                                     FunctionSyntaxKind kind,
                                                                     const Maybe<uint32_t>& parameterListEnd /* = Nothing() */,
                                                                     bool isStandaloneFunction /* = false */)
 {
     // Given a properly initialized parse context, try to parse an actual
     // function without concern for conversion to strict mode, use of lazy
     // parsing and such.
 
@@ -3724,17 +3741,17 @@ GeneralParser<ParseHandler, CharT>::func
 
     // See below for an explanation why arrow function parameters and arrow
     // function bodies are parsed with different yield/await settings.
     {
         AwaitHandling awaitHandling = funbox->isAsync() || (kind == Arrow && awaitIsKeyword())
                                       ? AwaitIsKeyword
                                       : AwaitIsName;
         AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, awaitHandling);
-        if (!functionArguments(yieldHandling, kind, pn))
+        if (!functionArguments(yieldHandling, kind, *pn))
             return false;
     }
 
     Maybe<ParseContext::VarScope> varScope;
     if (funbox->hasParameterExprs) {
         varScope.emplace(this);
         if (!varScope->init(pc))
             return false;
@@ -3774,16 +3791,17 @@ GeneralParser<ParseHandler, CharT>::func
                 error(JSMSG_CURLY_BEFORE_BODY);
                 return false;
             }
 
             if (allowExpressionClosures()) {
                 this->addTelemetry(DeprecatedLanguageExtension::ExpressionClosure);
                 if (!warnOnceAboutExprClosure())
                     return false;
+                handler.noteExpressionClosure(pn);
             } else {
                 error(JSMSG_CURLY_BEFORE_BODY);
                 return false;
             }
         }
 
         anyChars.ungetToken();
         bodyType = ExpressionBody;
@@ -3828,17 +3846,17 @@ GeneralParser<ParseHandler, CharT>::func
             // because of different context.
             // It should already be checked before this point.
             nameYieldHandling = YieldIsName;
         }
 
         // We already use the correct await-handling at this point, therefore
         // we don't need call AutoAwaitIsKeyword here.
 
-        uint32_t nameOffset = handler.getFunctionNameOffset(pn, anyChars);
+        uint32_t nameOffset = handler.getFunctionNameOffset(*pn, anyChars);
         if (!checkBindingIdentifier(propertyName, nameOffset, nameYieldHandling))
             return false;
     }
 
     if (bodyType == StatementListBody) {
         MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::Rc, TokenStream::Operand,
                                          reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
                                                               JSMSG_CURLY_OPENED, openedPos));
@@ -3855,18 +3873,18 @@ GeneralParser<ParseHandler, CharT>::func
 
     if (IsMethodDefinitionKind(kind) && pc->superScopeNeedsHomeObject())
         funbox->setNeedsHomeObject();
 
     if (!finishFunction(isStandaloneFunction))
         return false;
 
     handler.setEndPosition(body, pos().begin);
-    handler.setEndPosition(pn, pos().end);
-    handler.setFunctionBody(pn, body);
+    handler.setEndPosition(*pn, pos().end);
+    handler.setFunctionBody(*pn, body);
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::functionStmt(uint32_t toStringStart,
                                                  YieldHandling yieldHandling,
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -936,41 +936,42 @@ class GeneralParser
 
     Node noSubstitutionUntaggedTemplate();
     Node templateLiteral(YieldHandling yieldHandling);
     bool taggedTemplate(YieldHandling yieldHandling, Node nodeList, TokenKind tt);
     bool appendToCallSiteObj(Node callSiteObj);
     bool addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList,
                                         TokenKind* ttp);
 
-    inline bool trySyntaxParseInnerFunction(Node funcNode, HandleFunction fun,
+    inline bool trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun,
                                             uint32_t toStringStart, InHandling inHandling,
                                             YieldHandling yieldHandling, FunctionSyntaxKind kind,
                                             GeneratorKind generatorKind,
                                             FunctionAsyncKind asyncKind, bool tryAnnexB,
                                             Directives inheritedDirectives,
                                             Directives* newDirectives);
 
     inline bool skipLazyInnerFunction(Node funcNode, uint32_t toStringStart,
                                       FunctionSyntaxKind kind, bool tryAnnexB);
 
   public:
     /* Public entry points for parsing. */
     Node statementListItem(YieldHandling yieldHandling, bool canHaveDirectives = false);
 
     // Parse an inner function given an enclosing ParseContext and a
     // FunctionBox for the inner function.
-    bool innerFunctionForFunctionBox(Node funcNode, ParseContext* outerpc, FunctionBox* funbox,
-                                     InHandling inHandling, YieldHandling yieldHandling,
-                                     FunctionSyntaxKind kind, Directives* newDirectives);
+    MOZ_MUST_USE Node
+    innerFunctionForFunctionBox(Node funcNode, ParseContext* outerpc, FunctionBox* funbox,
+                                InHandling inHandling, YieldHandling yieldHandling,
+                                FunctionSyntaxKind kind, Directives* newDirectives);
 
     // Parse a function's formal parameters and its body assuming its function
     // ParseContext is already on the stack.
     bool functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling,
-                                         Node pn, FunctionSyntaxKind kind,
+                                         Node* pn, FunctionSyntaxKind kind,
                                          const mozilla::Maybe<uint32_t>& parameterListEnd = mozilla::Nothing(),
                                          bool isStandaloneFunction = false);
 
   private:
     /*
      * JS parsers, from lowest to highest precedence.
      *
      * Each parser must be called during the dynamic scope of a ParseContext
@@ -1241,21 +1242,22 @@ class GeneralParser
     PropertyName* bindingIdentifier(YieldHandling yieldHandling);
 
     bool checkLabelOrIdentifierReference(PropertyName* ident, uint32_t offset,
                                          YieldHandling yieldHandling,
                                          TokenKind hint = TokenKind::Limit);
 
     Node statementList(YieldHandling yieldHandling);
 
-    bool innerFunction(Node funcNode, ParseContext* outerpc, HandleFunction fun,
-                       uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling,
-                       FunctionSyntaxKind kind, GeneratorKind generatorKind,
-                       FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives,
-                       Directives* newDirectives);
+    MOZ_MUST_USE Node
+    innerFunction(Node funcNode, ParseContext* outerpc, HandleFunction fun,
+                  uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling,
+                  FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                  FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives,
+                  Directives* newDirectives);
 
     bool matchOrInsertSemicolon();
 
     bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
 
   private:
     inline bool asmJS(Node list);
 };
@@ -1348,17 +1350,17 @@ class Parser<SyntaxParseHandler, CharT> 
     inline Node importDeclaration();
     inline bool checkLocalExportNames(Node node);
     inline bool checkExportedName(JSAtom* exportName);
     inline bool checkExportedNamesForDeclaration(Node node);
     inline bool checkExportedNameForFunction(Node node);
     inline bool checkExportedNameForClass(Node node);
     inline bool checkExportedNameForClause(Node node);
 
-    bool trySyntaxParseInnerFunction(Node funcNode, HandleFunction fun, uint32_t toStringStart,
+    bool trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun, uint32_t toStringStart,
                                      InHandling inHandling, YieldHandling yieldHandling,
                                      FunctionSyntaxKind kind, GeneratorKind generatorKind,
                                      FunctionAsyncKind asyncKind, bool tryAnnexB,
                                      Directives inheritedDirectives, Directives* newDirectives);
 
     bool skipLazyInnerFunction(Node funcNode, uint32_t toStringStart, FunctionSyntaxKind kind,
                                bool tryAnnexB);
 
@@ -1464,17 +1466,17 @@ class Parser<FullParseHandler, CharT> fi
     Node importDeclaration();
     bool checkLocalExportNames(Node node);
     bool checkExportedName(JSAtom* exportName);
     bool checkExportedNamesForDeclaration(Node node);
     bool checkExportedNameForFunction(Node node);
     bool checkExportedNameForClass(Node node);
     inline bool checkExportedNameForClause(Node node);
 
-    bool trySyntaxParseInnerFunction(Node funcNode, HandleFunction fun, uint32_t toStringStart,
+    bool trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun, uint32_t toStringStart,
                                      InHandling inHandling, YieldHandling yieldHandling,
                                      FunctionSyntaxKind kind, GeneratorKind generatorKind,
                                      FunctionAsyncKind asyncKind, bool tryAnnexB,
                                      Directives inheritedDirectives, Directives* newDirectives);
 
     bool skipLazyInnerFunction(Node funcNode, uint32_t toStringStart, FunctionSyntaxKind kind,
                                bool tryAnnexB);
 
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -355,16 +355,20 @@ class SyntaxParseHandler
     }
 
     Node newArrowFunction(const TokenPos& pos) { return NodeFunctionArrow; }
 
     bool isExpressionClosure(Node node) const {
         return node == NodeFunctionExpressionClosure;
     }
 
+    void noteExpressionClosure(Node* funcNode) const {
+        *funcNode = NodeFunctionExpressionClosure;
+    }
+
     void setFunctionFormalParametersAndBody(Node funcNode, Node kid) {}
     void setFunctionBody(Node pn, Node kid) {}
     void setFunctionBox(Node pn, FunctionBox* funbox) {}
     void addFunctionFormalParameter(Node pn, Node argpn) {}
 
     Node newForStatement(uint32_t begin, Node forHead, Node body, unsigned iflags) {
         return NodeGeneric;
     }
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -1012,32 +1012,27 @@ js::Nursery::sweep(JSTracer* trc)
         c->sweepAfterMinorGC(trc);
 
     sweepDictionaryModeObjects();
 }
 
 void
 js::Nursery::clear()
 {
-#ifdef JS_GC_ZEAL
+#if defined(JS_GC_ZEAL) || defined(JS_CRASH_DIAGNOSTICS)
     /* Poison the nursery contents so touching a freed object will crash. */
-    for (unsigned i = 0; i < allocatedChunkCount(); i++)
+    for (unsigned i = currentStartChunk_; i < allocatedChunkCount(); ++i)
         chunk(i).poisonAndInit(runtime(), JS_SWEPT_NURSERY_PATTERN);
+#endif
 
     if (runtime()->hasZealMode(ZealMode::GenerationalGC)) {
         /* Only reset the alloc point when we are close to the end. */
         if (currentChunk_ + 1 == maxChunkCount())
             setCurrentChunk(0);
-    } else
-#endif
-    {
-#ifdef JS_CRASH_DIAGNOSTICS
-        for (unsigned i = 0; i < allocatedChunkCount(); ++i)
-            chunk(i).poisonAndInit(runtime(), JS_SWEPT_NURSERY_PATTERN);
-#endif
+    } else {
         setCurrentChunk(0);
     }
 
     /* Set current start position for isEmpty checks. */
     setStartPosition();
 }
 
 size_t
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3787,16 +3787,46 @@ void
 CodeGenerator::visitCopyLexicalEnvironmentObject(LCopyLexicalEnvironmentObject* lir)
 {
     pushArg(Imm32(lir->mir()->copySlots()));
     pushArg(ToRegister(lir->env()));
     callVM(CopyLexicalEnvironmentObjectInfo, lir);
 }
 
 void
+CodeGenerator::visitGuardShape(LGuardShape* guard)
+{
+    Register obj = ToRegister(guard->input());
+    Label bail;
+    masm.branchTestObjShape(Assembler::NotEqual, obj, guard->mir()->shape(), &bail);
+    bailoutFrom(&bail, guard->snapshot());
+}
+
+void
+CodeGenerator::visitGuardObjectGroup(LGuardObjectGroup* guard)
+{
+    Register obj = ToRegister(guard->input());
+    Assembler::Condition cond =
+        guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
+    Label bail;
+    masm.branchTestObjGroup(cond, obj, guard->mir()->group(), &bail);
+    bailoutFrom(&bail, guard->snapshot());
+}
+
+void
+CodeGenerator::visitGuardClass(LGuardClass* guard)
+{
+    Register obj = ToRegister(guard->input());
+    Register tmp = ToRegister(guard->tempInt());
+    Label bail;
+    masm.branchTestObjClass(Assembler::NotEqual, obj, tmp, guard->mir()->getClass(), &bail);
+    bailoutFrom(&bail, guard->snapshot());
+}
+
+void
 CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity* guard)
 {
     Register input = ToRegister(guard->input());
     Register expected = ToRegister(guard->expected());
 
     Assembler::Condition cond =
         guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
     bailoutCmpPtr(cond, input, expected, guard->snapshot());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -150,16 +150,19 @@ class CodeGenerator final : public CodeG
     void visitLoadSlotT(LLoadSlotT* lir);
     void visitLoadSlotV(LLoadSlotV* lir);
     void visitStoreSlotT(LStoreSlotT* lir);
     void visitStoreSlotV(LStoreSlotV* lir);
     void visitElements(LElements* lir);
     void visitConvertElementsToDoubles(LConvertElementsToDoubles* lir);
     void visitMaybeToDoubleElement(LMaybeToDoubleElement* lir);
     void visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite* lir);
+    void visitGuardShape(LGuardShape* guard);
+    void visitGuardObjectGroup(LGuardObjectGroup* guard);
+    void visitGuardClass(LGuardClass* guard);
     void visitGuardObjectIdentity(LGuardObjectIdentity* guard);
     void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir);
     void visitGuardUnboxedExpando(LGuardUnboxedExpando* lir);
     void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir);
     void visitTypeBarrierV(LTypeBarrierV* lir);
     void visitTypeBarrierO(LTypeBarrierO* lir);
     void emitPostWriteBarrier(const LAllocation* obj);
     void emitPostWriteBarrier(Register objreg);
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3993,20 +3993,41 @@ LIRGenerator::visitGuardObjectIdentity(M
     LGuardObjectIdentity* guard = new(alloc()) LGuardObjectIdentity(useRegister(ins->object()),
                                                                     useRegister(ins->expected()));
     assignSnapshot(guard, Bailout_ObjectIdentityOrTypeGuard);
     add(guard, ins);
     redefine(ins, ins->object());
 }
 
 void
+LIRGenerator::visitGuardShape(MGuardShape* ins)
+{
+    MOZ_ASSERT(ins->object()->type() == MIRType::Object);
+
+    LGuardShape* guard = new(alloc()) LGuardShape(useRegisterAtStart(ins->object()));
+    assignSnapshot(guard, ins->bailoutKind());
+    add(guard, ins);
+    redefine(ins, ins->object());
+}
+
+void
+LIRGenerator::visitGuardObjectGroup(MGuardObjectGroup* ins)
+{
+    MOZ_ASSERT(ins->object()->type() == MIRType::Object);
+
+    LGuardObjectGroup* guard = new(alloc()) LGuardObjectGroup(useRegisterAtStart(ins->object()));
+    assignSnapshot(guard, ins->bailoutKind());
+    add(guard, ins);
+    redefine(ins, ins->object());
+}
+
+void
 LIRGenerator::visitGuardClass(MGuardClass* ins)
 {
-    LDefinition t = temp();
-    LGuardClass* guard = new(alloc()) LGuardClass(useRegister(ins->object()), t);
+    LGuardClass* guard = new(alloc()) LGuardClass(useRegister(ins->object()), temp());
     assignSnapshot(guard, Bailout_ObjectIdentityOrTypeGuard);
     add(guard, ins);
 }
 
 void
 LIRGenerator::visitGuardObject(MGuardObject* ins)
 {
     // The type policy does all the work, so at this point the input
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -259,16 +259,18 @@ class LIRGenerator : public LIRGenerator
     void visitStoreFixedSlot(MStoreFixedSlot* ins) override;
     void visitGetPropSuperCache(MGetPropSuperCache* ins) override;
     void visitGetPropertyCache(MGetPropertyCache* ins) override;
     void visitGetPropertyPolymorphic(MGetPropertyPolymorphic* ins) override;
     void visitSetPropertyPolymorphic(MSetPropertyPolymorphic* ins) override;
     void visitBindNameCache(MBindNameCache* ins) override;
     void visitCallBindVar(MCallBindVar* ins) override;
     void visitGuardObjectIdentity(MGuardObjectIdentity* ins) override;
+    void visitGuardShape(MGuardShape* ins) override;
+    void visitGuardObjectGroup(MGuardObjectGroup* ins) override;
     void visitGuardClass(MGuardClass* ins) override;
     void visitGuardObject(MGuardObject* ins) override;
     void visitGuardString(MGuardString* ins) override;
     void visitGuardReceiverPolymorphic(MGuardReceiverPolymorphic* ins) override;
     void visitGuardUnboxedExpando(MGuardUnboxedExpando* ins) override;
     void visitLoadUnboxedExpando(MLoadUnboxedExpando* ins) override;
     void visitPolyInlineGuard(MPolyInlineGuard* ins) override;
     void visitAssertRange(MAssertRange* ins) override;
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -1738,46 +1738,16 @@ CodeGeneratorARM::visitNotF(LNotF* ins)
         masm.as_vmrs(pc);
         masm.ma_mov(Imm32(0), dest);
         masm.ma_mov(Imm32(1), dest, Assembler::Equal);
         masm.ma_mov(Imm32(1), dest, Assembler::Overflow);
     }
 }
 
 void
-CodeGeneratorARM::visitGuardShape(LGuardShape* guard)
-{
-    Register obj = ToRegister(guard->input());
-    Label bail;
-    masm.branchTestObjShape(Assembler::NotEqual, obj, guard->mir()->shape(), &bail);
-    bailoutFrom(&bail, guard->snapshot());
-}
-
-void
-CodeGeneratorARM::visitGuardObjectGroup(LGuardObjectGroup* guard)
-{
-    Register obj = ToRegister(guard->input());
-    Assembler::Condition cond =
-        guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
-    Label bail;
-    masm.branchTestObjGroup(cond, obj, guard->mir()->group(), &bail);
-    bailoutFrom(&bail, guard->snapshot());
-}
-
-void
-CodeGeneratorARM::visitGuardClass(LGuardClass* guard)
-{
-    Register obj = ToRegister(guard->input());
-    Register tmp = ToRegister(guard->tempInt());
-    Label bail;
-    masm.branchTestObjClass(Assembler::NotEqual, obj, tmp, guard->mir()->getClass(), &bail);
-    bailoutFrom(&bail, guard->snapshot());
-}
-
-void
 CodeGeneratorARM::generateInvalidateEpilogue()
 {
     // Ensure that there is enough space in the buffer for the OsiPoint patching
     // to occur. Otherwise, we could overwrite the invalidation epilogue.
     for (size_t i = 0; i < sizeof(void*); i += Assembler::NopSize())
         masm.nop();
 
     masm.bind(&invalidate_);
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -217,21 +217,16 @@ class CodeGeneratorARM : public CodeGene
 
   public:
     void visitBox(LBox* box);
     void visitBoxFloatingPoint(LBoxFloatingPoint* box);
     void visitUnbox(LUnbox* unbox);
     void visitValue(LValue* value);
     void visitDouble(LDouble* ins);
     void visitFloat32(LFloat32* ins);
-
-    void visitGuardShape(LGuardShape* guard);
-    void visitGuardObjectGroup(LGuardObjectGroup* guard);
-    void visitGuardClass(LGuardClass* guard);
-
     void visitNegI(LNegI* lir);
     void visitNegD(LNegD* lir);
     void visitNegF(LNegF* lir);
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* lir);
     void visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayElementBinopForEffect* lir);
     void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir);
--- a/js/src/jit/arm/LIR-arm.h
+++ b/js/src/jit/arm/LIR-arm.h
@@ -378,48 +378,16 @@ class LTableSwitchV : public LInstructio
     const LDefinition* tempFloat() {
         return getTemp(1);
     }
     const LDefinition* tempPointer() {
         return nullptr;
     }
 };
 
-class LGuardShape : public LInstructionHelper<0, 1, 0>
-{
-  public:
-    LIR_HEADER(GuardShape);
-
-    explicit LGuardShape(const LAllocation& in) {
-        setOperand(0, in);
-    }
-    const MGuardShape* mir() const {
-        return mir_->toGuardShape();
-    }
-    const LDefinition* tempInt() {
-        return getTemp(0);
-    }
-};
-
-class LGuardObjectGroup : public LInstructionHelper<0, 1, 0>
-{
-  public:
-    LIR_HEADER(GuardObjectGroup);
-
-    explicit LGuardObjectGroup(const LAllocation& in) {
-        setOperand(0, in);
-    }
-    const MGuardObjectGroup* mir() const {
-        return mir_->toGuardObjectGroup();
-    }
-    const LDefinition* tempInt() {
-        return getTemp(0);
-    }
-};
-
 class LMulI : public LBinaryMath<0>
 {
   public:
     LIR_HEADER(MulI);
 
     MMul* mir() {
         return mir_->toMul();
     }
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -476,38 +476,16 @@ LIRGeneratorARM::newLTableSwitch(const L
 LTableSwitchV*
 LIRGeneratorARM::newLTableSwitchV(MTableSwitch* tableswitch)
 {
     return new(alloc()) LTableSwitchV(useBox(tableswitch->getOperand(0)),
                                       temp(), tempDouble(), tableswitch);
 }
 
 void
-LIRGeneratorARM::visitGuardShape(MGuardShape* ins)
-{
-    MOZ_ASSERT(ins->object()->type() == MIRType::Object);
-
-    LGuardShape* guard = new(alloc()) LGuardShape(useRegister(ins->object()));
-    assignSnapshot(guard, ins->bailoutKind());
-    add(guard, ins);
-    redefine(ins, ins->object());
-}
-
-void
-LIRGeneratorARM::visitGuardObjectGroup(MGuardObjectGroup* ins)
-{
-    MOZ_ASSERT(ins->object()->type() == MIRType::Object);
-
-    LGuardObjectGroup* guard = new(alloc()) LGuardObjectGroup(useRegister(ins->object()));
-    assignSnapshot(guard, ins->bailoutKind());
-    add(guard, ins);
-    redefine(ins, ins->object());
-}
-
-void
 LIRGeneratorARM::lowerUrshD(MUrsh* mir)
 {
     MDefinition* lhs = mir->lhs();
     MDefinition* rhs = mir->rhs();
 
     MOZ_ASSERT(lhs->type() == MIRType::Int32);
     MOZ_ASSERT(rhs->type() == MIRType::Int32);
 
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -95,18 +95,16 @@ class LIRGeneratorARM : public LIRGenera
                                   MTableSwitch* ins);
     LTableSwitchV* newLTableSwitchV(MTableSwitch* ins);
 
   public:
     void visitBox(MBox* box) override;
     void visitUnbox(MUnbox* unbox) override;
     void visitReturn(MReturn* ret) override;
     void lowerPhi(MPhi* phi);
-    void visitGuardShape(MGuardShape* ins) override;
-    void visitGuardObjectGroup(MGuardObjectGroup* ins) override;
     void visitWasmSelect(MWasmSelect* ins) override;
     void visitWasmUnsignedToDouble(MWasmUnsignedToDouble* ins) override;
     void visitWasmUnsignedToFloat32(MWasmUnsignedToFloat32* ins) override;
     void visitWasmLoad(MWasmLoad* ins) override;
     void visitWasmStore(MWasmStore* ins) override;
     void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins) override;
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins) override;
     void visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins) override;
--- a/js/src/jit/arm64/CodeGenerator-arm64.cpp
+++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp
@@ -572,34 +572,16 @@ void
 CodeGeneratorARM64::storeElementTyped(const LAllocation* value, MIRType valueType,
                                       MIRType elementType, Register elements,
                                       const LAllocation* index)
 {
     MOZ_CRASH("CodeGeneratorARM64::storeElementTyped");
 }
 
 void
-CodeGeneratorARM64::visitGuardShape(LGuardShape* guard)
-{
-    MOZ_CRASH("visitGuardShape");
-}
-
-void
-CodeGeneratorARM64::visitGuardObjectGroup(LGuardObjectGroup* guard)
-{
-    MOZ_CRASH("visitGuardObjectGroup");
-}
-
-void
-CodeGeneratorARM64::visitGuardClass(LGuardClass* guard)
-{
-    MOZ_CRASH("CodeGeneratorARM64::visitGuardClass");
-}
-
-void
 CodeGeneratorARM64::visitInterruptCheck(LInterruptCheck* lir)
 {
     MOZ_CRASH("CodeGeneratorARM64::visitInterruptCheck");
 }
 
 void
 CodeGeneratorARM64::generateInvalidateEpilogue()
 {
--- a/js/src/jit/arm64/CodeGenerator-arm64.h
+++ b/js/src/jit/arm64/CodeGenerator-arm64.h
@@ -179,20 +179,16 @@ class CodeGeneratorARM64 : public CodeGe
     void visitFloat32(LFloat32* ins);
 
     void visitLoadSlotV(LLoadSlotV* load);
     void visitLoadSlotT(LLoadSlotT* load);
     void visitStoreSlotT(LStoreSlotT* load);
 
     void visitLoadElementT(LLoadElementT* load);
 
-    void visitGuardShape(LGuardShape* guard);
-    void visitGuardObjectGroup(LGuardObjectGroup* guard);
-    void visitGuardClass(LGuardClass* guard);
-
     void visitInterruptCheck(LInterruptCheck* lir);
 
     void visitNegI(LNegI* lir);
     void visitNegD(LNegD* lir);
     void visitNegF(LNegF* lir);
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir);
--- a/js/src/jit/arm64/LIR-arm64.h
+++ b/js/src/jit/arm64/LIR-arm64.h
@@ -303,50 +303,16 @@ class LTableSwitchV : public LInstructio
     const LDefinition* tempFloat() {
         return getTemp(1);
     }
     const LDefinition* tempPointer() {
         return nullptr;
     }
 };
 
-class LGuardShape : public LInstructionHelper<0, 1, 1>
-{
-  public:
-    LIR_HEADER(GuardShape);
-
-    LGuardShape(const LAllocation& in, const LDefinition& temp) {
-        setOperand(0, in);
-        setTemp(0, temp);
-    }
-    const MGuardShape* mir() const {
-        return mir_->toGuardShape();
-    }
-    const LDefinition* tempInt() {
-        return getTemp(0);
-    }
-};
-
-class LGuardObjectGroup : public LInstructionHelper<0, 1, 1>
-{
-  public:
-    LIR_HEADER(GuardObjectGroup);
-
-    LGuardObjectGroup(const LAllocation& in, const LDefinition& temp) {
-        setOperand(0, in);
-        setTemp(0, temp);
-    }
-    const MGuardObjectGroup* mir() const {
-        return mir_->toGuardObjectGroup();
-    }
-    const LDefinition* tempInt() {
-        return getTemp(0);
-    }
-};
-
 class LMulI : public LBinaryMath<0>
 {
   public:
     LIR_HEADER(MulI);
 
     MMul* mir() {
         return mir_->toMul();
     }
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -193,28 +193,16 @@ LIRGeneratorARM64::newLTableSwitch(const
 
 LTableSwitchV*
 LIRGeneratorARM64::newLTableSwitchV(MTableSwitch* tableswitch)
 {
     MOZ_CRASH("newLTableSwitchV");
 }
 
 void
-LIRGeneratorARM64::visitGuardShape(MGuardShape* ins)
-{
-    MOZ_CRASH("visitGuardShape");
-}
-
-void
-LIRGeneratorARM64::visitGuardObjectGroup(MGuardObjectGroup* ins)
-{
-    MOZ_CRASH("visitGuardObjectGroup");
-}
-
-void
 LIRGeneratorARM64::lowerUrshD(MUrsh* mir)
 {
     MOZ_CRASH("lowerUrshD");
 }
 
 void
 LIRGeneratorARM64::visitWasmNeg(MWasmNeg* ins)
 {
--- a/js/src/jit/arm64/Lowering-arm64.h
+++ b/js/src/jit/arm64/Lowering-arm64.h
@@ -96,18 +96,16 @@ class LIRGeneratorARM64 : public LIRGene
                                   const LDefinition& inputCopy,
                                   MTableSwitch* ins);
 
   public:
     void visitBox(MBox* box) override;
     void visitUnbox(MUnbox* unbox) override;
     void visitReturn(MReturn* ret) override;
     void lowerPhi(MPhi* phi);
-    void visitGuardShape(MGuardShape* ins) override;
-    void visitGuardObjectGroup(MGuardObjectGroup* ins) override;
     void visitWasmUnsignedToDouble(MWasmUnsignedToDouble* ins) override;
     void visitWasmUnsignedToFloat32(MWasmUnsignedToFloat32* ins) override;
     void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins) override;
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins) override;
     void visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins) override;
     void visitWasmAtomicExchangeHeap(MWasmAtomicExchangeHeap* ins) override;
     void visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins) override;
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins) override;
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
+++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
@@ -1764,46 +1764,16 @@ CodeGeneratorMIPSShared::visitNotF(LNotF
     FloatRegister in = ToFloatRegister(ins->input());
     Register dest = ToRegister(ins->output());
 
     masm.loadConstantFloat32(0.0f, ScratchFloat32Reg);
     masm.ma_cmp_set_float32(dest, in, ScratchFloat32Reg, Assembler::DoubleEqualOrUnordered);
 }
 
 void
-CodeGeneratorMIPSShared::visitGuardShape(LGuardShape* guard)
-{
-    Register obj = ToRegister(guard->input());
-    Label bail;
-    masm.branchTestObjShape(Assembler::NotEqual, obj, guard->mir()->shape(), &bail);
-    bailoutFrom(&bail, guard->snapshot());
-}
-
-void
-CodeGeneratorMIPSShared::visitGuardObjectGroup(LGuardObjectGroup* guard)
-{
-    Register obj = ToRegister(guard->input());
-    Assembler::Condition cond =
-        guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
-    Label bail;
-    masm.branchTestObjGroup(cond, obj, guard->mir()->group(), &bail);
-    bailoutFrom(&bail, guard->snapshot());
-}
-
-void
-CodeGeneratorMIPSShared::visitGuardClass(LGuardClass* guard)
-{
-    Register obj = ToRegister(guard->input());
-    Register tmp = ToRegister(guard->tempInt());
-    Label bail;
-    masm.branchTestObjClass(Assembler::NotEqual, obj, tmp, guard->mir()->getClass(), &bail);
-    bailoutFrom(&bail, guard->snapshot());
-}
-
-void
 CodeGeneratorMIPSShared::visitMemoryBarrier(LMemoryBarrier* ins)
 {
     masm.memoryBarrier(ins->type());
 }
 
 void
 CodeGeneratorMIPSShared::generateInvalidateEpilogue()
 {
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.h
+++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.h
@@ -201,21 +201,16 @@ class CodeGeneratorMIPSShared : public C
     void visitCopySignF(LCopySignF* ins);
 
   public:
     CodeGeneratorMIPSShared(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm);
 
     void visitValue(LValue* value);
     void visitDouble(LDouble* ins);
     void visitFloat32(LFloat32* ins);
-
-    void visitGuardShape(LGuardShape* guard);
-    void visitGuardObjectGroup(LGuardObjectGroup* guard);
-    void visitGuardClass(LGuardClass* guard);
-
     void visitNegI(LNegI* lir);
     void visitNegD(LNegD* lir);
     void visitNegF(LNegF* lir);
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitWasmLoad(LWasmLoad* ins);
     void visitWasmUnalignedLoad(LWasmUnalignedLoad* ins);
     void visitWasmStore(LWasmStore* ins);
--- a/js/src/jit/mips-shared/LIR-mips-shared.h
+++ b/js/src/jit/mips-shared/LIR-mips-shared.h
@@ -205,48 +205,16 @@ class LTableSwitchV : public LInstructio
     const LDefinition* tempFloat() {
         return getTemp(1);
     }
     const LDefinition* tempPointer() {
         return getTemp(2);
     }
 };
 
-class LGuardShape : public LInstructionHelper<0, 1, 0>
-{
-  public:
-    LIR_HEADER(GuardShape);
-
-    explicit LGuardShape(const LAllocation& in) {
-        setOperand(0, in);
-    }
-    const MGuardShape* mir() const {
-        return mir_->toGuardShape();
-    }
-    const LDefinition* tempInt() {
-        return getTemp(0);
-    }
-};
-
-class LGuardObjectGroup : public LInstructionHelper<0, 1, 0>
-{
-  public:
-    LIR_HEADER(GuardObjectGroup);
-
-    explicit LGuardObjectGroup(const LAllocation& in) {
-        setOperand(0, in);
-    }
-    const MGuardObjectGroup* mir() const {
-        return mir_->toGuardObjectGroup();
-    }
-    const LDefinition* tempInt() {
-        return getTemp(0);
-    }
-};
-
 class LMulI : public LBinaryMath<0>
 {
   public:
     LIR_HEADER(MulI);
 
     MMul* mir() {
         return mir_->toMul();
     }
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -283,38 +283,16 @@ LIRGeneratorMIPSShared::newLTableSwitch(
 LTableSwitchV*
 LIRGeneratorMIPSShared::newLTableSwitchV(MTableSwitch* tableswitch)
 {
     return new(alloc()) LTableSwitchV(useBox(tableswitch->getOperand(0)),
                                       temp(), tempDouble(), temp(), tableswitch);
 }
 
 void
-LIRGeneratorMIPSShared::visitGuardShape(MGuardShape* ins)
-{
-    MOZ_ASSERT(ins->object()->type() == MIRType::Object);
-
-    LGuardShape* guard = new(alloc()) LGuardShape(useRegister(ins->object()));
-    assignSnapshot(guard, ins->bailoutKind());
-    add(guard, ins);
-    redefine(ins, ins->object());
-}
-
-void
-LIRGeneratorMIPSShared::visitGuardObjectGroup(MGuardObjectGroup* ins)
-{
-    MOZ_ASSERT(ins->object()->type() == MIRType::Object);
-
-    LGuardObjectGroup* guard = new(alloc()) LGuardObjectGroup(useRegister(ins->object()));
-    assignSnapshot(guard, ins->bailoutKind());
-    add(guard, ins);
-    redefine(ins, ins->object());
-}
-
-void
 LIRGeneratorMIPSShared::lowerUrshD(MUrsh* mir)
 {
     MDefinition* lhs = mir->lhs();
     MDefinition* rhs = mir->rhs();
 
     MOZ_ASSERT(lhs->type() == MIRType::Int32);
     MOZ_ASSERT(rhs->type() == MIRType::Int32);
 
--- a/js/src/jit/mips-shared/Lowering-mips-shared.h
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.h
@@ -76,18 +76,16 @@ class LIRGeneratorMIPSShared : public LI
     void visitWasmSelect(MWasmSelect* ins);
 
     LTableSwitch* newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
                                   MTableSwitch* ins);
     LTableSwitchV* newLTableSwitchV(MTableSwitch* ins);
 
   public:
     void lowerPhi(MPhi* phi);
-    void visitGuardShape(MGuardShape* ins);
-    void visitGuardObjectGroup(MGuardObjectGroup* ins);
     void visitWasmUnsignedToDouble(MWasmUnsignedToDouble* ins);
     void visitWasmUnsignedToFloat32(MWasmUnsignedToFloat32* ins);
     void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
     void visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins);
     void visitWasmAtomicExchangeHeap(MWasmAtomicExchangeHeap* ins);
     void visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
--- a/js/src/jit/none/LIR-none.h
+++ b/js/src/jit/none/LIR-none.h
@@ -96,16 +96,14 @@ class LModPowTwoI : public LInstructionH
 {
 
   public:
     int32_t shift() { MOZ_CRASH(); }
     LModPowTwoI(const LAllocation& lhs, int32_t shift) { MOZ_CRASH(); }
     MMod* mir() const { MOZ_CRASH(); }
 };
 
-class LGuardShape : public LInstruction {};
-class LGuardObjectGroup : public LInstruction {};
 class LMulI : public LInstruction {};
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_none_LIR_none_h */
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -70,18 +70,16 @@ class LIRGeneratorNone : public LIRGener
     void lowerMulI(MMul*, MDefinition*, MDefinition*) { MOZ_CRASH(); }
     void lowerUDiv(MDiv*) { MOZ_CRASH(); }
     void lowerUMod(MMod*) { MOZ_CRASH(); }
     void visitBox(MBox* box) override { MOZ_CRASH(); }
     void visitUnbox(MUnbox* unbox) override { MOZ_CRASH(); }
     void visitReturn(MReturn* ret) override { MOZ_CRASH(); }
     void visitPowHalf(MPowHalf*) override { MOZ_CRASH(); }
     void visitWasmNeg(MWasmNeg*) override { MOZ_CRASH(); }
-    void visitGuardShape(MGuardShape* ins) override { MOZ_CRASH(); }
-    void visitGuardObjectGroup(MGuardObjectGroup* ins) override { MOZ_CRASH(); }
     void visitWasmUnsignedToDouble(MWasmUnsignedToDouble* ins) override { MOZ_CRASH(); }
     void visitWasmUnsignedToFloat32(MWasmUnsignedToFloat32* ins) override { MOZ_CRASH(); }
     void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins) override { MOZ_CRASH(); }
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins) override { MOZ_CRASH(); }
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins) override { MOZ_CRASH(); }
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins) override { MOZ_CRASH(); }
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins) override { MOZ_CRASH(); }
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins) override { MOZ_CRASH(); }
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -8036,16 +8036,42 @@ class LGuardObjectIdentity : public LIns
     const LAllocation* expected() {
         return getOperand(1);
     }
     const MGuardObjectIdentity* mir() const {
         return mir_->toGuardObjectIdentity();
     }
 };
 
+class LGuardShape : public LInstructionHelper<0, 1, 0>
+{
+  public:
+    LIR_HEADER(GuardShape)
+
+    explicit LGuardShape(const LAllocation& in) {
+        setOperand(0, in);
+    }
+    const MGuardShape* mir() const {
+        return mir_->toGuardShape();
+    }
+};
+
+class LGuardObjectGroup : public LInstructionHelper<0, 1, 0>
+{
+  public:
+    LIR_HEADER(GuardObjectGroup)
+
+    explicit LGuardObjectGroup(const LAllocation& in) {
+        setOperand(0, in);
+    }
+    const MGuardObjectGroup* mir() const {
+        return mir_->toGuardObjectGroup();
+    }
+};
+
 // Guard against an object's class.
 class LGuardClass : public LInstructionHelper<0, 1, 1>
 {
   public:
     LIR_HEADER(GuardClass)
 
     LGuardClass(const LAllocation& in, const LDefinition& temp) {
         setOperand(0, in);
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -2368,46 +2368,16 @@ CodeGeneratorX86Shared::visitNearbyIntF(
     FloatRegister input = ToFloatRegister(lir->input());
     FloatRegister output = ToFloatRegister(lir->output());
 
     RoundingMode roundingMode = lir->mir()->roundingMode();
     masm.vroundss(Assembler::ToX86RoundingMode(roundingMode), input, output, output);
 }
 
 void
-CodeGeneratorX86Shared::visitGuardShape(LGuardShape* guard)
-{
-    Register obj = ToRegister(guard->input());
-    Label bail;
-    masm.branchTestObjShape(Assembler::NotEqual, obj, guard->mir()->shape(), &bail);
-    bailoutFrom(&bail, guard->snapshot());
-}
-
-void
-CodeGeneratorX86Shared::visitGuardObjectGroup(LGuardObjectGroup* guard)
-{
-    Register obj = ToRegister(guard->input());
-    Assembler::Condition cond =
-        guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
-    Label bail;
-    masm.branchTestObjGroup(cond, obj, guard->mir()->group(), &bail);
-    bailoutFrom(&bail, guard->snapshot());
-}
-
-void
-CodeGeneratorX86Shared::visitGuardClass(LGuardClass* guard)
-{
-    Register obj = ToRegister(guard->input());
-    Register tmp = ToRegister(guard->tempInt());
-    Label bail;
-    masm.branchTestObjClass(Assembler::NotEqual, obj, tmp, guard->mir()->getClass(), &bail);
-    bailoutFrom(&bail, guard->snapshot());
-}
-
-void
 CodeGeneratorX86Shared::visitEffectiveAddress(LEffectiveAddress* ins)
 {
     const MEffectiveAddress* mir = ins->mir();
     Register base = ToRegister(ins->base());
     Register index = ToRegister(ins->index());
     Register output = ToRegister(ins->output());
     masm.leal(Operand(base, index, mir->scale(), mir->displacement()), output);
 }
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
@@ -236,19 +236,16 @@ class CodeGeneratorX86Shared : public Co
     void visitFloor(LFloor* lir);
     void visitFloorF(LFloorF* lir);
     void visitCeil(LCeil* lir);
     void visitCeilF(LCeilF* lir);
     void visitRound(LRound* lir);
     void visitRoundF(LRoundF* lir);
     void visitNearbyInt(LNearbyInt* lir);
     void visitNearbyIntF(LNearbyIntF* lir);
-    void visitGuardShape(LGuardShape* guard);
-    void visitGuardObjectGroup(LGuardObjectGroup* guard);
-    void visitGuardClass(LGuardClass* guard);
     void visitEffectiveAddress(LEffectiveAddress* ins);
     void visitUDivOrMod(LUDivOrMod* ins);
     void visitUDivOrModConstant(LUDivOrModConstant *ins);
     void visitWasmStackArg(LWasmStackArg* ins);
     void visitWasmStackArgI64(LWasmStackArgI64* ins);
     void visitWasmSelect(LWasmSelect* ins);
     void visitWasmReinterpret(LWasmReinterpret* lir);
     void visitMemoryBarrier(LMemoryBarrier* ins);
--- a/js/src/jit/x86-shared/LIR-x86-shared.h
+++ b/js/src/jit/x86-shared/LIR-x86-shared.h
@@ -299,42 +299,16 @@ class LTableSwitchV : public LInstructio
     const LDefinition* tempFloat() {
         return getTemp(1);
     }
     const LDefinition* tempPointer() {
         return getTemp(2);
     }
 };
 
-class LGuardShape : public LInstructionHelper<0, 1, 0>
-{
-  public:
-    LIR_HEADER(GuardShape)
-
-    explicit LGuardShape(const LAllocation& in) {
-        setOperand(0, in);
-    }
-    const MGuardShape* mir() const {
-        return mir_->toGuardShape();
-    }
-};
-
-class LGuardObjectGroup : public LInstructionHelper<0, 1, 0>
-{
-  public:
-    LIR_HEADER(GuardObjectGroup)
-
-    explicit LGuardObjectGroup(const LAllocation& in) {
-        setOperand(0, in);
-    }
-    const MGuardObjectGroup* mir() const {
-        return mir_->toGuardObjectGroup();
-    }
-};
-
 class LMulI : public LBinaryMath<0, 1>
 {
   public:
     LIR_HEADER(MulI)
 
     LMulI(const LAllocation& lhs, const LAllocation& rhs, const LAllocation& lhsCopy) {
         setOperand(0, lhs);
         setOperand(1, rhs);
--- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp
@@ -29,38 +29,16 @@ LIRGeneratorX86Shared::newLTableSwitch(c
 LTableSwitchV*
 LIRGeneratorX86Shared::newLTableSwitchV(MTableSwitch* tableswitch)
 {
     return new(alloc()) LTableSwitchV(useBox(tableswitch->getOperand(0)),
                                       temp(), tempDouble(), temp(), tableswitch);
 }
 
 void
-LIRGeneratorX86Shared::visitGuardShape(MGuardShape* ins)
-{
-    MOZ_ASSERT(ins->object()->type() == MIRType::Object);
-
-    LGuardShape* guard = new(alloc()) LGuardShape(useRegisterAtStart(ins->object()));
-    assignSnapshot(guard, ins->bailoutKind());
-    add(guard, ins);
-    redefine(ins, ins->object());
-}
-
-void
-LIRGeneratorX86Shared::visitGuardObjectGroup(MGuardObjectGroup* ins)
-{
-    MOZ_ASSERT(ins->object()->type() == MIRType::Object);
-
-    LGuardObjectGroup* guard = new(alloc()) LGuardObjectGroup(useRegisterAtStart(ins->object()));
-    assignSnapshot(guard, ins->bailoutKind());
-    add(guard, ins);
-    redefine(ins, ins->object());
-}
-
-void
 LIRGeneratorX86Shared::visitPowHalf(MPowHalf* ins)
 {
     MDefinition* input = ins->input();
     MOZ_ASSERT(input->type() == MIRType::Double);
     LPowHalfD* lir = new(alloc()) LPowHalfD(useRegisterAtStart(input));
     define(lir, ins);
 }
 
--- a/js/src/jit/x86-shared/Lowering-x86-shared.h
+++ b/js/src/jit/x86-shared/Lowering-x86-shared.h
@@ -18,18 +18,16 @@ class LIRGeneratorX86Shared : public LIR
     LIRGeneratorX86Shared(MIRGenerator* gen, MIRGraph& graph, LIRGraph& lirGraph)
       : LIRGeneratorShared(gen, graph, lirGraph)
     {}
 
     LTableSwitch* newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
                                   MTableSwitch* ins);
     LTableSwitchV* newLTableSwitchV(MTableSwitch* ins);
 
-    void visitGuardShape(MGuardShape* ins) override;
-    void visitGuardObjectGroup(MGuardObjectGroup* ins) override;
     void visitPowHalf(MPowHalf* ins) override;
     void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
                        MDefinition* rhs);
     void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input);
     void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
                      MDefinition* rhs);
 
     template<size_t Temps>
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2983,16 +2983,25 @@ SetJitExceptionHandler(JitExceptionHandl
  *
  * Do NOT pass a non-SavedFrame object here.
  *
  * The savedFrame and cx do not need to be in the same compartment.
  */
 extern JS_FRIEND_API(JSObject*)
 GetFirstSubsumedSavedFrame(JSContext* cx, JS::HandleObject savedFrame, JS::SavedFrameSelfHosted selfHosted);
 
+/**
+ * Get the first SavedFrame object in this SavedFrame stack whose principals are
+ * subsumed by the given |principals|. If there is no such frame, return nullptr.
+ *
+ * Do NOT pass a non-SavedFrame object here.
+ */
+extern JS_FRIEND_API(JSObject*)
+GetFirstSubsumedSavedFrame(JSContext* cx, JSPrincipals* principals, JS::HandleObject savedFrame, JS::SavedFrameSelfHosted selfHosted);
+
 extern JS_FRIEND_API(bool)
 ReportIsNotFunction(JSContext* cx, JS::HandleValue v);
 
 extern JS_FRIEND_API(JSObject*)
 ConvertArgsToArray(JSContext* cx, const JS::CallArgs& args);
 
 /**
  * Window and WindowProxy
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -9,16 +9,17 @@
  */
 
 #include "jsmath.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Unused.h"
+#include "mozilla/WrappingOperations.h"
 
 #include <algorithm>  // for std::max
 #include <fcntl.h>
 #ifdef XP_UNIX
 # include <unistd.h>
 #endif
 
 #include "fdlibm.h"
@@ -109,16 +110,17 @@ using mozilla::ExponentComponent;
 using mozilla::FloatingPoint;
 using mozilla::IsFinite;
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::IsNegativeZero;
 using mozilla::PositiveInfinity;
 using mozilla::NegativeInfinity;
+using mozilla::WrappingMultiply;
 using JS::ToNumber;
 using JS::GenericNaN;
 
 static const JSConstDoubleSpec math_constants[] = {
     {"E"      ,  M_E       },
     {"LOG2E"  ,  M_LOG2E   },
     {"LOG10E" ,  M_LOG10E  },
     {"LN2"    ,  M_LN2     },
@@ -477,26 +479,23 @@ js::math_floor(JSContext* cx, unsigned a
     }
 
     return math_floor_handle(cx, args[0], args.rval());
 }
 
 bool
 js::math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res)
 {
-    uint32_t a = 0, b = 0;
-    if (!lhs.isUndefined() && !ToUint32(cx, lhs, &a))
+    int32_t a = 0, b = 0;
+    if (!lhs.isUndefined() && !ToInt32(cx, lhs, &a))
         return false;
-    if (!rhs.isUndefined() && !ToUint32(cx, rhs, &b))
+    if (!rhs.isUndefined() && !ToInt32(cx, rhs, &b))
         return false;
 
-    uint32_t product = a * b;
-    res.setInt32(product > INT32_MAX
-                 ? int32_t(INT32_MIN + (product - INT32_MAX - 1))
-                 : int32_t(product));
+    res.setInt32(WrappingMultiply(a, b));
     return true;
 }
 
 bool
 js::math_imul(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
--- a/js/src/tests/non262/extensions/arrow-as-end-of-expression-closure.js
+++ b/js/src/tests/non262/extensions/arrow-as-end-of-expression-closure.js
@@ -11,30 +11,73 @@
 
 enableExpressionClosures();
 eval(`
 var ec1 = function() 0 ? 1 : a => {};
 assertEq(typeof ec1, "function");
 assertEq(typeof ec1(), "function");
 assertEq(ec1()(), undefined);
 
+function inFunction1()
+{
+  var ec1f = function() 0 ? 1 : a => {};
+  assertEq(typeof ec1f, "function");
+  assertEq(typeof ec1f(), "function");
+  assertEq(ec1f()(), undefined);
+}
+inFunction1();
+
 var ec2 = function() 0 ? 1 : a => {} // deliberately exercise ASI here
 assertEq(typeof ec2, "function");
 assertEq(typeof ec2(), "function");
 assertEq(ec2()(), undefined);
 
+function inFunction2()
+{
+  var ec2f = function() 0 ? 1 : a => {} // deliberately exercise ASI here
+  assertEq(typeof ec2f, "function");
+  assertEq(typeof ec2f(), "function");
+  assertEq(ec2f()(), undefined);
+}
+inFunction2();
+
 function ec3() 0 ? 1 : a => {} // exercise ASI here
 assertEq(typeof ec3(), "function");
 
+function inFunction3()
+{
+  function ec3f() 0 ? 1 : a => {} // exercise ASI here
+  assertEq(typeof ec3f(), "function");
+}
+inFunction3();
+
 function ec4() 0 ? 1 : a => {};
 assertEq(typeof ec4(), "function");
 
+function inFunction4()
+{
+  function ec4f() 0 ? 1 : a => {};
+  assertEq(typeof ec4f(), "function");
+}
+
 var needle = "@";
 var x = 42;
 var g = { test() { assertEq(true, false, "shouldn't be called"); } };
 
 function ec5() 0 ? 1 : a => {} // ASI
 /x/g.test((needle = "x"));
 assertEq(needle, "x");
+
+function inFunction5()
+{
+  var needle = "@";
+  var x = 42;
+  var g = { test() { assertEq(true, false, "shouldn't be called"); } };
+
+  function ec5f() 0 ? 1 : a => {} // ASI
+  /x/g.test((needle = "x"));
+  assertEq(needle, "x");
+}
+inFunction5();
 `);
 
 if (typeof reportCompare === "function")
   reportCompare(true, true);
--- a/js/src/util/DoubleToString.cpp
+++ b/js/src/util/DoubleToString.cpp
@@ -256,17 +256,17 @@ static uint32_t quorem2(Bigint* b, int32
 {
     ULong mask;
     ULong result;
     ULong* bx;
     ULong* bxe;
     int32_t w;
     int32_t n = k >> 5;
     k &= 0x1F;
-    mask = (1<<k) - 1;
+    mask = (ULong(1)<<k) - 1;
 
     w = b->wds - n;
     if (w <= 0)
         return 0;
     MOZ_ASSERT(w <= 2);
     bx = b->x;
     bxe = bx + n;
     result = *bxe >> k;
--- a/js/src/vm/MatchPairs.h
+++ b/js/src/vm/MatchPairs.h
@@ -46,17 +46,18 @@ struct MatchPair
     inline bool check() const {
         MOZ_ASSERT(limit >= start);
         MOZ_ASSERT_IF(start < 0, start == -1);
         MOZ_ASSERT_IF(limit < 0, limit == -1);
         return true;
     }
 };
 
-/* Base class for RegExp execution output. */
+// MachPairs is used as base class for VectorMatchPairs but can also be
+// stack-allocated (without a Vector) in JIT code.
 class MatchPairs
 {
   protected:
     /* Length of pairs_. */
     uint32_t pairCount_;
 
     /* Raw pointer into an allocated MatchPair buffer. */
     MatchPair* pairs_;
@@ -67,20 +68,16 @@ class MatchPairs
       : pairCount_(0), pairs_(nullptr)
     { }
 
   protected:
     /* Functions used by friend classes. */
     friend class RegExpShared;
     friend class RegExpStatics;
 
-    /* MatchPair buffer allocator: set pairs_ and pairCount_. */
-    virtual bool allocOrExpandArray(size_t pairCount) = 0;
-
-    bool initArrayFrom(MatchPairs& copyFrom);
     void forgetArray() { pairs_ = nullptr; }
 
     void checkAgainst(size_t inputLength) {
 #ifdef DEBUG
         for (size_t i = 0; i < pairCount_; i++) {
             const MatchPair& p = (*this)[i];
             MOZ_ASSERT(p.check());
             if (p.isUndefined())
@@ -109,44 +106,25 @@ class MatchPairs
         return pairs_[i];
     }
     MatchPair& operator[](size_t i) {
         MOZ_ASSERT(i < pairCount_);
         return pairs_[i];
     }
 };
 
-/* MatchPairs allocated into temporary storage, removed when out of scope. */
-class ScopedMatchPairs : public MatchPairs
-{
-    LifoAllocScope lifoScope_;
-
-  public:
-    /* Constructs an implicit LifoAllocScope. */
-    explicit ScopedMatchPairs(LifoAlloc* lifoAlloc)
-      : lifoScope_(lifoAlloc)
-    { }
-
-  protected:
-    bool allocOrExpandArray(size_t pairCount) override;
-};
-
-/*
- * MatchPairs allocated into permanent storage, for RegExpStatics.
- * The Vector of MatchPairs is reusable by Vector expansion.
- */
 class VectorMatchPairs : public MatchPairs
 {
     Vector<MatchPair, 10, SystemAllocPolicy> vec_;
 
-  public:
-    VectorMatchPairs() {
-        vec_.clear();
-    }
+  protected:
+    friend class RegExpShared;
+    friend class RegExpStatics;
 
-  protected:
-    friend class RegExpStatics;
-    bool allocOrExpandArray(size_t pairCount) override;
+    /* MatchPair buffer allocator: set pairs_ and pairCount_. */
+    bool allocOrExpandArray(size_t pairCount);
+
+    bool initArrayFrom(VectorMatchPairs& copyFrom);
 };
 
 } /* namespace js */
 
 #endif /* vm_MatchPairs_h */
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -65,51 +65,32 @@ js::RegExpAlloc(JSContext* cx, NewObject
                RegExpObject::lastIndexSlot());
 
     return regexp;
 }
 
 /* MatchPairs */
 
 bool
-MatchPairs::initArrayFrom(MatchPairs& copyFrom)
+VectorMatchPairs::initArrayFrom(VectorMatchPairs& copyFrom)
 {
     MOZ_ASSERT(copyFrom.pairCount() > 0);
 
     if (!allocOrExpandArray(copyFrom.pairCount()))
         return false;
 
     PodCopy(pairs_, copyFrom.pairs_, pairCount_);
 
     return true;
 }
 
 bool
-ScopedMatchPairs::allocOrExpandArray(size_t pairCount)
-{
-    /* Array expansion is forbidden, but array reuse is acceptable. */
-    if (pairCount_) {
-        MOZ_ASSERT(pairs_);
-        MOZ_ASSERT(pairCount_ == pairCount);
-        return true;
-    }
-
-    MOZ_ASSERT(!pairs_);
-    pairs_ = (MatchPair*)lifoScope_.alloc().alloc(sizeof(MatchPair) * pairCount);
-    if (!pairs_)
-        return false;
-
-    pairCount_ = pairCount;
-    return true;
-}
-
-bool
 VectorMatchPairs::allocOrExpandArray(size_t pairCount)
 {
-    if (!vec_.resizeUninitialized(sizeof(MatchPair) * pairCount))
+    if (!vec_.resizeUninitialized(pairCount))
         return false;
 
     pairs_ = &vec_[0];
     pairCount_ = pairCount;
     return true;
 }
 
 /* RegExpObject */
@@ -1077,17 +1058,17 @@ RegExpShared::compileIfNecessary(JSConte
 {
     if (re->isCompiled(mode, input->hasLatin1Chars(), force))
         return true;
     return compile(cx, re, input, mode, force);
 }
 
 /* static */ RegExpRunStatus
 RegExpShared::execute(JSContext* cx, MutableHandleRegExpShared re, HandleLinearString input,
-                      size_t start, MatchPairs* matches, size_t* endIndex)
+                      size_t start, VectorMatchPairs* matches, size_t* endIndex)
 {
     MOZ_ASSERT_IF(matches, !endIndex);
     MOZ_ASSERT_IF(!matches, endIndex);
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
 
     CompilationMode mode = matches ? Normal : MatchOnly;
 
     /* Compile the code at point-of-use. */
--- a/js/src/vm/RegExpShared.h
+++ b/js/src/vm/RegExpShared.h
@@ -23,20 +23,20 @@
 #include "js/UbiNode.h"
 #include "js/Vector.h"
 #include "vm/ArrayObject.h"
 #include "vm/JSAtom.h"
 
 namespace js {
 
 class ArrayObject;
-class MatchPairs;
 class RegExpCompartment;
 class RegExpShared;
 class RegExpStatics;
+class VectorMatchPairs;
 
 using RootedRegExpShared = JS::Rooted<RegExpShared*>;
 using HandleRegExpShared = JS::Handle<RegExpShared*>;
 using MutableHandleRegExpShared = JS::MutableHandle<RegExpShared*>;
 
 enum RegExpFlag : uint8_t
 {
     IgnoreCaseFlag  = 0x01,
@@ -154,17 +154,17 @@ class RegExpShared : public gc::TenuredC
 
   public:
     ~RegExpShared() = delete;
 
     // Execute this RegExp on input starting from searchIndex, filling in
     // matches if specified and otherwise only determining if there is a match.
     static RegExpRunStatus execute(JSContext* cx, MutableHandleRegExpShared res,
                                    HandleLinearString input, size_t searchIndex,
-                                   MatchPairs* matches, size_t* endIndex);
+                                   VectorMatchPairs* matches, size_t* endIndex);
 
     // Register a table with this RegExpShared, and take ownership.
     bool addTable(JitCodeTable table) {
         return tables.append(Move(table));
     }
 
     /* Accessors */
 
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -57,17 +57,18 @@ class RegExpStatics
      */
     bool makeMatch(JSContext* cx, size_t pairNum, MutableHandleValue out);
     bool createDependent(JSContext* cx, size_t start, size_t end, MutableHandleValue out);
 
   public:
     /* Mutators. */
     inline void updateLazily(JSContext* cx, JSLinearString* input,
                              RegExpShared* shared, size_t lastIndex);
-    inline bool updateFromMatchPairs(JSContext* cx, JSLinearString* input, MatchPairs& newPairs);
+    inline bool updateFromMatchPairs(JSContext* cx, JSLinearString* input,
+                                     VectorMatchPairs& newPairs);
 
     inline void clear();
 
     /* Corresponds to JSAPI functionality to set the pending RegExp input. */
     void reset(JSString* newInput) {
         clear();
         pendingInput = newInput;
         checkInvariants();
@@ -246,17 +247,18 @@ RegExpStatics::updateLazily(JSContext* c
 
     lazySource = shared->source;
     lazyFlags = shared->flags;
     lazyIndex = lastIndex;
     pendingLazyEvaluation = 1;
 }
 
 inline bool
-RegExpStatics::updateFromMatchPairs(JSContext* cx, JSLinearString* input, MatchPairs& newPairs)
+RegExpStatics::updateFromMatchPairs(JSContext* cx, JSLinearString* input,
+                                    VectorMatchPairs& newPairs)
 {
     MOZ_ASSERT(input);
 
     /* Unset all lazy state. */
     pendingLazyEvaluation = false;
     this->lazySource = nullptr;
     this->lazyIndex = size_t(-1);
 
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -567,55 +567,89 @@ SavedFrameSubsumedByCaller(JSContext* cx
         return cx->runningWithTrustedPrincipals();
     if (framePrincipals == &ReconstructedSavedFramePrincipals::IsNotSystem)
         return true;
 
     return subsumes(currentCompartmentPrincipals, framePrincipals);
 }
 
 // Return the first SavedFrame in the chain that starts with |frame| whose
-// principals are subsumed by |principals|, according to |subsumes|. If there is
-// no such frame, return nullptr. |skippedAsync| is set to true if any of the
-// skipped frames had the |asyncCause| property set, otherwise it is explicitly
-// set to false.
+// for which the given match function returns true. If there is no such frame,
+// return nullptr. |skippedAsync| is set to true if any of the skipped frames
+// had the |asyncCause| property set, otherwise it is explicitly set to false.
+template<typename Matcher>
 static SavedFrame*
-GetFirstSubsumedFrame(JSContext* cx, HandleSavedFrame frame, JS::SavedFrameSelfHosted selfHosted,
-                      bool& skippedAsync)
+GetFirstMatchedFrame(JSContext* cx, Matcher& matches,
+                     HandleSavedFrame frame, JS::SavedFrameSelfHosted selfHosted,
+                     bool& skippedAsync)
 {
     skippedAsync = false;
 
     RootedSavedFrame rootedFrame(cx, frame);
     while (rootedFrame) {
         if ((selfHosted == JS::SavedFrameSelfHosted::Include ||
              !rootedFrame->isSelfHosted(cx)) &&
-            SavedFrameSubsumedByCaller(cx, rootedFrame))
+            matches(cx, rootedFrame))
         {
             return rootedFrame;
         }
 
         if (rootedFrame->getAsyncCause())
             skippedAsync = true;
 
         rootedFrame = rootedFrame->getParent();
     }
 
     return nullptr;
 }
 
+// Return the first SavedFrame in the chain that starts with |frame| whose
+// principals are subsumed by the principals of the context's current
+// compartment, according to |subsumes|. If there is no such frame, return
+// nullptr. |skippedAsync| is set to true if any of the skipped frames had the
+// |asyncCause| property set, otherwise it is explicitly set to false.
+static SavedFrame*
+GetFirstSubsumedFrame(JSContext* cx, HandleSavedFrame frame, JS::SavedFrameSelfHosted selfHosted,
+                      bool& skippedAsync)
+{
+    return GetFirstMatchedFrame(cx, SavedFrameSubsumedByCaller, frame, selfHosted, skippedAsync);
+}
+
 JS_FRIEND_API(JSObject*)
 GetFirstSubsumedSavedFrame(JSContext* cx, HandleObject savedFrame,
                            JS::SavedFrameSelfHosted selfHosted)
 {
     if (!savedFrame)
         return nullptr;
     bool skippedAsync;
     RootedSavedFrame frame(cx, &savedFrame->as<SavedFrame>());
     return GetFirstSubsumedFrame(cx, frame, selfHosted, skippedAsync);
 }
 
+JS_FRIEND_API(JSObject*)
+GetFirstSubsumedSavedFrame(JSContext* cx, JSPrincipals* principals,
+                           HandleObject savedFrame,
+                           JS::SavedFrameSelfHosted selfHosted)
+{
+    if (!savedFrame)
+        return nullptr;
+
+    auto subsumes = cx->runtime()->securityCallbacks->subsumes;
+    if (!subsumes)
+        return nullptr;
+
+    auto matcher = [&](JSContext* cx, HandleSavedFrame frame) -> bool {
+        return subsumes(principals, frame->getPrincipals());
+    };
+
+    bool skippedAsync;
+    RootedSavedFrame frame(cx, &savedFrame->as<SavedFrame>());
+    return GetFirstMatchedFrame(cx, matcher, frame, selfHosted, skippedAsync);
+}
+
 static MOZ_MUST_USE bool
 SavedFrame_checkThis(JSContext* cx, CallArgs& args, const char* fnName,
                      MutableHandleObject frame)
 {
     const Value& thisValue = args.thisv();
 
     if (!thisValue.isObject()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -7205,17 +7205,17 @@ ParseFunction(ModuleValidator& m, ParseN
         return false;
     funbox->initWithEnclosingParseContext(outerpc, frontend::Statement);
 
     Directives newDirectives = directives;
     SourceParseContext funpc(&m.parser(), funbox, &newDirectives);
     if (!funpc.init())
         return false;
 
-    if (!m.parser().functionFormalParametersAndBody(InAllowed, YieldIsName, fn, Statement)) {
+    if (!m.parser().functionFormalParametersAndBody(InAllowed, YieldIsName, &fn, Statement)) {
         if (anyChars.hadError() || directives == newDirectives)
             return false;
 
         return m.fail(fn, "encountered new directive in function");
     }
 
     MOZ_ASSERT(!anyChars.hadError());
     MOZ_ASSERT(directives == newDirectives);
--- a/js/xpconnect/idl/xpccomponents.idl
+++ b/js/xpconnect/idl/xpccomponents.idl
@@ -144,18 +144,22 @@ interface nsIXPCComponents_Utils : nsISu
      *
      * It will report a JS Error object to the JS console, and return. It
      * is meant for use in exception handler blocks which want to "eat"
      * an exception, but still want to report it to the console.
      *
      * It must be called with one param, usually an object which was caught by
      * an exception handler.  If it is not a JS error object, the parameter
      * is converted to a string and reported as a new error.
+     *
+     * If called with two parameters, and the first parameter is not an
+     * object, the second parameter is used as the stack for the error report.
      */
-    [implicit_jscontext] void reportError(in jsval error);
+    [implicit_jscontext]
+    void reportError(in jsval error, [optional] in jsval stack);
 
     readonly attribute nsIXPCComponents_utils_Sandbox Sandbox;
 
     /*
      * evalInSandbox is designed to be called from JavaScript only.
      *
      * evalInSandbox evaluates the provided source string in the given sandbox.
      * It returns the result of the evaluation to the caller.
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -2051,17 +2051,17 @@ nsXPCComponents_Utils::GetSandbox(nsIXPC
         mSandbox = NewSandboxConstructor();
 
     nsCOMPtr<nsIXPCComponents_utils_Sandbox> rval = mSandbox;
     rval.forget(aSandbox);
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsXPCComponents_Utils::ReportError(HandleValue error, JSContext* cx)
+nsXPCComponents_Utils::ReportError(HandleValue error, HandleValue stack, JSContext* cx)
 {
     // This function shall never fail! Silently eat any failure conditions.
 
     nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
     if (!console)
         return NS_OK;
 
     nsGlobalWindowInner* globalWin = CurrentWindowOrNull(cx);
@@ -2077,29 +2077,54 @@ nsXPCComponents_Utils::ReportError(Handl
         JS::RootedObject stackVal(cx,
           FindExceptionStackForConsoleReport(win, error));
         if (stackVal) {
             scripterr = new nsScriptErrorWithStack(stackVal);
         }
     }
 
     nsString fileName;
-    int32_t lineNo = 0;
+    uint32_t lineNo = 0;
 
     if (!scripterr) {
-        nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack();
-        if (frame) {
-            frame->GetFilename(cx, fileName);
-            lineNo = frame->GetLineNumber(cx);
-            JS::Rooted<JS::Value> stack(cx);
-            nsresult rv = frame->GetNativeSavedFrame(&stack);
-            if (NS_SUCCEEDED(rv) && stack.isObject()) {
-              JS::Rooted<JSObject*> stackObj(cx, &stack.toObject());
-              scripterr = new nsScriptErrorWithStack(stackObj);
+        RootedObject stackObj(cx);
+        if (stack.isObject()) {
+            if (!JS::IsSavedFrame(&stack.toObject())) {
+                return NS_ERROR_INVALID_ARG;
+            }
+
+            stackObj = &stack.toObject();
+
+            if (GetSavedFrameLine(cx, stackObj, &lineNo) != SavedFrameResult::Ok) {
+                JS_ClearPendingException(cx);
             }
+
+            RootedString source(cx);
+            nsAutoJSString str;
+            if (GetSavedFrameSource(cx, stackObj, &source) == SavedFrameResult::Ok &&
+                str.init(cx, source)) {
+                fileName = str;
+            } else {
+                JS_ClearPendingException(cx);
+            }
+        } else {
+            nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack();
+            if (frame) {
+                frame->GetFilename(cx, fileName);
+                lineNo = frame->GetLineNumber(cx);
+                JS::Rooted<JS::Value> stack(cx);
+                nsresult rv = frame->GetNativeSavedFrame(&stack);
+                if (NS_SUCCEEDED(rv) && stack.isObject()) {
+                  stackObj = &stack.toObject();
+                }
+            }
+        }
+
+        if (stackObj) {
+            scripterr = new nsScriptErrorWithStack(stackObj);
         }
     }
 
     if (!scripterr) {
         scripterr = new nsScriptError();
     }
 
     if (err) {
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2892,16 +2892,17 @@ XPCJSRuntime::Initialize(JSContext* cx)
     mPrevGCSliceCallback = JS::SetGCSliceCallback(cx, GCSliceCallback);
     mPrevDoCycleCollectionCallback = JS::SetDoCycleCollectionCallback(cx,
             DoCycleCollectionCallback);
     JS_AddFinalizeCallback(cx, FinalizeCallback, nullptr);
     JS_AddWeakPointerZonesCallback(cx, WeakPointerZonesCallback, this);
     JS_AddWeakPointerCompartmentCallback(cx, WeakPointerCompartmentCallback, this);
     JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
     js::SetPreserveWrapperCallback(cx, PreserveWrapper);
+    JS_InitReadPrincipalsCallback(cx, nsJSPrincipals::ReadPrincipals);
     JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback);
     JS_SetSetUseCounterCallback(cx, SetUseCounterCallback);
     js::SetWindowProxyClass(cx, &OuterWindowProxyClass);
     js::SetXrayJitInfo(&gXrayJitInfo);
     JS::SetProcessLargeAllocationFailureCallback(OnLargeAllocationFailureCallback);
 
     // The JS engine needs to keep the source code around in order to implement
     // Function.prototype.toSource(). It'd be nice to not have to do this for
--- a/js/xpconnect/src/xpc.msg
+++ b/js/xpconnect/src/xpc.msg
@@ -229,12 +229,9 @@ XPC_MSG_DEF(NS_ERROR_DOM_NOT_FOUND_ERR  
 XPC_MSG_DEF(NS_ERROR_DOM_NOT_ALLOWED_ERR              , "The request is not allowed.")
 
 /* Codes related to the URIClassifier service */
 XPC_MSG_DEF(NS_ERROR_MALWARE_URI                      , "The URI is malware")
 XPC_MSG_DEF(NS_ERROR_PHISHING_URI                     , "The URI is phishing")
 XPC_MSG_DEF(NS_ERROR_TRACKING_URI                     , "The URI is tracking")
 XPC_MSG_DEF(NS_ERROR_UNWANTED_URI                     , "The URI is unwanted")
 XPC_MSG_DEF(NS_ERROR_BLOCKED_URI                      , "The URI is blocked")
-XPC_MSG_DEF(NS_ERROR_HARMFUL_URI                      , "The URI is harmful")
-
-/* nsISocketTransport status codes */
-XPC_MSG_DEF(NS_NET_STATUS_WAITING_FOR                 , "Waiting for response")
+XPC_MSG_DEF(NS_ERROR_HARMFUL_URI                      , "The URI is harmful")
\ No newline at end of file
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -672,20 +672,26 @@ AreNonLocalConnectionsDisabled()
         }
     }
     return disabledForTest;
 }
 
 inline bool
 IsInAutomation()
 {
-    const char* prefName =
-      "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer";
-    return mozilla::Preferences::GetBool(prefName) &&
-        AreNonLocalConnectionsDisabled();
+    static bool sAutomationPrefIsSet;
+    static bool sPrefCacheAdded = false;
+    if (!sPrefCacheAdded) {
+        mozilla::Preferences::AddBoolVarCache(
+          &sAutomationPrefIsSet,
+          "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer",
+          false);
+        sPrefCacheAdded = true;
+    }
+    return sAutomationPrefIsSet && AreNonLocalConnectionsDisabled();
 }
 
 void
 CreateCooperativeContext();
 
 void
 DestroyCooperativeContext();
 
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_getCallerLocation.js
@@ -0,0 +1,86 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+Cu.importGlobalProperties(["ChromeUtils"]);
+
+ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
+
+add_task(async function() {
+  const sandbox = Cu.Sandbox("http://example.com/");
+
+  function foo() {
+    return bar();
+  }
+
+  function bar() {
+    return baz();
+  }
+
+  function baz() {
+    return ChromeUtils.getCallerLocation(Cu.getObjectPrincipal(sandbox));
+  }
+
+  Cu.evalInSandbox(`
+    function it() {
+      // Use map() to throw a self-hosted frame on the stack, which we
+      // should filter out.
+      return [0].map(foo)[0];
+    }
+    function thing() {
+      return it();
+    }
+  `, sandbox, undefined, "thing.js");
+
+  Cu.exportFunction(foo, sandbox, {defineAs: "foo"});
+
+  let frame = sandbox.thing();
+
+  equal(frame.source, "thing.js", "Frame source");
+  equal(frame.line, 5, "Frame line");
+  equal(frame.column, 14, "Frame column");
+  equal(frame.functionDisplayName, "it", "Frame function name");
+  equal(frame.parent, null, "Frame parent");
+
+  equal(String(frame), "it@thing.js:5:14\n", "Stringified frame");
+
+
+  // reportError
+
+  let {messages} = await AddonTestUtils.promiseConsoleOutput(() => {
+    Cu.reportError("Meh", frame);
+  });
+
+  let [msg] = messages.filter(m => m.message.includes("Meh"));
+
+  equal(msg.stack, frame, "reportError stack frame");
+  equal(msg.message, '[JavaScript Error: "Meh" {file: "thing.js" line: 5}]\nit@thing.js:5:14\n');
+
+  Assert.throws(() => { Cu.reportError("Meh", {}); },
+                err => err.result == Cr.NS_ERROR_INVALID_ARG,
+                "reportError should throw when passed a non-SavedFrame object");
+
+
+  // createError
+
+  Assert.throws(() => { ChromeUtils.createError("Meh", {}); },
+                err => err.result == Cr.NS_ERROR_INVALID_ARG,
+                "createError should throw when passed a non-SavedFrame object");
+
+  let cloned = Cu.cloneInto(frame, sandbox);
+  let error = ChromeUtils.createError("Meh", cloned);
+
+  equal(String(cloned), String(frame),
+        "Cloning a SavedStack preserves its stringification");
+
+  equal(Cu.getGlobalForObject(error), sandbox,
+        "createError creates errors in the global of the SavedFrame");
+  equal(error.stack, String(cloned),
+        "createError creates errors with the correct stack");
+
+  equal(error.message, "Meh", "Error message");
+  equal(error.fileName, "thing.js", "Error filename");
+  equal(error.lineNumber, 5, "Error line");
+  equal(error.columnNumber, 14, "Error column");
+});
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -67,16 +67,17 @@ support-files =
 [test_classesByID_instanceof.js]
 [test_compileScript.js]
 [test_deepFreezeClone.js]
 [test_defineModuleGetter.js]
 [test_file.js]
 [test_blob.js]
 [test_blob2.js]
 [test_file2.js]
+[test_getCallerLocation.js]
 [test_import.js]
 [test_import_fail.js]
 [test_interposition.js]
 [test_isModuleLoaded.js]
 [test_js_weak_references.js]
 [test_onGarbageCollection-01.js]
 head = head_ongc.js
 [test_onGarbageCollection-02.js]
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -4286,22 +4286,21 @@ PresShell::DoFlushPendingNotifications(m
     // We suppressed this flush either due to it not being safe to flush,
     // or due to SuppressInterruptibleReflows().  Either way, the
     // mNeedLayoutFlush flag needs to be re-set.
     SetNeedLayoutFlush();
   }
 }
 
 void
-PresShell::CharacterDataChanged(nsIDocument* aDocument,
-                                nsIContent* aContent,
+PresShell::CharacterDataChanged(nsIContent* aContent,
                                 const CharacterDataChangeInfo& aInfo)
 {
   NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged");
-  NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
+  NS_PRECONDITION(aContent->OwnerDoc() == mDocument, "Unexpected document");
 
   nsAutoCauseReflowNotifier crNotifier(this);
 
   mPresContext->RestyleManager()->CharacterDataChanged(aContent, aInfo);
   mFrameConstructor->CharacterDataChanged(aContent, aInfo);
   VERIFY_STYLE_TREE;
 }
 
@@ -4352,162 +4351,129 @@ PresShell::DocumentStatesChanged(nsIDocu
   if (aStateMask.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
     if (nsIFrame* root = mFrameConstructor->GetRootFrame()) {
       root->SchedulePaint();
     }
   }
 }
 
 void
-PresShell::AttributeWillChange(nsIDocument* aDocument,
-                               Element*     aElement,
-                               int32_t      aNameSpaceID,
-                               nsAtom*     aAttribute,
-                               int32_t      aModType,
+PresShell::AttributeWillChange(Element* aElement,
+                               int32_t aNameSpaceID,
+                               nsAtom* aAttribute,
+                               int32_t aModType,
                                const nsAttrValue* aNewValue)
 {
   NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeWillChange");
-  NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
+  NS_PRECONDITION(aElement->OwnerDoc() == mDocument, "Unexpected document");
 
   // XXXwaterson it might be more elegant to wait until after the
   // initial reflow to begin observing the document. That would
   // squelch any other inappropriate notifications as well.
   if (mDidInitialize) {
     nsAutoCauseReflowNotifier crNotifier(this);
     mPresContext->RestyleManager()->AttributeWillChange(aElement, aNameSpaceID,
                                                         aAttribute, aModType,
                                                         aNewValue);
     VERIFY_STYLE_TREE;
   }
 }
 
 void
-PresShell::AttributeChanged(nsIDocument* aDocument,
-                            Element*     aElement,
-                            int32_t      aNameSpaceID,
-                            nsAtom*     aAttribute,
-                            int32_t      aModType,
+PresShell::AttributeChanged(Element* aElement,
+                            int32_t aNameSpaceID,
+                            nsAtom* aAttribute,
+                            int32_t aModType,
                             const nsAttrValue* aOldValue)
 {
   NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeChanged");
-  NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
+  NS_PRECONDITION(aElement->OwnerDoc() == mDocument, "Unexpected document");
 
   // XXXwaterson it might be more elegant to wait until after the
   // initial reflow to begin observing the document. That would
   // squelch any other inappropriate notifications as well.
   if (mDidInitialize) {
     nsAutoCauseReflowNotifier crNotifier(this);
     mPresContext->RestyleManager()->AttributeChanged(aElement, aNameSpaceID,
                                                      aAttribute, aModType,
                                                      aOldValue);
     VERIFY_STYLE_TREE;
   }
 }
 
-// nsIMutationObserver callbacks have this terrible API where aContainer is
-// null in the case that the container is the document (since nsIDocument is
-// not an nsIContent), and callees are supposed to figure this out and use the
-// document instead. It would be nice to fix that API to just pass a single
-// nsINode* parameter in place of the nsIDocument*, nsIContent* pair, but
-// there are quite a lot of consumers. So we fix things up locally with this
-// routine for now.
-static inline nsINode*
-RealContainer(nsIDocument* aDocument, nsIContent* aContainer, nsIContent* aContent)
-{
-  MOZ_ASSERT(aDocument);
-  MOZ_ASSERT(!aContainer || aContainer->OwnerDoc() == aDocument);
-  MOZ_ASSERT(aContent->OwnerDoc() == aDocument);
-  MOZ_ASSERT(aContainer || aContent->GetParentNode() == aDocument);
-  if (!aContainer) {
-    return aDocument;
-  }
-  return aContainer;
-}
-
-void
-PresShell::ContentAppended(nsIDocument *aDocument,
-                           nsIContent* aContainer,
-                           nsIContent* aFirstNewContent)
+void
+PresShell::ContentAppended(nsIContent* aFirstNewContent)
 {
   NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentAppended");
-  NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
+  NS_PRECONDITION(aFirstNewContent->OwnerDoc() == mDocument,
+                  "Unexpected document");
 
   // We never call ContentAppended with a document as the container, so we can
   // assert that we have an nsIContent container.
-  MOZ_ASSERT(aContainer);
-  MOZ_ASSERT(aContainer->IsElement() ||
-             aContainer->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT));
+  nsIContent* container = aFirstNewContent->GetParent();
+  MOZ_ASSERT(container);
+  MOZ_ASSERT(container->IsElement() || container->IsShadowRoot());
   if (!mDidInitialize) {
     return;
   }
 
   nsAutoCauseReflowNotifier crNotifier(this);
 
   // Call this here so it only happens for real content mutations and
   // not cases when the frame constructor calls its own methods to force
   // frame reconstruction.
-  mPresContext->RestyleManager()->ContentAppended(aContainer, aFirstNewContent);
+  mPresContext->RestyleManager()->ContentAppended(container, aFirstNewContent);
 
   mFrameConstructor->ContentAppended(
-      aContainer,
+      container,
       aFirstNewContent,
       nsCSSFrameConstructor::InsertionKind::Async);
 
   VERIFY_STYLE_TREE;
 }
 
 void
-PresShell::ContentInserted(nsIDocument* aDocument,
-                           nsIContent*  aMaybeContainer,
-                           nsIContent*  aChild)
+PresShell::ContentInserted(nsIContent* aChild)
 {
   NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentInserted");
-  NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
-  nsINode* container = RealContainer(aDocument, aMaybeContainer, aChild);
+  NS_PRECONDITION(aChild->OwnerDoc() == mDocument, "Unexpected document");
+  nsINode* container = aChild->GetParentNode();
 
   if (!mDidInitialize) {
     return;
   }
 
   nsAutoCauseReflowNotifier crNotifier(this);
 
   // Call this here so it only happens for real content mutations and
   // not cases when the frame constructor calls its own methods to force
   // frame reconstruction.
   mPresContext->RestyleManager()->ContentInserted(container, aChild);
 
   mFrameConstructor->ContentInserted(
-      aMaybeContainer,
+      aChild->GetParent(),
       aChild,
       nullptr,
       nsCSSFrameConstructor::InsertionKind::Async);
 
   VERIFY_STYLE_TREE;
 }
 
 void
-PresShell::ContentRemoved(nsIDocument* aDocument,
-                          nsIContent* aMaybeContainer,
-                          nsIContent* aChild,
-                          nsIContent* aPreviousSibling)
+PresShell::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
 {
   NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentRemoved");
-  NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
-  nsINode* container = RealContainer(aDocument, aMaybeContainer, aChild);
+  NS_PRECONDITION(aChild->OwnerDoc() == mDocument, "Unexpected document");
+  nsINode* container = aChild->GetParentNode();
 
   // Notify the ESM that the content has been removed, so that
   // it can clean up any state related to the content.
 
-  // XXX_jwir3: There is no null check for aDocument necessary, since, even
-  //            though by nsIMutationObserver, aDocument could be null, the
-  //            precondition check that mDocument == aDocument ensures that
-  //            aDocument will not be null (since mDocument can't be null unless
-  //            we're still intializing).
   mPresContext->EventStateManager()
-    ->ContentRemoved(aDocument, aMaybeContainer, aChild);
+    ->ContentRemoved(mDocument, aChild->GetParent(), aChild);
 
   nsAutoCauseReflowNotifier crNotifier(this);
 
   // Call this here so it only happens for real content mutations and
   // not cases when the frame constructor calls its own methods to force
   // frame reconstruction.
   nsIContent* oldNextSibling = nullptr;
 
@@ -4519,21 +4485,22 @@ PresShell::ContentRemoved(nsIDocument* a
       : container->GetFirstChild();
   }
 
   mPresContext->RestyleManager()->ContentRemoved(container, aChild, oldNextSibling);
 
   // After removing aChild from tree we should save information about live ancestor
   if (mPointerEventTarget &&
       nsContentUtils::ContentIsDescendantOf(mPointerEventTarget, aChild)) {
-    mPointerEventTarget = aMaybeContainer;
-  }
-
-  mFrameConstructor->ContentRemoved(aMaybeContainer, aChild, oldNextSibling,
-                                    nsCSSFrameConstructor::REMOVE_CONTENT);
+    mPointerEventTarget = aChild->GetParent();
+  }
+
+  mFrameConstructor->ContentRemoved(
+      aChild->GetParent(), aChild, oldNextSibling,
+      nsCSSFrameConstructor::REMOVE_CONTENT);
 
   VERIFY_STYLE_TREE;
 }
 
 void
 PresShell::NotifyCounterStylesAreDirty()
 {
   nsAutoCauseReflowNotifier reflowNotifier(this);
@@ -6782,17 +6749,17 @@ nsIFrame* GetNearestFrameContainingPresS
   if (view) {
     frame = view->GetFrame();
   }
 
   return frame;
 }
 
 static bool
-FlushThrottledStyles(nsIDocument *aDocument, void *aData)
+FlushThrottledStyles(nsIDocument* aDocument, void *aData)
 {
   nsIPresShell* shell = aDocument->GetShell();
   if (shell && shell->IsVisible()) {
     nsPresContext* presContext = shell->GetPresContext();
     if (presContext) {
       presContext->RestyleManager()->UpdateOnlyAnimationStyles();
     }
   }
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -1461,42 +1461,34 @@ nsTextControlFrame::EditorInitializer::R
   mFrame->FinishedInitializer();
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS(nsTextControlFrame::nsAnonDivObserver, nsIMutationObserver)
 
 void
 nsTextControlFrame::nsAnonDivObserver::CharacterDataChanged(
-  nsIDocument* aDocument,
   nsIContent* aContent,
   const CharacterDataChangeInfo&)
 {
   mFrame.ClearCachedValue();
 }
 
 void
 nsTextControlFrame::nsAnonDivObserver::ContentAppended(
-  nsIDocument* aDocument,
-  nsIContent* aContainer,
   nsIContent* aFirstNewContent)
 {
   mFrame.ClearCachedValue();
 }
 
 void
-nsTextControlFrame::nsAnonDivObserver::ContentInserted(
-  nsIDocument* aDocument,
-  nsIContent* aContainer,
-  nsIContent* aChild)
+nsTextControlFrame::nsAnonDivObserver::ContentInserted(nsIContent* aChild)
 {
   mFrame.ClearCachedValue();
 }
 
 void
 nsTextControlFrame::nsAnonDivObserver::ContentRemoved(
-  nsIDocument* aDocument,
-  nsIContent* aContainer,
   nsIContent* aChild,
   nsIContent* aPreviousSibling)
 {
   mFrame.ClearCachedValue();
 }
--- a/layout/generic/nsImageMap.cpp
+++ b/layout/generic/nsImageMap.cpp
@@ -875,21 +875,20 @@ void
 nsImageMap::MaybeUpdateAreas(nsIContent* aContent)
 {
   if (aContent == mMap || mConsiderWholeSubtree) {
     UpdateAreas();
   }
 }
 
 void
-nsImageMap::AttributeChanged(nsIDocument*  aDocument,
-                             dom::Element* aElement,
-                             int32_t       aNameSpaceID,
-                             nsAtom*      aAttribute,
-                             int32_t       aModType,
+nsImageMap::AttributeChanged(dom::Element* aElement,
+                             int32_t aNameSpaceID,
+                             nsAtom* aAttribute,
+                             int32_t aModType,
                              const nsAttrValue* aOldValue)
 {
   // If the parent of the changing content node is our map then update
   // the map.  But only do this if the node is an HTML <area> or <a>
   // and the attribute that's changing is "shape" or "coords" -- those
   // are the only cases we care about.
   if ((aElement->NodeInfo()->Equals(nsGkAtoms::area) ||
        aElement->NodeInfo()->Equals(nsGkAtoms::a)) &&
@@ -904,29 +903,25 @@ nsImageMap::AttributeChanged(nsIDocument
               aAttribute == nsGkAtoms::id) &&
              mImageFrame) {
     // ID or name has changed. Let ImageFrame recreate ImageMap.
     mImageFrame->DisconnectMap();
   }
 }
 
 void
-nsImageMap::ContentAppended(nsIDocument *aDocument,
-                            nsIContent* aContainer,
-                            nsIContent* aFirstNewContent)
+nsImageMap::ContentAppended(nsIContent* aFirstNewContent)
 {
-  MaybeUpdateAreas(aContainer);
+  MaybeUpdateAreas(aFirstNewContent->GetParent());
 }
 
 void
-nsImageMap::ContentInserted(nsIDocument *aDocument,
-                            nsIContent* aContainer,
-                            nsIContent* aChild)
+nsImageMap::ContentInserted(nsIContent* aChild)
 {
-  MaybeUpdateAreas(aContainer);
+  MaybeUpdateAreas(aChild->GetParent());
 }
 
 static UniquePtr<Area>
 TakeArea(nsImageMap::AreaList& aAreas, HTMLAreaElement* aArea)
 {
   UniquePtr<Area> result;
   size_t index = 0;
   for (UniquePtr<Area>& area : aAreas) {
@@ -940,22 +935,20 @@ TakeArea(nsImageMap::AreaList& aAreas, H
   if (result) {
     aAreas.RemoveElementAt(index);
   }
 
   return result;
 }
 
 void
-nsImageMap::ContentRemoved(nsIDocument *aDocument,
-                           nsIContent* aContainer,
-                           nsIContent* aChild,
+nsImageMap::ContentRemoved(nsIContent* aChild,
                            nsIContent* aPreviousSibling)
 {
-  if (aContainer != mMap && !mConsiderWholeSubtree) {
+  if (aChild->GetParent() != mMap && !mConsiderWholeSubtree) {
     return;
   }
 
   auto* areaElement = HTMLAreaElement::FromContent(aChild);
   if (!areaElement) {
     return;
   }
 
--- a/layout/inspector/inDOMView.cpp
+++ b/layout/inspector/inDOMView.cpp
@@ -613,17 +613,17 @@ inDOMView::PerformActionOnCell(const cha
 
 void
 inDOMView::NodeWillBeDestroyed(const nsINode* aNode)
 {
   NS_NOTREACHED("Document destroyed while we're holding a strong ref to it");
 }
 
 void
-inDOMView::AttributeChanged(nsIDocument* aDocument, dom::Element* aElement,
+inDOMView::AttributeChanged(dom::Element* aElement,
                             int32_t aNameSpaceID, nsAtom* aAttribute,
                             int32_t aModType,
                             const nsAttrValue* aOldValue)
 {
   if (!mTree) {
     return;
   }
 
@@ -742,43 +742,39 @@ inDOMView::AttributeChanged(nsIDocument*
       if (checkNode->level <= baseLevel)
         break;
     }
 
  }
 }
 
 void
-inDOMView::ContentAppended(nsIDocument *aDocument,
-                           nsIContent* aContainer,
-                           nsIContent* aFirstNewContent)
+inDOMView::ContentAppended(nsIContent* aFirstNewContent)
 {
   if (!mTree) {
     return;
   }
 
   for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
-    // Our ContentInserted impl doesn't use the index
-    ContentInserted(aDocument, aContainer, cur);
+    ContentInserted(cur);
   }
 }
 
 static already_AddRefed<nsIDOMNode>
 GetParentForNode(nsINode* aChild, bool aShowAnonymous)
 {
   MOZ_ASSERT(aChild);
   nsINode* parent = InspectorUtils::GetParentForNode(*aChild, aShowAnonymous);
 
   nsCOMPtr<nsIDOMNode> parentDOMNode = do_QueryInterface(parent);
   return parentDOMNode.forget();
 }
 
 void
-inDOMView::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
-                           nsIContent* aChild)
+inDOMView::ContentInserted(nsIContent* aChild)
 {
   if (!mTree)
     return;
 
   nsresult rv;
   nsCOMPtr<nsIDOMNode> childDOMNode(do_QueryInterface(aChild));
   nsCOMPtr<nsIDOMNode> parent = GetParentForNode(aChild, mShowAnonymous);
 
@@ -840,18 +836,17 @@ inDOMView::ContentInserted(nsIDocument *
 
   // insert new node
   InsertNode(newNode, row);
 
   mTree->RowCountChanged(row, 1);
 }
 
 void
-inDOMView::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
-                          nsIContent* aChild, nsIContent* aPreviousSibling)
+inDOMView::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
 {
   if (!mTree)
     return;
 
   nsresult rv;
 
   // find the inDOMViewNode for the old child
   nsCOMPtr<nsIDOMNode> oldDOMNode(do_QueryInterface(aChild));
--- a/layout/svg/SVGObserverUtils.cpp
+++ b/layout/svg/SVGObserverUtils.cpp
@@ -108,18 +108,17 @@ nsSVGRenderingObserver::OnNonDOMMutation
 void
 nsSVGRenderingObserver::NotifyEvictedFromRenderingObserverList()
 {
   mInObserverList = false; // We've been removed from rendering-obs. list.
   StopObserving();            // Remove ourselves from mutation-obs. list.
 }
 
 void
-nsSVGRenderingObserver::AttributeChanged(nsIDocument* aDocument,
-                                         dom::Element* aElement,
+nsSVGRenderingObserver::AttributeChanged(dom::Element* aElement,
                                          int32_t aNameSpaceID,
                                          nsAtom* aAttribute,
                                          int32_t aModType,
                                          const nsAttrValue* aOldValue)
 {
   // An attribute belonging to the element that we are observing *or one of its
   // descendants* has changed.
   //
@@ -132,35 +131,29 @@ nsSVGRenderingObserver::AttributeChanged
   // XXXjwatt: do we really want to blindly break the link between our
   // observers and ourselves for all attribute changes? For non-ID changes
   // surely that is unnecessary.
 
   OnRenderingChange();
 }
 
 void
-nsSVGRenderingObserver::ContentAppended(nsIDocument* aDocument,
-                                        nsIContent* aContainer,
-                                        nsIContent* aFirstNewContent)
+nsSVGRenderingObserver::ContentAppended(nsIContent* aFirstNewContent)
 {
   OnRenderingChange();
 }
 
 void
-nsSVGRenderingObserver::ContentInserted(nsIDocument* aDocument,
-                                        nsIContent* aContainer,
-                                        nsIContent* aChild)
+nsSVGRenderingObserver::ContentInserted(nsIContent* aChild)
 {
   OnRenderingChange();
 }
 
 void
-nsSVGRenderingObserver::ContentRemoved(nsIDocument* aDocument,
-                                       nsIContent* aContainer,
-                                       nsIContent* aChild,
+nsSVGRenderingObserver::ContentRemoved(nsIContent* aChild,
                                        nsIContent* aPreviousSibling)
 {
   OnRenderingChange();
 }
 
 /**
  * Note that in the current setup there are two separate observer lists.
  *
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -3333,54 +3333,43 @@ SVGTextFrame::ScheduleReflowSVGNonDispla
   MOZ_ASSERT(f, "should have found an ancestor frame to reflow");
 
   PresShell()->FrameNeedsReflow(f, aReason, NS_FRAME_IS_DIRTY);
 }
 
 NS_IMPL_ISUPPORTS(SVGTextFrame::MutationObserver, nsIMutationObserver)
 
 void
-SVGTextFrame::MutationObserver::ContentAppended(nsIDocument* aDocument,
-                                                nsIContent* aContainer,
-                                                nsIContent* aFirstNewContent)
+SVGTextFrame::MutationObserver::ContentAppended(nsIContent* aFirstNewContent)
 {
   mFrame->NotifyGlyphMetricsChange();
 }
 
 void
-SVGTextFrame::MutationObserver::ContentInserted(
-                                        nsIDocument* aDocument,
-                                        nsIContent* aContainer,
-                                        nsIContent* aChild)
+SVGTextFrame::MutationObserver::ContentInserted(nsIContent* aChild)
 {
   mFrame->NotifyGlyphMetricsChange();
 }
 
 void
-SVGTextFrame::MutationObserver::ContentRemoved(
-                                       nsIDocument *aDocument,
-                                       nsIContent* aContainer,
-                                       nsIContent* aChild,
-                                       nsIContent* aPreviousSibling)
+SVGTextFrame::MutationObserver::ContentRemoved(nsIContent* aChild,
+                                               nsIContent* aPreviousSibling)
 {
   mFrame->NotifyGlyphMetricsChange();
 }
 
 void
-SVGTextFrame::MutationObserver::CharacterDataChanged(
-                                                 nsIDocument* aDocument,
-                                                 nsIContent* aContent,
-                                                 const CharacterDataChangeInfo&)
+SVGTextFrame::MutationObserver::CharacterDataChanged(nsIContent* aContent,
+                                                     const CharacterDataChangeInfo&)
 {
   mFrame->NotifyGlyphMetricsChange();
 }
 
 void
 SVGTextFrame::MutationObserver::AttributeChanged(
-                                                nsIDocument* aDocument,
                                                 mozilla::dom::Element* aElement,
                                                 int32_t aNameSpaceID,
                                                 nsAtom* aAttribute,
                                                 int32_t aModType,
                                                 const nsAttrValue* aOldValue)
 {
   if (!aElement->IsSVGElement()) {
     return;
--- a/layout/xul/tree/nsTreeContentView.cpp
+++ b/layout/xul/tree/nsTreeContentView.cpp
@@ -890,18 +890,17 @@ nsTreeContentView::GetIndexOfItem(nsIDOM
   nsCOMPtr<Element> element = do_QueryInterface(aItem);
 
   *_retval = GetIndexOfItem(element);
 
   return NS_OK;
 }
 
 void
-nsTreeContentView::AttributeChanged(nsIDocument*  aDocument,
-                                    dom::Element* aElement,
+nsTreeContentView::AttributeChanged(dom::Element* aElement,
                                     int32_t       aNameSpaceID,
                                     nsAtom*      aAttribute,
                                     int32_t       aModType,
                                     const nsAttrValue* aOldValue)
 {
   // Lots of codepaths under here that do all sorts of stuff, so be safe.
   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
 
@@ -1045,132 +1044,128 @@ nsTreeContentView::AttributeChanged(nsID
           }
         }
       }
     }
   }
 }
 
 void
-nsTreeContentView::ContentAppended(nsIDocument *aDocument,
-                                   nsIContent* aContainer,
-                                   nsIContent* aFirstNewContent)
+nsTreeContentView::ContentAppended(nsIContent* aFirstNewContent)
 {
   for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
     // Our contentinserted doesn't use the index
-    ContentInserted(aDocument, aContainer, cur);
+    ContentInserted(cur);
   }
 }
 
 void
-nsTreeContentView::ContentInserted(nsIDocument *aDocument,
-                                   nsIContent* aContainer,
-                                   nsIContent* aChild)
+nsTreeContentView::ContentInserted(nsIContent* aChild)
 {
   NS_ASSERTION(aChild, "null ptr");
+  nsIContent* container = aChild->GetParent();
 
   // Make sure this notification concerns us.
   // First check the tag to see if it's one that we care about.
 
   // Don't allow non-XUL nodes.
-  if (!aChild->IsXULElement() || !aContainer->IsXULElement())
+  if (!aChild->IsXULElement() || !container->IsXULElement())
     return;
 
   if (!aChild->IsAnyOfXULElements(nsGkAtoms::treeitem,
                                   nsGkAtoms::treeseparator,
                                   nsGkAtoms::treechildren,
                                   nsGkAtoms::treerow,
                                   nsGkAtoms::treecell)) {
     return;
   }
 
   // If we have a legal tag, go up to the tree/select and make sure
   // that it's ours.
 
-  for (nsIContent* element = aContainer; element != mBody; element = element->GetParent()) {
+  for (nsIContent* element = container; element != mBody; element = element->GetParent()) {
     if (!element)
       return; // this is not for us
     if (element->IsXULElement(nsGkAtoms::tree))
       return; // this is not for us
   }
 
   // Lots of codepaths under here that do all sorts of stuff, so be safe.
   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
 
   if (aChild->IsXULElement(nsGkAtoms::treechildren)) {
-    int32_t index = FindContent(aContainer);
+    int32_t index = FindContent(container);
     if (index >= 0) {
       Row* row = mRows[index].get();
       row->SetEmpty(false);
       if (mBoxObject)
         mBoxObject->InvalidateRow(index);
       if (row->IsContainer() && row->IsOpen()) {
         int32_t count = EnsureSubtree(index);
         if (mBoxObject)
           mBoxObject->RowCountChanged(index + 1, count);
       }
     }
   }
   else if (aChild->IsAnyOfXULElements(nsGkAtoms::treeitem,
                                       nsGkAtoms::treeseparator)) {
-    InsertRowFor(aContainer, aChild);
+    InsertRowFor(container, aChild);
   }
   else if (aChild->IsXULElement(nsGkAtoms::treerow)) {
-    int32_t index = FindContent(aContainer);
+    int32_t index = FindContent(container);
     if (index >= 0 && mBoxObject)
       mBoxObject->InvalidateRow(index);
   }
   else if (aChild->IsXULElement(nsGkAtoms::treecell)) {
-    nsCOMPtr<nsIContent> parent = aContainer->GetParent();
+    nsCOMPtr<nsIContent> parent = container->GetParent();
     if (parent) {
       int32_t index = FindContent(parent);
       if (index >= 0 && mBoxObject)
         mBoxObject->InvalidateRow(index);
     }
   }
 }
 
 void
-nsTreeContentView::ContentRemoved(nsIDocument *aDocument,
-                                  nsIContent* aContainer,
-                                  nsIContent* aChild,
+nsTreeContentView::ContentRemoved(nsIContent* aChild,
                                   nsIContent* aPreviousSibling)
 {
   NS_ASSERTION(aChild, "null ptr");
 
+  nsIContent* container = aChild->GetParent();
   // Make sure this notification concerns us.
   // First check the tag to see if it's one that we care about.
 
   // We don't consider non-XUL nodes.
-  if (!aChild->IsXULElement() || !aContainer->IsXULElement())
+  if (!aChild->IsXULElement() || !container->IsXULElement())
     return;
 
   if (!aChild->IsAnyOfXULElements(nsGkAtoms::treeitem,
                                   nsGkAtoms::treeseparator,
                                   nsGkAtoms::treechildren,
                                   nsGkAtoms::treerow,
                                   nsGkAtoms::treecell)) {
     return;
   }
 
   // If we have a legal tag, go up to the tree/select and make sure
   // that it's ours.
 
-  for (nsIContent* element = aContainer; element != mBody; element = element->GetParent()) {
+  for (nsIContent* element = container; element != mBody; element = element->GetParent()) {
     if (!element)
       return; // this is not for us
     if (element->IsXULElement(nsGkAtoms::tree))
       return; // this is not for us
   }
 
   // Lots of codepaths under here that do all sorts of stuff, so be safe.
   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
 
   if (aChild->IsXULElement(nsGkAtoms::treechildren)) {
-    int32_t index = FindContent(aContainer);
+    int32_t index = FindContent(container);
     if (index >= 0) {
       Row* row = mRows[index].get();
       row->SetEmpty(true);
       int32_t count = RemoveSubtree(index);
       // Invalidate also the row to update twisty.
       if (mBoxObject) {
         mBoxObject->InvalidateRow(index);
         mBoxObject->RowCountChanged(index + 1, -count);
@@ -1182,22 +1177,22 @@ nsTreeContentView::ContentRemoved(nsIDoc
     int32_t index = FindContent(aChild);
     if (index >= 0) {
       int32_t count = RemoveRow(index);
       if (mBoxObject)
         mBoxObject->RowCountChanged(index, -count);
     }
   }
   else if (aChild->IsXULElement(nsGkAtoms::treerow)) {
-    int32_t index = FindContent(aContainer);
+    int32_t index = FindContent(container);
     if (index >= 0 && mBoxObject)
       mBoxObject->InvalidateRow(index);
   }
   else if (aChild->IsXULElement(nsGkAtoms::treecell)) {
-    nsCOMPtr<nsIContent> parent = aContainer->GetParent();
+    nsCOMPtr<nsIContent> parent = container->GetParent();
     if (parent) {
       int32_t index = FindContent(parent);
       if (index >= 0 && mBoxObject)
         mBoxObject->InvalidateRow(index);
     }
   }
 }
 
--- a/mfbt/HashFunctions.h
+++ b/mfbt/HashFunctions.h
@@ -47,16 +47,17 @@
 #ifndef mozilla_HashFunctions_h
 #define mozilla_HashFunctions_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Char16.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Types.h"
+#include "mozilla/WrappingOperations.h"
 
 #include <stdint.h>
 
 #ifdef __cplusplus
 namespace mozilla {
 
 /**
  * The golden ratio as a 32-bit fixed-point value.
@@ -91,17 +92,19 @@ AddU32ToHash(uint32_t aHash, uint32_t aV
    *
    * The rotation length of 5 is also arbitrary, although an odd number is again
    * preferable so our hash explores the whole universe of possible rotations.
    *
    * Finally, we multiply by the golden ratio *after* xor'ing, not before.
    * Otherwise, if |aHash| is 0 (as it often is for the beginning of a
    * message), the expression
    *
-   *   (kGoldenRatioU32 * RotateBitsLeft(aHash, 5)) |xor| aValue
+   *   mozilla::WrappingMultiply(kGoldenRatioU32, RotateBitsLeft(aHash, 5))
+   *   |xor|
+   *   aValue
    *
    * evaluates to |aValue|.
    *
    * (Number-theoretic aside: Because any odd number |m| is relatively prime to
    * our modulus (2^32), the list
    *
    *    [x * m (mod 2^32) for 0 <= x < 2^32]
    *
@@ -109,17 +112,18 @@ AddU32ToHash(uint32_t aHash, uint32_t aV
    * cause us to skip any possible hash values.
    *
    * It's also nice if |m| has large-ish order mod 2^32 -- that is, if the
    * smallest k such that m^k == 1 (mod 2^32) is large -- so we can safely
    * multiply our hash value by |m| a few times without negating the
    * multiplicative effect.  Our golden ratio constant has order 2^29, which is
    * more than enough for our purposes.)
    */
-  return kGoldenRatioU32 * (RotateBitsLeft32(aHash, 5) ^ aValue);
+  return mozilla::WrappingMultiply(kGoldenRatioU32,
+                                   (RotateBitsLeft32(aHash, 5) ^ aValue));
 }
 
 /**
  * AddUintptrToHash takes sizeof(uintptr_t) as a template parameter.
  */
 template<size_t PtrSize>
 inline uint32_t
 AddUintptrToHash(uint32_t aHash, uintptr_t aValue)
--- a/mfbt/MathAlgorithms.h
+++ b/mfbt/MathAlgorithms.h
@@ -537,80 +537,11 @@ Clamp(const T aValue, const T aMin, cons
 
     if (aValue <= aMin)
         return aMin;
     if (aValue >= aMax)
         return aMax;
     return aValue;
 }
 
-namespace detail {
-
-template<typename UnsignedType>
-struct WrapToSignedHelper
-{
-  static_assert(mozilla::IsUnsigned<UnsignedType>::value,
-                "WrapToSigned must be passed an unsigned type");
-
-  using SignedType = typename mozilla::MakeSigned<UnsignedType>::Type;
-
-  static constexpr SignedType MaxValue =
-    (UnsignedType(1) << (CHAR_BIT * sizeof(SignedType) - 1)) - 1;
-  static constexpr SignedType MinValue = -MaxValue - 1;
-
-  static constexpr UnsignedType MinValueUnsigned =
-    static_cast<UnsignedType>(MinValue);
-  static constexpr UnsignedType MaxValueUnsigned =
-    static_cast<UnsignedType>(MaxValue);
-
-  // Overflow-correctness was proven in bug 1432646 and is explained in the
-  // comment below.  This function is very hot, both at compile time and
-  // runtime, so disable all overflow checking in it.
-  MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW MOZ_NO_SANITIZE_SIGNED_OVERFLOW
-  static constexpr SignedType compute(UnsignedType aValue)
-  {
-    // This algorithm was originally provided here:
-    // https://stackoverflow.com/questions/13150449/efficient-unsigned-to-signed-cast-avoiding-implementation-defined-behavior
-    //
-    // If the value is in the non-negative signed range, just cast.
-    //
-    // If the value will be negative, compute its delta from the first number
-    // past the max signed integer, then add that to the minimum signed value.
-    //
-    // At the low end: if |u| is the maximum signed value plus one, then it has
-    // the same mathematical value as |MinValue| cast to unsigned form.  The
-    // delta is zero, so the signed form of |u| is |MinValue| -- exactly the
-    // result of adding zero delta to |MinValue|.
-    //
-    // At the high end: if |u| is the maximum *unsigned* value, then it has all
-    // bits set.  |MinValue| cast to unsigned form is purely the high bit set.
-    // So the delta is all bits but high set -- exactly |MaxValue|.  And as
-    // |MinValue = -MaxValue - 1|, we have |MaxValue + (-MaxValue - 1)| to
-    // equal -1.
-    //
-    // Thus the delta below is in signed range, the corresponding cast is safe,
-    // and this computation produces values spanning [MinValue, 0): exactly the
-    // desired range of all negative signed integers.
-    return (aValue <= MaxValueUnsigned)
-           ? static_cast<SignedType>(aValue)
-           : static_cast<SignedType>(aValue - MinValueUnsigned) + MinValue;
-  }
-};
-
-} // namespace detail
-
-/**
- * Convert an unsigned value to signed, if necessary wrapping around.
- *
- * This is the behavior normal C++ casting will perform in most implementations
- * these days -- but this function makes explicit that such conversion is
- * happening.
- */
-template<typename UnsignedType>
-inline constexpr typename detail::WrapToSignedHelper<UnsignedType>::SignedType
-WrapToSigned(UnsignedType aValue)
-{
-  return detail::WrapToSignedHelper<UnsignedType>::compute(aValue);
-}
-
 } /* namespace mozilla */
 
 #endif /* mozilla_MathAlgorithms_h */
copy from mfbt/MathAlgorithms.h
copy to mfbt/WrappingOperations.h
--- a/mfbt/MathAlgorithms.h
+++ b/mfbt/WrappingOperations.h
@@ -1,554 +1,32 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* mfbt maths algorithms. */
+/*
+ * Math operations that implement wraparound semantics on overflow or underflow
+ * without performing C++ undefined behavior or tripping up compiler-based
+ * integer-overflow sanitizers.
+ */
 
-#ifndef mozilla_MathAlgorithms_h
-#define mozilla_MathAlgorithms_h
+#ifndef mozilla_WrappingOperations_h
+#define mozilla_WrappingOperations_h
 
-#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
 #include "mozilla/TypeTraits.h"
 
-#include <cmath>
 #include <limits.h>
-#include <stdint.h>
-
-namespace mozilla {
-
-// Greatest Common Divisor
-template<typename IntegerType>
-MOZ_ALWAYS_INLINE IntegerType
-EuclidGCD(IntegerType aA, IntegerType aB)
-{
-  // Euclid's algorithm; O(N) in the worst case.  (There are better
-  // ways, but we don't need them for the current use of this algo.)
-  MOZ_ASSERT(aA > IntegerType(0));
-  MOZ_ASSERT(aB > IntegerType(0));
-
-  while (aA != aB) {
-    if (aA > aB) {
-      aA = aA - aB;
-    } else {
-      aB = aB - aA;
-    }
-  }
-
-  return aA;
-}
-
-// Least Common Multiple
-template<typename IntegerType>
-MOZ_ALWAYS_INLINE IntegerType
-EuclidLCM(IntegerType aA, IntegerType aB)
-{
-  // Divide first to reduce overflow risk.
-  return (aA / EuclidGCD(aA, aB)) * aB;
-}
-
-namespace detail {
-
-template<typename T>
-struct AllowDeprecatedAbsFixed : FalseType {};
-
-template<> struct AllowDeprecatedAbsFixed<int32_t> : TrueType {};
-template<> struct AllowDeprecatedAbsFixed<int64_t> : TrueType {};
-
-template<typename T>
-struct AllowDeprecatedAbs : AllowDeprecatedAbsFixed<T> {};
-
-template<> struct AllowDeprecatedAbs<int> : TrueType {};
-template<> struct AllowDeprecatedAbs<long> : TrueType {};
-
-} // namespace detail
-
-// DO NOT USE DeprecatedAbs.  It exists only until its callers can be converted
-// to Abs below, and it will be removed when all callers have been changed.
-template<typename T>
-inline typename mozilla::EnableIf<detail::AllowDeprecatedAbs<T>::value, T>::Type
-DeprecatedAbs(const T aValue)
-{
-  // The absolute value of the smallest possible value of a signed-integer type
-  // won't fit in that type (on twos-complement systems -- and we're blithely
-  // assuming we're on such systems, for the non-<stdint.h> types listed above),
-  // so assert that the input isn't that value.
-  //
-  // This is the case if: the value is non-negative; or if adding one (giving a
-  // value in the range [-maxvalue, 0]), then negating (giving a value in the
-  // range [0, maxvalue]), doesn't produce maxvalue (because in twos-complement,
-  // (minvalue + 1) == -maxvalue).
-  MOZ_ASSERT(aValue >= 0 ||
-             -(aValue + 1) != T((1ULL << (CHAR_BIT * sizeof(T) - 1)) - 1),
-             "You can't negate the smallest possible negative integer!");
-  return aValue >= 0 ? aValue : -aValue;
-}
-
-namespace detail {
-
-// For now mozilla::Abs only takes intN_T, the signed natural types, and
-// float/double/long double.  Feel free to add overloads for other standard,
-// signed types if you need them.
-
-template<typename T>
-struct AbsReturnTypeFixed;
-
-template<> struct AbsReturnTypeFixed<int8_t> { typedef uint8_t Type; };
-template<> struct AbsReturnTypeFixed<int16_t> { typedef uint16_t Type; };
-template<> struct AbsReturnTypeFixed<int32_t> { typedef uint32_t Type; };
-template<> struct AbsReturnTypeFixed<int64_t> { typedef uint64_t Type; };
-
-template<typename T>
-struct AbsReturnType : AbsReturnTypeFixed<T> {};
-
-template<> struct AbsReturnType<char> :
-  EnableIf<char(-1) < char(0), unsigned char> {};
-template<> struct AbsReturnType<signed char> { typedef unsigned char Type; };
-template<> struct AbsReturnType<short> { typedef unsigned short Type; };
-template<> struct AbsReturnType<int> { typedef unsigned int Type; };
-template<> struct AbsReturnType<long> { typedef unsigned long Type; };
-template<> struct AbsReturnType<long long> { typedef unsigned long long Type; };
-template<> struct AbsReturnType<float> { typedef float Type; };
-template<> struct AbsReturnType<double> { typedef double Type; };
-template<> struct AbsReturnType<long double> { typedef long double Type; };
-
-} // namespace detail
-
-template<typename T>
-inline constexpr typename detail::AbsReturnType<T>::Type
-Abs(const T aValue)
-{
-  using ReturnType = typename detail::AbsReturnType<T>::Type;
-  return aValue >= 0 ? ReturnType(aValue) : ~ReturnType(aValue) + 1;
-}
-
-template<>
-inline float
-Abs<float>(const float aFloat)
-{
-  return std::fabs(aFloat);
-}
-
-template<>
-inline double
-Abs<double>(const double aDouble)
-{
-  return std::fabs(aDouble);
-}
-
-template<>
-inline long double
-Abs<long double>(const long double aLongDouble)
-{
-  return std::fabs(aLongDouble);
-}
-
-} // namespace mozilla
-
-#if defined(_MSC_VER) && \
-    (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64))
-#  define MOZ_BITSCAN_WINDOWS
-
-#  include <intrin.h>
-#  pragma intrinsic(_BitScanForward, _BitScanReverse)
-
-#  if defined(_M_AMD64) || defined(_M_X64)
-#    define MOZ_BITSCAN_WINDOWS64
-#   pragma intrinsic(_BitScanForward64, _BitScanReverse64)
-#  endif
-
-#endif
 
 namespace mozilla {
 
 namespace detail {
 
-#if defined(MOZ_BITSCAN_WINDOWS)
-
-inline uint_fast8_t
-CountLeadingZeroes32(uint32_t aValue)
-{
-  unsigned long index;
-  if (!_BitScanReverse(&index, static_cast<unsigned long>(aValue)))
-      return 32;
-  return uint_fast8_t(31 - index);
-}
-
-
-inline uint_fast8_t
-CountTrailingZeroes32(uint32_t aValue)
-{
-  unsigned long index;
-  if (!_BitScanForward(&index, static_cast<unsigned long>(aValue)))
-      return 32;
-  return uint_fast8_t(index);
-}
-
-inline uint_fast8_t
-CountPopulation32(uint32_t aValue)
-{
-  uint32_t x = aValue - ((aValue >> 1) & 0x55555555);
-  x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
-  return (((x + (x >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
-}
-inline uint_fast8_t
-CountPopulation64(uint64_t aValue)
-{
-  return uint_fast8_t(CountPopulation32(aValue & 0xffffffff) +
-                      CountPopulation32(aValue >> 32));
-}
-
-inline uint_fast8_t
-CountLeadingZeroes64(uint64_t aValue)
-{
-#if defined(MOZ_BITSCAN_WINDOWS64)
-  unsigned long index;
-  if (!_BitScanReverse64(&index, static_cast<unsigned __int64>(aValue)))
-      return 64;
-  return uint_fast8_t(63 - index);
-#else
-  uint32_t hi = uint32_t(aValue >> 32);
-  if (hi != 0) {
-    return CountLeadingZeroes32(hi);
-  }
-  return 32u + CountLeadingZeroes32(uint32_t(aValue));
-#endif
-}
-
-inline uint_fast8_t
-CountTrailingZeroes64(uint64_t aValue)
-{
-#if defined(MOZ_BITSCAN_WINDOWS64)
-  unsigned long index;
-  if (!_BitScanForward64(&index, static_cast<unsigned __int64>(aValue)))
-      return 64;
-  return uint_fast8_t(index);
-#else
-  uint32_t lo = uint32_t(aValue);
-  if (lo != 0) {
-    return CountTrailingZeroes32(lo);
-  }
-  return 32u + CountTrailingZeroes32(uint32_t(aValue >> 32));
-#endif
-}
-
-#  ifdef MOZ_HAVE_BITSCAN64
-#    undef MOZ_HAVE_BITSCAN64
-#  endif
-
-#elif defined(__clang__) || defined(__GNUC__)
-
-#  if defined(__clang__)
-#    if !__has_builtin(__builtin_ctz) || !__has_builtin(__builtin_clz)
-#      error "A clang providing __builtin_c[lt]z is required to build"
-#    endif
-#  else
-     // gcc has had __builtin_clz and friends since 3.4: no need to check.
-#  endif
-
-inline uint_fast8_t
-CountLeadingZeroes32(uint32_t aValue)
-{
-  return __builtin_clz(aValue);
-}
-
-inline uint_fast8_t
-CountTrailingZeroes32(uint32_t aValue)
-{
-  return __builtin_ctz(aValue);
-}
-
-inline uint_fast8_t
-CountPopulation32(uint32_t aValue)
-{
-  return __builtin_popcount(aValue);
-}
-
-inline uint_fast8_t
-CountPopulation64(uint64_t aValue)
-{
-  return __builtin_popcountll(aValue);
-}
-
-inline uint_fast8_t
-CountLeadingZeroes64(uint64_t aValue)
-{
-  return __builtin_clzll(aValue);
-}
-
-inline uint_fast8_t
-CountTrailingZeroes64(uint64_t aValue)
-{
-  return __builtin_ctzll(aValue);
-}
-
-#else
-#  error "Implement these!"
-inline uint_fast8_t CountLeadingZeroes32(uint32_t aValue) = delete;
-inline uint_fast8_t CountTrailingZeroes32(uint32_t aValue) = delete;
-inline uint_fast8_t CountPopulation32(uint32_t aValue) = delete;
-inline uint_fast8_t CountPopulation64(uint64_t aValue) = delete;
-inline uint_fast8_t CountLeadingZeroes64(uint64_t aValue) = delete;
-inline uint_fast8_t CountTrailingZeroes64(uint64_t aValue) = delete;
-#endif
-
-} // namespace detail
-
-/**
- * Compute the number of high-order zero bits in the NON-ZERO number |aValue|.
- * That is, looking at the bitwise representation of the number, with the
- * highest- valued bits at the start, return the number of zeroes before the
- * first one is observed.
- *
- * CountLeadingZeroes32(0xF0FF1000) is 0;
- * CountLeadingZeroes32(0x7F8F0001) is 1;
- * CountLeadingZeroes32(0x3FFF0100) is 2;
- * CountLeadingZeroes32(0x1FF50010) is 3; and so on.
- */
-inline uint_fast8_t
-CountLeadingZeroes32(uint32_t aValue)
-{
-  MOZ_ASSERT(aValue != 0);
-  return detail::CountLeadingZeroes32(aValue);
-}
-
-/**
- * Compute the number of low-order zero bits in the NON-ZERO number |aValue|.
- * That is, looking at the bitwise representation of the number, with the
- * lowest- valued bits at the start, return the number of zeroes before the
- * first one is observed.
- *
- * CountTrailingZeroes32(0x0100FFFF) is 0;
- * CountTrailingZeroes32(0x7000FFFE) is 1;
- * CountTrailingZeroes32(0x0080FFFC) is 2;
- * CountTrailingZeroes32(0x0080FFF8) is 3; and so on.
- */
-inline uint_fast8_t
-CountTrailingZeroes32(uint32_t aValue)
-{
-  MOZ_ASSERT(aValue != 0);
-  return detail::CountTrailingZeroes32(aValue);
-}
-
-/**
- * Compute the number of one bits in the number |aValue|,
- */
-inline uint_fast8_t
-CountPopulation32(uint32_t aValue)
-{
-  return detail::CountPopulation32(aValue);
-}
-
-/** Analogous to CountPopulation32, but for 64-bit numbers */
-inline uint_fast8_t
-CountPopulation64(uint64_t aValue)
-{
-  return detail::CountPopulation64(aValue);
-}
-
-/** Analogous to CountLeadingZeroes32, but for 64-bit numbers. */
-inline uint_fast8_t
-CountLeadingZeroes64(uint64_t aValue)
-{
-  MOZ_ASSERT(aValue != 0);
-  return detail::CountLeadingZeroes64(aValue);
-}
-
-/** Analogous to CountTrailingZeroes32, but for 64-bit numbers. */
-inline uint_fast8_t
-CountTrailingZeroes64(uint64_t aValue)
-{
-  MOZ_ASSERT(aValue != 0);
-  return detail::CountTrailingZeroes64(aValue);
-}
-
-namespace detail {
-
-template<typename T, size_t Size = sizeof(T)>
-class CeilingLog2;
-
-template<typename T>
-class CeilingLog2<T, 4>
-{
-public:
-  static uint_fast8_t compute(const T aValue)
-  {
-    // Check for <= 1 to avoid the == 0 undefined case.
-    return aValue <= 1 ? 0u : 32u - CountLeadingZeroes32(aValue - 1);
-  }
-};
-
-template<typename T>
-class CeilingLog2<T, 8>
-{
-public:
-  static uint_fast8_t compute(const T aValue)
-  {
-    // Check for <= 1 to avoid the == 0 undefined case.
-    return aValue <= 1 ? 0u : 64u - CountLeadingZeroes64(aValue - 1);
-  }
-};
-
-} // namespace detail
-
-/**
- * Compute the log of the least power of 2 greater than or equal to |aValue|.
- *
- * CeilingLog2(0..1) is 0;
- * CeilingLog2(2) is 1;
- * CeilingLog2(3..4) is 2;
- * CeilingLog2(5..8) is 3;
- * CeilingLog2(9..16) is 4; and so on.
- */
-template<typename T>
-inline uint_fast8_t
-CeilingLog2(const T aValue)
-{
-  return detail::CeilingLog2<T>::compute(aValue);
-}
-
-/** A CeilingLog2 variant that accepts only size_t. */
-inline uint_fast8_t
-CeilingLog2Size(size_t aValue)
-{
-  return CeilingLog2(aValue);
-}
-
-namespace detail {
-
-template<typename T, size_t Size = sizeof(T)>
-class FloorLog2;
-
-template<typename T>
-class FloorLog2<T, 4>
-{
-public:
-  static uint_fast8_t compute(const T aValue)
-  {
-    return 31u - CountLeadingZeroes32(aValue | 1);
-  }
-};
-
-template<typename T>
-class FloorLog2<T, 8>
-{
-public:
-  static uint_fast8_t compute(const T aValue)
-  {
-    return 63u - CountLeadingZeroes64(aValue | 1);
-  }
-};
-
-} // namespace detail
-
-/**
- * Compute the log of the greatest power of 2 less than or equal to |aValue|.
- *
- * FloorLog2(0..1) is 0;
- * FloorLog2(2..3) is 1;
- * FloorLog2(4..7) is 2;
- * FloorLog2(8..15) is 3; and so on.
- */
-template<typename T>
-inline uint_fast8_t
-FloorLog2(const T aValue)
-{
-  return detail::FloorLog2<T>::compute(aValue);
-}
-
-/** A FloorLog2 variant that accepts only size_t. */
-inline uint_fast8_t
-FloorLog2Size(size_t aValue)
-{
-  return FloorLog2(aValue);
-}
-
-/*
- * Compute the smallest power of 2 greater than or equal to |x|.  |x| must not
- * be so great that the computed value would overflow |size_t|.
- */
-inline size_t
-RoundUpPow2(size_t aValue)
-{
-  MOZ_ASSERT(aValue <= (size_t(1) << (sizeof(size_t) * CHAR_BIT - 1)),
-             "can't round up -- will overflow!");
-  return size_t(1) << CeilingLog2(aValue);
-}
-
-/**
- * Rotates the bits of the given value left by the amount of the shift width.
- */
-template<typename T>
-inline T
-RotateLeft(const T aValue, uint_fast8_t aShift)
-{
-  MOZ_ASSERT(aShift < sizeof(T) * CHAR_BIT, "Shift value is too large!");
-  MOZ_ASSERT(aShift > 0,
-             "Rotation by value length is undefined behavior, but compilers "
-             "do not currently fold a test into the rotate instruction. "
-             "Please remove this restriction when compilers optimize the "
-             "zero case (http://blog.regehr.org/archives/1063).");
-  static_assert(IsUnsigned<T>::value, "Rotates require unsigned values");
-  return (aValue << aShift) | (aValue >> (sizeof(T) * CHAR_BIT - aShift));
-}
-
-/**
- * Rotates the bits of the given value right by the amount of the shift width.
- */
-template<typename T>
-inline T
-RotateRight(const T aValue, uint_fast8_t aShift)
-{
-  MOZ_ASSERT(aShift < sizeof(T) * CHAR_BIT, "Shift value is too large!");
-  MOZ_ASSERT(aShift > 0,
-             "Rotation by value length is undefined behavior, but compilers "
-             "do not currently fold a test into the rotate instruction. "
-             "Please remove this restriction when compilers optimize the "
-             "zero case (http://blog.regehr.org/archives/1063).");
-  static_assert(IsUnsigned<T>::value, "Rotates require unsigned values");
-  return (aValue >> aShift) | (aValue << (sizeof(T) * CHAR_BIT - aShift));
-}
-
-/**
- * Returns true if |x| is a power of two.
- * Zero is not an integer power of two. (-Inf is not an integer)
- */
-template<typename T>
-constexpr bool
-IsPowerOfTwo(T x)
-{
-    static_assert(IsUnsigned<T>::value,
-                  "IsPowerOfTwo requires unsigned values");
-    return x && (x & (x - 1)) == 0;
-}
-
-template<typename T>
-inline T
-Clamp(const T aValue, const T aMin, const T aMax)
-{
-    static_assert(IsIntegral<T>::value,
-                  "Clamp accepts only integral types, so that it doesn't have"
-                  " to distinguish differently-signed zeroes (which users may"
-                  " or may not care to distinguish, likely at a perf cost) or"
-                  " to decide how to clamp NaN or a range with a NaN"
-                  " endpoint.");
-    MOZ_ASSERT(aMin <= aMax);
-
-    if (aValue <= aMin)
-        return aMin;
-    if (aValue >= aMax)
-        return aMax;
-    return aValue;
-}
-
-namespace detail {
-
 template<typename UnsignedType>
 struct WrapToSignedHelper
 {
   static_assert(mozilla::IsUnsigned<UnsignedType>::value,
                 "WrapToSigned must be passed an unsigned type");
 
   using SignedType = typename mozilla::MakeSigned<UnsignedType>::Type;
 
@@ -606,11 +84,105 @@ struct WrapToSignedHelper
  */
 template<typename UnsignedType>
 inline constexpr typename detail::WrapToSignedHelper<UnsignedType>::SignedType
 WrapToSigned(UnsignedType aValue)
 {
   return detail::WrapToSignedHelper<UnsignedType>::compute(aValue);
 }
 
+namespace detail {
+
+template<typename T>
+struct WrappingMultiplyHelper
+{
+private:
+  using UnsignedT = typename MakeUnsigned<T>::Type;
+
+  MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
+  static UnsignedT
+  multiply(UnsignedT aX, UnsignedT aY)
+  {
+  // |mozilla::WrappingMultiply| isn't constexpr because MSVC warns about well-
+  // defined unsigned integer overflows that may happen here.
+  // https://msdn.microsoft.com/en-us/library/4kze989h.aspx  And constexpr
+  // seems to cause the warning to be emitted at |WrappingMultiply| call *sites*
+  // instead of here, so these #pragmas are ineffective.
+  //
+  // https://stackoverflow.com/questions/37658794/integer-constant-overflow-warning-in-constexpr
+  //
+  // If/when MSVC fix this bug, we should make these functions constexpr.
+
+    // Begin with |1U| to ensure the overall operation chain is never promoted
+    // to signed integer operations that might have *signed* integer overflow.
+    return static_cast<UnsignedT>(1U * aX * aY);
+  }
+
+  static T
+  toResult(UnsignedT aX, UnsignedT aY)
+  {
+    // We could always return WrapToSigned and rely on unsigned conversion
+    // undoing the wrapping when |T| is unsigned, but this seems clearer.
+    return IsSigned<T>::value
+           ? WrapToSigned(multiply(aX, aY))
+           : multiply(aX, aY);
+  }
+
+public:
+  MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
+  static T compute(T aX, T aY)
+  {
+    return toResult(static_cast<UnsignedT>(aX), static_cast<UnsignedT>(aY));
+  }
+};
+
+} // namespace detail
+
+/**
+ * Multiply two integers of the same type, and return the result converted to
+ * that type using wraparound semantics.  This function:
+ *
+ *   1) makes explicit the desire for and dependence upon wraparound semantics,
+ *   2) provides wraparound semantics *safely* with no signed integer overflow
+ *      that would have undefined behavior, and
+ *   3) won't trip up {,un}signed-integer overflow sanitizers (see
+ *      build/autoconf/sanitize.m4) at runtime.
+ *
+ * For N-bit unsigned integer types, this is equivalent to multiplying the two
+ * numbers, then taking the result mod 2**N:
+ *
+ *   WrappingMultiply(uint32_t(42), uint32_t(17)) is 714 (714 mod 2**32);
+ *   WrappingMultiply(uint8_t(16), uint8_t(24)) is 128 (384 mod 2**8);
+ *   WrappingMultiply(uint16_t(3), uint16_t(32768)) is 32768 (98304 mod 2*16).
+ *
+ * Use this function for any unsigned multiplication that can wrap (instead of
+ * normal C++ multiplication) to play nice with the sanitizers.  But it's
+ * especially important to use it for uint16_t multiplication: in most compilers
+ * for uint16_t*uint16_t some operand values will trigger signed integer
+ * overflow with undefined behavior!  http://kqueue.org/blog/2013/09/17/cltq/
+ * has the grody details.  Other than that one weird case, WrappingMultiply on
+ * unsigned types is the same as C++ multiplication.
+ *
+ * For N-bit signed integer types, this is equivalent to multiplying the two
+ * numbers wrapped to unsigned, taking the product mod 2**N, then wrapping that
+ * number to the signed range:
+ *
+ *   WrappingMultiply(int16_t(-456), int16_t(123)) is 9448 ((-56088 mod 2**16) + 2**16);
+ *   WrappingMultiply(int32_t(-7), int32_t(-9)) is 63 (63 mod 2**32);
+ *   WrappingMultiply(int8_t(16), int8_t(24)) is -128 ((384 mod 2**8) - 2**8);
+ *   WrappingMultiply(int8_t(16), int8_t(255)) is -16 ((4080 mod 2**8) - 2**8).
+ *
+ * There is no ready equivalent to this operation in C++, as applying C++
+ * multiplication to signed integer types in ways that trigger overflow has
+ * undefined behavior.  However, it's how multiplication *tends* to behave with
+ * most compilers in most situations, even though it's emphatically not required
+ * to do so.
+ */
+template<typename T>
+inline T
+WrappingMultiply(T aX, T aY)
+{
+  return detail::WrappingMultiplyHelper<T>::compute(aX, aY);
+}
+
 } /* namespace mozilla */
 
-#endif /* mozilla_MathAlgorithms_h */
+#endif /* mozilla_WrappingOperations_h */
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -97,16 +97,17 @@ EXPORTS.mozilla = [
     'Types.h',
     'TypeTraits.h',
     'UniquePtr.h',
     'UniquePtrExtensions.h',
     'Unused.h',
     'Variant.h',
     'Vector.h',
     'WeakPtr.h',
+    'WrappingOperations.h',
     'XorShift128PlusRNG.h',
 ]
 
 EXPORTS["double-conversion"] = [
     'double-conversion/double-conversion/double-conversion.h',
     'double-conversion/double-conversion/utils.h',
 ]
 
--- a/mfbt/tests/TestMathAlgorithms.cpp
+++ b/mfbt/tests/TestMathAlgorithms.cpp
@@ -5,17 +5,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/MathAlgorithms.h"
 
 #include <stdint.h>
 
 using mozilla::Clamp;
 using mozilla::IsPowerOfTwo;
-using mozilla::WrapToSigned;
 
 static void
 TestClamp()
 {
   MOZ_RELEASE_ASSERT(Clamp(0, 0, 0) == 0);
   MOZ_RELEASE_ASSERT(Clamp(1, 0, 0) == 0);
   MOZ_RELEASE_ASSERT(Clamp(-1, 0, 0) == 0);
 
@@ -75,65 +74,16 @@ TestIsPowerOfTwo()
 
   static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX/2)), "0x7fffffffffffffff isn't a power of two");
   static_assert(IsPowerOfTwo(uint64_t(UINT64_MAX/2 + 1)), "0x8000000000000000 is a power of two");
   static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX/2 + 2)), "0x8000000000000001 isn't a power of two");
   static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX - 1)), "0xfffffffffffffffe isn't a power of two");
   static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX)), "0xffffffffffffffff isn't a power of two");
 }
 
-// NOTE: In places below |-FOO_MAX - 1| is used instead of |-FOO_MIN| because
-//       in C++ numeric literals are full expressions -- the |-| in a negative
-//       number is technically separate.  So with most compilers that limit
-//       |int| to the signed 32-bit range, something like |-2147483648| is
-//       operator-() applied to an *unsigned* expression.  And MSVC, at least,
-//       warns when you do that.  (The operation is well-defined, but it likely
-//       doesn't do what was intended.)  So we do the usual workaround for this
-//       (see your local copy of <stdint.h> for a likely demo of this), writing
-//       it out by negating the max value and subtracting 1.
-
-static_assert(WrapToSigned(uint8_t(17)) == 17,
-              "no wraparound should work, 8-bit");
-static_assert(WrapToSigned(uint8_t(128)) == -128,
-              "works for 8-bit numbers, wraparound low end");
-static_assert(WrapToSigned(uint8_t(128 + 7)) == -128 + 7,
-              "works for 8-bit numbers, wraparound mid");
-static_assert(WrapToSigned(uint8_t(128 + 127)) == -128 + 127,
-              "works for 8-bit numbers, wraparound high end");
-
-static_assert(WrapToSigned(uint16_t(12345)) == 12345,
-              "no wraparound should work, 16-bit");
-static_assert(WrapToSigned(uint16_t(32768)) == -32768,
-              "works for 16-bit numbers, wraparound low end");
-static_assert(WrapToSigned(uint16_t(32768 + 42)) == -32768 + 42,
-              "works for 16-bit numbers, wraparound mid");
-static_assert(WrapToSigned(uint16_t(32768 + 32767)) == -32768 + 32767,
-              "works for 16-bit numbers, wraparound high end");
-
-static_assert(WrapToSigned(uint32_t(8675309)) == 8675309,
-              "no wraparound should work, 32-bit");
-static_assert(WrapToSigned(uint32_t(2147483648)) == -2147483647 - 1,
-              "works for 32-bit numbers, wraparound low end");
-static_assert(WrapToSigned(uint32_t(2147483648 + 42)) == -2147483647 - 1 + 42,
-              "works for 32-bit numbers, wraparound mid");
-static_assert(WrapToSigned(uint32_t(2147483648 + 2147483647)) ==
-                -2147483647 - 1 + 2147483647,
-              "works for 32-bit numbers, wraparound high end");
-
-static_assert(WrapToSigned(uint64_t(4152739164)) == 4152739164,
-              "no wraparound should work, 64-bit");
-static_assert(WrapToSigned(uint64_t(9223372036854775808ULL)) == -9223372036854775807LL - 1,
-              "works for 64-bit numbers, wraparound low end");
-static_assert(WrapToSigned(uint64_t(9223372036854775808ULL + 8005552368LL)) ==
-                -9223372036854775807LL - 1 + 8005552368LL,
-              "works for 64-bit numbers, wraparound mid");
-static_assert(WrapToSigned(uint64_t(9223372036854775808ULL + 9223372036854775807ULL)) ==
-                -9223372036854775807LL - 1 + 9223372036854775807LL,
-              "works for 64-bit numbers, wraparound high end");
-
 int
 main()
 {
   TestIsPowerOfTwo();
   TestClamp();
 
   return 0;
 }
copy from mfbt/tests/TestMathAlgorithms.cpp
copy to mfbt/tests/TestWrappingOperations.cpp
--- a/mfbt/tests/TestMathAlgorithms.cpp
+++ b/mfbt/tests/TestWrappingOperations.cpp
@@ -1,90 +1,22 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/MathAlgorithms.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/WrappingOperations.h"
 
 #include <stdint.h>
 
-using mozilla::Clamp;
-using mozilla::IsPowerOfTwo;
+using mozilla::WrappingMultiply;
 using mozilla::WrapToSigned;
 
-static void
-TestClamp()
-{
-  MOZ_RELEASE_ASSERT(Clamp(0, 0, 0) == 0);
-  MOZ_RELEASE_ASSERT(Clamp(1, 0, 0) == 0);
-  MOZ_RELEASE_ASSERT(Clamp(-1, 0, 0) == 0);
-
-  MOZ_RELEASE_ASSERT(Clamp(0, 1, 1) == 1);
-  MOZ_RELEASE_ASSERT(Clamp(0, 1, 2) == 1);
-
-  MOZ_RELEASE_ASSERT(Clamp(0, -1, -1) == -1);
-  MOZ_RELEASE_ASSERT(Clamp(0, -2, -1) == -1);
-
-  MOZ_RELEASE_ASSERT(Clamp(0, 1, 3) == 1);
-  MOZ_RELEASE_ASSERT(Clamp(1, 1, 3) == 1);
-  MOZ_RELEASE_ASSERT(Clamp(2, 1, 3) == 2);
-  MOZ_RELEASE_ASSERT(Clamp(3, 1, 3) == 3);
-  MOZ_RELEASE_ASSERT(Clamp(4, 1, 3) == 3);
-  MOZ_RELEASE_ASSERT(Clamp(5, 1, 3) == 3);
-
-  MOZ_RELEASE_ASSERT(Clamp<uint8_t>(UINT8_MAX, 0, UINT8_MAX) == UINT8_MAX);
-  MOZ_RELEASE_ASSERT(Clamp<uint8_t>(0, 0, UINT8_MAX) == 0);
-
-  MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MIN, INT8_MIN, INT8_MAX) == INT8_MIN);
-  MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MIN, 0, INT8_MAX) == 0);
-  MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MAX, INT8_MIN, INT8_MAX) == INT8_MAX);
-  MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MAX, INT8_MIN, 0) == 0);
-}
-
-static void
-TestIsPowerOfTwo()
-{
-  static_assert(!IsPowerOfTwo(0u), "0 isn't a power of two");
-  static_assert(IsPowerOfTwo(1u), "1 is a power of two");
-  static_assert(IsPowerOfTwo(2u), "2 is a power of two");
-  static_assert(!IsPowerOfTwo(3u), "3 isn't a power of two");
-  static_assert(IsPowerOfTwo(4u), "4 is a power of two");
-  static_assert(!IsPowerOfTwo(5u), "5 isn't a power of two");
-  static_assert(!IsPowerOfTwo(6u), "6 isn't a power of two");
-  static_assert(!IsPowerOfTwo(7u), "7 isn't a power of two");
-  static_assert(IsPowerOfTwo(8u), "8 is a power of two");
-  static_assert(!IsPowerOfTwo(9u), "9 isn't a power of two");
-
-  static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX/2)), "127, 0x7f isn't a power of two");
-  static_assert(IsPowerOfTwo(uint8_t(UINT8_MAX/2 + 1)), "128, 0x80 is a power of two");
-  static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX/2 + 2)), "129, 0x81 isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX - 1)), "254, 0xfe isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX)), "255, 0xff isn't a power of two");
-
-  static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX/2)), "0x7fff isn't a power of two");
-  static_assert(IsPowerOfTwo(uint16_t(UINT16_MAX/2 + 1)), "0x8000 is a power of two");
-  static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX/2 + 2)), "0x8001 isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX - 1)), "0xfffe isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX)), "0xffff isn't a power of two");
-
-  static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX/2)), "0x7fffffff isn't a power of two");
-  static_assert(IsPowerOfTwo(uint32_t(UINT32_MAX/2 + 1)), "0x80000000 is a power of two");
-  static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX/2 + 2)), "0x80000001 isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX - 1)), "0xfffffffe isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX)), "0xffffffff isn't a power of two");
-
-  static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX/2)), "0x7fffffffffffffff isn't a power of two");
-  static_assert(IsPowerOfTwo(uint64_t(UINT64_MAX/2 + 1)), "0x8000000000000000 is a power of two");
-  static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX/2 + 2)), "0x8000000000000001 isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX - 1)), "0xfffffffffffffffe isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX)), "0xffffffffffffffff isn't a power of two");
-}
-
 // NOTE: In places below |-FOO_MAX - 1| is used instead of |-FOO_MIN| because
 //       in C++ numeric literals are full expressions -- the |-| in a negative
 //       number is technically separate.  So with most compilers that limit
 //       |int| to the signed 32-bit range, something like |-2147483648| is
 //       operator-() applied to an *unsigned* expression.  And MSVC, at least,
 //       warns when you do that.  (The operation is well-defined, but it likely
 //       doesn't do what was intended.)  So we do the usual workaround for this
 //       (see your local copy of <stdint.h> for a likely demo of this), writing
@@ -124,16 +56,193 @@ static_assert(WrapToSigned(uint64_t(9223
               "works for 64-bit numbers, wraparound low end");
 static_assert(WrapToSigned(uint64_t(9223372036854775808ULL + 8005552368LL)) ==
                 -9223372036854775807LL - 1 + 8005552368LL,
               "works for 64-bit numbers, wraparound mid");
 static_assert(WrapToSigned(uint64_t(9223372036854775808ULL + 9223372036854775807ULL)) ==
                 -9223372036854775807LL - 1 + 9223372036854775807LL,
               "works for 64-bit numbers, wraparound high end");
 
+template<typename T>
+inline constexpr bool
+TestEqual(T aX, T aY)
+{
+  return aX == aY;
+}
+
+static void
+TestWrappingMultiply8()
+{
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(0), uint8_t(128)),
+                               uint8_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(128), uint8_t(1)),
+                               uint8_t(128)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(2), uint8_t(128)),
+                               uint8_t(0)),
+                     "2 times high bit overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(8), uint8_t(16)),
+                               uint8_t(128)),
+                     "multiply that populates the high bit produces that value");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(127), uint8_t(127)),
+                               uint8_t(1)),
+                     "multiplying signed maxvals overflows all the way to 1");
+
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(0), int8_t(-128)),
+                               int8_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(-128), int8_t(1)),
+                               int8_t(-128)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(2), int8_t(-128)),
+                               int8_t(0)),
+                     "2 times min overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(16), int8_t(24)),
+                               int8_t(-128)),
+                     "multiply that populates the sign bit produces minval");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(8), int8_t(16)),
+                               int8_t(-128)),
+                     "multiply that populates the sign bit produces minval");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(127), int8_t(127)),
+                               int8_t(1)),
+                     "multiplying maxvals overflows all the way to 1");
+}
+
+static void
+TestWrappingMultiply16()
+{
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(0), uint16_t(32768)),
+                               uint16_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(32768), uint16_t(1)),
+                               uint16_t(32768)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(2), uint16_t(32768)),
+                               uint16_t(0)),
+                     "2 times high bit overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(3), uint16_t(32768)),
+                               uint16_t(-32768)),
+                     "3 * 32768 - 65536 is 32768");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(64), uint16_t(512)),
+                               uint16_t(32768)),
+                     "multiply that populates the high bit produces that value");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(32767), uint16_t(32767)),
+                               uint16_t(1)),
+                    "multiplying signed maxvals overflows all the way to 1");
+
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(0), int16_t(-32768)),
+                               int16_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(-32768), int16_t(1)),
+                               int16_t(-32768)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(-456), int16_t(123)),
+                               int16_t(9448)),
+                     "multiply opposite signs, then add 2**16 for the result");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(2), int16_t(-32768)),
+                               int16_t(0)),
+                     "2 times min overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(64), int16_t(512)),
+                               int16_t(-32768)),
+                     "multiply that populates the sign bit produces minval");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(32767), int16_t(32767)),
+                               int16_t(1)),
+                     "multiplying maxvals overflows all the way to 1");
+}
+
+static void
+TestWrappingMultiply32()
+{
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(0), uint32_t(2147483648)),
+                               uint32_t(0)),
+                    "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(42), uint32_t(17)),
+                               uint32_t(714)),
+                     "42 * 17 is 714 without wraparound");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(2147483648), uint32_t(1)),
+                               uint32_t(2147483648)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(2), uint32_t(2147483648)),
+                               uint32_t(0)),
+                     "2 times high bit overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(8192), uint32_t(262144)),
+                               uint32_t(2147483648)),
+                     "multiply that populates the high bit produces that value");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(2147483647),
+                                                uint32_t(2147483647)),
+                               uint32_t(1)),
+                     "multiplying signed maxvals overflows all the way to 1");
+
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(0), int32_t(-2147483647 - 1)),
+                               int32_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(-2147483647 - 1), int32_t(1)),
+                               int32_t(-2147483647 - 1)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(2), int32_t(-2147483647 - 1)),
+                               int32_t(0)),
+                     "2 times min overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(-7), int32_t(-9)),
+                               int32_t(63)),
+                     "-7 * -9 is 63, no wraparound needed");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(8192), int32_t(262144)),
+                               int32_t(-2147483647 - 1)),
+                     "multiply that populates the sign bit produces minval");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(2147483647), int32_t(2147483647)),
+                               int32_t(1)),
+                     "multiplying maxvals overflows all the way to 1");
+}
+
+static void
+TestWrappingMultiply64()
+{
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(0),
+                                                uint64_t(9223372036854775808ULL)),
+                               uint64_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(9223372036854775808ULL),
+                                                uint64_t(1)),
+                               uint64_t(9223372036854775808ULL)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(2),
+                                                uint64_t(9223372036854775808ULL)),
+                               uint64_t(0)),
+                     "2 times high bit overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(131072),
+                                                uint64_t(70368744177664)),
+                               uint64_t(9223372036854775808ULL)),
+                     "multiply that populates the high bit produces that value");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(9223372036854775807),
+                                                uint64_t(9223372036854775807)),
+                               uint64_t(1)),
+                     "multiplying signed maxvals overflows all the way to 1");
+
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(0), int64_t(-9223372036854775807 - 1)),
+                               int64_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(-9223372036854775807 - 1),
+                                                int64_t(1)),
+                               int64_t(-9223372036854775807 - 1)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(2),
+                                                int64_t(-9223372036854775807 - 1)),
+                               int64_t(0)),
+                     "2 times min overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(131072),
+                                                int64_t(70368744177664)),
+                               int64_t(-9223372036854775807 - 1)),
+                     "multiply that populates the sign bit produces minval");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(9223372036854775807),
+                                                int64_t(9223372036854775807)),
+                               int64_t(1)),
+                     "multiplying maxvals overflows all the way to 1");
+}
+
 int
 main()
 {
-  TestIsPowerOfTwo();
-  TestClamp();
-
+  TestWrappingMultiply8();
+  TestWrappingMultiply16();
+  TestWrappingMultiply32();
+  TestWrappingMultiply64();
   return 0;
 }
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -53,16 +53,17 @@ CppUnitTests([
     'TestThreadSafeWeakPtr',
     'TestTuple',
     'TestTypedEnum',
     'TestTypeTraits',
     'TestUniquePtr',
     'TestVariant',
     'TestVector',
     'TestWeakPtr',
+    'TestWrappingOperations',
     'TestXorShift128PlusRNG',
 ])
 
 if not CONFIG['MOZ_ASAN']:
     CppUnitTests([
         'TestPoisonArea',
     ])
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -1675,57 +1675,57 @@ public class GeckoSession extends LayerS
              * Display choices in a list that allows multiple selections.
              */
             public static final int CHOICE_TYPE_MULTIPLE = 3;
 
             /**
              * A boolean indicating if the item is disabled. Item should not be
              * selectable if this is true.
              */
-            public boolean disabled;
+            public final boolean disabled;
 
             /**
              * A String giving the URI of the item icon, or null if none exists
              * (only valid for menus)
              */
-            public String icon;
+            public final String icon;
 
             /**
              * A String giving the ID of the item or group
              */
-            public String id;
+            public final String id;
 
             /**
              * A Choice array of sub-items in a group, or null if not a group
              */
-            public Choice[] items;
+            public final Choice[] items;
 
             /**
              * A string giving the label for displaying the item or group
              */
-            public String label;
+            public final String label;
 
             /**
              * A boolean indicating if the item should be pre-selected
              * (pre-checked for menu items)
              */
-            public boolean selected;
+            public final boolean selected;
 
             /**
              * A boolean indicating if the item should be a menu separator
              * (only valid for menus)
              */
-            public boolean separator;
+            public final boolean separator;
 
             /* package */ Choice(GeckoBundle choice) {
                 disabled = choice.getBoolean("disabled");
                 icon = choice.getString("icon");
                 id = choice.getString("id");
                 label = choice.getString("label");
-                selected = choice.getBoolean("label");
+                selected = choice.getBoolean("selected");
                 separator = choice.getBoolean("separator");
 
                 GeckoBundle[] choices = choice.getBundleArray("items");
                 if (choices == null) {
                     items = null;
                 } else {
                     items = new Choice[choices.length];
                     for (int i = 0; i < choices.length; i++) {
@@ -2053,43 +2053,43 @@ public class GeckoSession extends LayerS
             /**
              * The media type is audio.
              */
             public static final int TYPE_AUDIO = 1;
 
             /**
              * A string giving the origin-specific source identifier.
              */
-            public String id;
+            public final String id;
 
             /**
              * A string giving the non-origin-specific source identifier.
              */
-            public String rawId;
+            public final String rawId;
 
             /**
              * A string giving the name of the video source from the system
              * (for example, "Camera 0, Facing back, Orientation 90").
              * May be empty.
              */
-            public String name;
+            public final String name;
 
             /**
              * An int giving the media source type.
              * Possible values for a video source are:
              * SOURCE_CAMERA, SOURCE_SCREEN, SOURCE_APPLICATION, SOURCE_WINDOW, SOURCE_BROWSER, and SOURCE_OTHER.
              * Possible values for an audio source are:
              * SOURCE_MICROPHONE, SOURCE_AUDIOCAPTURE, and SOURCE_OTHER.
              */
-            public int source;
+            public final int source;
 
             /**
              * An int giving the type of media, must be either TYPE_VIDEO or TYPE_AUDIO.
              */
-            public int type;
+            public final int type;
 
             private static int getSourceFromString(String src) {
                 // The strings here should match those in MediaSourceEnum in MediaStreamTrack.webidl
                 if ("camera".equals(src)) {
                     return SOURCE_CAMERA;
                 } else if ("screen".equals(src)) {
                     return SOURCE_SCREEN;
                 } else if ("application".equals(src)) {
@@ -2115,19 +2115,19 @@ public class GeckoSession extends LayerS
                     return TYPE_VIDEO;
                 } else if ("audio".equals(type)) {
                     return TYPE_AUDIO;
                 } else {
                     throw new IllegalArgumentException("String: " + type + " is not a valid media type string");
                 }
             }
 
-            public MediaSource(GeckoBundle media) {
+            /* package */ MediaSource(GeckoBundle media) {
                 id = media.getString("id");
-                rawId = media.getString("id");
+                rawId = media.getString("rawId");
                 name = media.getString("name");
                 source = getSourceFromString(media.getString("source"));
                 type = getTypeFromString(media.getString("type"));
             }
         }
 
         /**
          * Callback interface for notifying the result of a media permission request,
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java
@@ -255,29 +255,46 @@ final class BasicGeckoViewPrompt impleme
                         } else {
                             callback.confirm(password.getText().toString());
                         }
                     }
                 });
         createStandardDialog(addCheckbox(builder, container, callback), callback).show();
     }
 
-    private void addChoiceItems(final int type, final ArrayAdapter<Choice> list,
+    private static class ModifiableChoice {
+        public boolean modifiableSelected;
+        public String modifiableLabel;
+        public final Choice choice;
+
+        public ModifiableChoice(Choice c) {
+            choice = c;
+            modifiableSelected = choice.selected;
+            modifiableLabel = choice.label;
+        }
+    }
+
+    private void addChoiceItems(final int type, final ArrayAdapter<ModifiableChoice> list,
                                 final Choice[] items, final String indent) {
         if (type == Choice.CHOICE_TYPE_MENU) {
-            list.addAll(items);
+            for (final Choice item : items) {
+                list.add(new ModifiableChoice(item));
+            }
             return;
         }
 
         for (final Choice item : items) {
+            final ModifiableChoice modItem = new ModifiableChoice(item);
+
             final Choice[] children = item.items;
+
             if (indent != null && children == null) {
-                item.label = indent + item.label;
+                modItem.modifiableLabel = indent + modItem.modifiableLabel;
             }
-            list.add(item);
+            list.add(modItem);
 
             if (children != null) {
                 final String newIndent;
                 if (type == Choice.CHOICE_TYPE_SINGLE || type == Choice.CHOICE_TYPE_MULTIPLE) {
                     newIndent = (indent != null) ? indent + '\t' : "\t";
                 } else {
                     newIndent = null;
                 }
@@ -297,17 +314,17 @@ final class BasicGeckoViewPrompt impleme
         final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
         addStandardLayout(builder, title, msg);
 
         final ListView list = new ListView(builder.getContext());
         if (type == Choice.CHOICE_TYPE_MULTIPLE) {
             list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
         }
 
-        final ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(
+        final ArrayAdapter<ModifiableChoice> adapter = new ArrayAdapter<ModifiableChoice>(
                 builder.getContext(), android.R.layout.simple_list_item_1) {
             private static final int TYPE_MENU_ITEM = 0;
             private static final int TYPE_MENU_CHECK = 1;
             private static final int TYPE_SEPARATOR = 2;
             private static final int TYPE_GROUP = 3;
             private static final int TYPE_SINGLE = 4;
             private static final int TYPE_MULTIPLE = 5;
             private static final int TYPE_COUNT = 6;
@@ -317,38 +334,38 @@ final class BasicGeckoViewPrompt impleme
 
             @Override
             public int getViewTypeCount() {
                 return TYPE_COUNT;
             }
 
             @Override
             public int getItemViewType(final int position) {
-                final Choice item = getItem(position);
-                if (item.separator) {
+                final ModifiableChoice item = getItem(position);
+                if (item.choice.separator) {
                     return TYPE_SEPARATOR;
                 } else if (type == Choice.CHOICE_TYPE_MENU) {
-                    return item.selected ? TYPE_MENU_CHECK : TYPE_MENU_ITEM;
-                } else if (item.items != null) {
+                    return item.modifiableSelected ? TYPE_MENU_CHECK : TYPE_MENU_ITEM;
+                } else if (item.choice.items != null) {
                     return TYPE_GROUP;
                 } else if (type == Choice.CHOICE_TYPE_SINGLE) {
                     return TYPE_SINGLE;
                 } else if (type == Choice.CHOICE_TYPE_MULTIPLE) {
                     return TYPE_MULTIPLE;
                 } else {
                     throw new UnsupportedOperationException();
                 }
             }
 
             @Override
             public boolean isEnabled(final int position) {
-                final Choice item = getItem(position);
-                return !item.separator && !item.disabled &&
+                final ModifiableChoice item = getItem(position);
+                return !item.choice.separator && !item.choice.disabled &&
                         ((type != Choice.CHOICE_TYPE_SINGLE && type != Choice.CHOICE_TYPE_MULTIPLE) ||
-                         item.items != null);
+                         item.choice.items != null);
             }
 
             @Override
             public View getView(final int position, View view,
                                 final ViewGroup parent) {
                 final int itemType = getItemViewType(position);
                 final int layoutId;
                 if (itemType == TYPE_SEPARATOR) {
@@ -378,22 +395,22 @@ final class BasicGeckoViewPrompt impleme
 
                 if (view == null) {
                     if (mInflater == null) {
                         mInflater = LayoutInflater.from(builder.getContext());
                     }
                     view = mInflater.inflate(layoutId, parent, false);
                 }
 
-                final Choice item = getItem(position);
+                final ModifiableChoice item = getItem(position);
                 final TextView text = (TextView) view;
-                text.setEnabled(!item.disabled);
-                text.setText(item.label);
+                text.setEnabled(!item.choice.disabled);
+                text.setText(item.modifiableLabel);
                 if (view instanceof CheckedTextView) {
-                    final boolean selected = item.selected;
+                    final boolean selected = item.modifiableSelected;
                     if (itemType == TYPE_MULTIPLE) {
                         list.setItemChecked(position, selected);
                     } else {
                         ((CheckedTextView) view).setChecked(selected);
                     }
                 }
                 return view;
             }
@@ -405,53 +422,53 @@ final class BasicGeckoViewPrompt impleme
 
         final AlertDialog dialog;
         if (type == Choice.CHOICE_TYPE_SINGLE || type == Choice.CHOICE_TYPE_MENU) {
             dialog = createStandardDialog(builder, callback);
             list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                 @Override
                 public void onItemClick(final AdapterView<?> parent, final View v,
                                         final int position, final long id) {
-                    final Choice item = adapter.getItem(position);
+                    final ModifiableChoice item = adapter.getItem(position);
                     if (type == Choice.CHOICE_TYPE_MENU) {
-                        final Choice[] children = item.items;
+                        final Choice[] children = item.choice.items;
                         if (children != null) {
                             // Show sub-menu.
                             dialog.setOnDismissListener(null);
                             dialog.dismiss();
-                            onChoicePrompt(session, item.label, /* msg */ null,
+                            onChoicePrompt(session, item.modifiableLabel, /* msg */ null,
                                             type, children, callback);
                             return;
                         }
                     }
-                    callback.confirm(item);
+                    callback.confirm(item.choice);
                     dialog.dismiss();
                 }
             });
         } else if (type == Choice.CHOICE_TYPE_MULTIPLE) {
             list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                 @Override
                 public void onItemClick(final AdapterView<?> parent, final View v,
                                         final int position, final long id) {
-                    final Choice item = adapter.getItem(position);
-                    item.selected = ((CheckedTextView) v).isChecked();
+                    final ModifiableChoice item = adapter.getItem(position);
+                    item.modifiableSelected = ((CheckedTextView) v).isChecked();
                 }
             });
             builder.setNegativeButton(android.R.string.cancel, /* listener */ null)
                    .setPositiveButton(android.R.string.ok,
                                       new DialogInterface.OnClickListener() {
                 @Override
                 public void onClick(final DialogInterface dialog,
                                     final int which) {
                     final int len = adapter.getCount();
                     ArrayList<String> items = new ArrayList<>(len);
                     for (int i = 0; i < len; i++) {
-                        final Choice item = adapter.getItem(i);
-                        if (item.selected) {
-                            items.add(item.id);
+                        final ModifiableChoice item = adapter.getItem(i);
+                        if (item.modifiableSelected) {
+                            items.add(item.choice.id);
                         }
                     }
                     callback.confirm(items.toArray(new String[items.size()]));
                 }
             });
             dialog = createStandardDialog(builder, callback);
         } else {
             throw new UnsupportedOperationException();
@@ -804,23 +821,23 @@ final class BasicGeckoViewPrompt impleme
                    public void onDismiss(final DialogInterface dialog) {
                        callback.reject();
                    }
                });
         dialog.show();
     }
 
     private Spinner addMediaSpinner(final Context context, final ViewGroup container,
-                                    final MediaSource[] sources) {
+                                    final MediaSource[] sources, final String[] sourceNames) {
         final ArrayAdapter<MediaSource> adapter = new ArrayAdapter<MediaSource>(
                 context, android.R.layout.simple_spinner_item) {
             private View convertView(final int position, final View view) {
                 if (view != null) {
                     final MediaSource item = getItem(position);
-                    ((TextView) view).setText(item.name);
+                    ((TextView) view).setText(sourceNames != null ? sourceNames[position] : item.name);
                 }
                 return view;
             }
 
             @Override
             public View getView(final int position, View view,
                                 final ViewGroup parent) {
                 return convertView(position, super.getView(position, view, parent));
@@ -839,35 +856,36 @@ final class BasicGeckoViewPrompt impleme
         spinner.setAdapter(adapter);
         spinner.setSelection(0);
         container.addView(spinner);
         return spinner;
     }
 
     public void onMediaPrompt(final GeckoSession session, final String title,
                                final MediaSource[] video, final MediaSource[] audio,
+                               final String[] videoNames, final String[] audioNames,
                                final GeckoSession.PermissionDelegate.MediaCallback callback) {
         final Activity activity = mActivity;
         if (activity == null || (video == null && audio == null)) {
             callback.reject();
             return;
         }
         final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
         final LinearLayout container = addStandardLayout(builder, title, /* msg */ null);
 
         final Spinner videoSpinner;
         if (video != null) {
-            videoSpinner = addMediaSpinner(builder.getContext(), container, video);
+            videoSpinner = addMediaSpinner(builder.getContext(), container, video, videoNames);
         } else {
             videoSpinner = null;
         }
 
         final Spinner audioSpinner;
         if (audio != null) {
-            audioSpinner = addMediaSpinner(builder.getContext(), container, audio);
+            audioSpinner = addMediaSpinner(builder.getContext(), container, audio, audioNames);
         } else {
             audioSpinner = null;
         }
 
         builder.setNegativeButton(android.R.string.cancel, /* listener */ null)
                .setPositiveButton(android.R.string.ok,
                                   new DialogInterface.OnClickListener() {
                     @Override
@@ -884,9 +902,15 @@ final class BasicGeckoViewPrompt impleme
         dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
                     @Override
                     public void onDismiss(final DialogInterface dialog) {
                         callback.reject();
                     }
                 });
         dialog.show();
     }
+
+    public void onMediaPrompt(final GeckoSession session, final String title,
+                               final MediaSource[] video, final MediaSource[] audio,
+                               final GeckoSession.PermissionDelegate.MediaCallback callback) {
+        onMediaPrompt(session, title, video, audio, null, null, callback);
+    }
 }
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -286,60 +286,63 @@ public class GeckoViewActivity extends A
             }
 
             final String title = getString(resId, Uri.parse(uri).getAuthority());
             final BasicGeckoViewPrompt prompt = (BasicGeckoViewPrompt)
                     mGeckoSession.getPromptDelegate();
             prompt.onPermissionPrompt(session, title, callback);
         }
 
-        private void normalizeMediaName(final MediaSource[] sources) {
+        private String[] normalizeMediaName(final MediaSource[] sources) {
             if (sources == null) {
-                return;
+                return null;
             }
-            for (final MediaSource source : sources) {
-                final int mediaSource = source.source;
-                String name = source.name;
+
+            String[] res = new String[sources.length];
+            for (int i = 0; i < sources.length; i++) {
+                final int mediaSource = sources[i].source;
+                final String name = sources[i].name;
                 if (MediaSource.SOURCE_CAMERA == mediaSource) {
                     if (name.toLowerCase(Locale.ENGLISH).contains("front")) {
-                        name = getString(R.string.media_front_camera);
+                        res[i] = getString(R.string.media_front_camera);
                     } else {
-                        name = getString(R.string.media_back_camera);
+                        res[i] = getString(R.string.media_back_camera);
                     }
                 } else if (!name.isEmpty()) {
-                    continue;
+                    res[i] = name;
                 } else if (MediaSource.SOURCE_MICROPHONE == mediaSource) {
-                    name = getString(R.string.media_microphone);
+                    res[i] = getString(R.string.media_microphone);
                 } else {
-                    name = getString(R.string.media_other);
+                    res[i] = getString(R.string.media_other);
                 }
-                source.name = name;
             }
+
+            return res;
         }
 
         @Override
         public void onMediaPermissionRequest(final GeckoSession session, final String uri,
                                            final MediaSource[] video, final MediaSource[] audio,
                                            final MediaCallback callback) {
             final String host = Uri.parse(uri).getAuthority();
             final String title;
             if (audio == null) {
                 title = getString(R.string.request_video, host);
             } else if (video == null) {
                 title = getString(R.string.request_audio, host);
             } else {
                 title = getString(R.string.request_media, host);
             }
 
-            normalizeMediaName(video);
-            normalizeMediaName(audio);
+            String[] videoNames = normalizeMediaName(video);
+            String[] audioNames = normalizeMediaName(audio);
 
             final BasicGeckoViewPrompt prompt = (BasicGeckoViewPrompt)
                     mGeckoSession.getPromptDelegate();
-            prompt.onMediaPrompt(session, title, video, audio, callback);
+            prompt.onMediaPrompt(session, title, video, audio, videoNames, audioNames, callback);
         }
     }
 
     private class Navigation implements GeckoSession.NavigationDelegate {
         @Override
         public void onLocationChange(GeckoSession session, final String url) {
         }
 
--- a/toolkit/.eslintrc.js
+++ b/toolkit/.eslintrc.js
@@ -1,11 +1,11 @@
 "use strict";
 
 module.exports = {
   rules: {
     // XXX Bug 1326071 - This should be reduced down - probably to 20 or to
     // be removed & synced with the mozilla/recommended value.
-    "complexity": ["error", 44],
+    "complexity": ["error", 45],
 
     "mozilla/no-task": "error",
   }
 };
--- a/toolkit/components/extensions/ExtensionChild.jsm
+++ b/toolkit/components/extensions/ExtensionChild.jsm
@@ -85,35 +85,43 @@ function injectAPI(source, dest) {
  * A finalization witness helper that wraps a sendMessage response and
  * guarantees to either get the promise resolved, or rejected when the
  * wrapped promise goes out of scope.
  *
  * Holding a reference to a returned StrongPromise doesn't prevent the
  * wrapped promise from being garbage collected.
  */
 const StrongPromise = {
+  locations: new Map(),
+
   wrap(promise, channelId, location) {
     return new Promise((resolve, reject) => {
-      const tag = `${channelId}|${location}`;
-      const witness = finalizationService.make("extensions-sendMessage-witness", tag);
+      this.locations.set(channelId, location);
+
+      const witness = finalizationService.make("extensions-sendMessage-witness", channelId);
       promise.then(value => {
+        this.locations.delete(channelId);
         witness.forget();
         resolve(value);
       }, error => {
+        this.locations.delete(channelId);
         witness.forget();
         reject(error);
       });
     });
   },
-  observe(subject, topic, tag) {
-    const pos = tag.indexOf("|");
-    const channel = Number(tag.substr(0, pos));
-    const location = tag.substr(pos + 1);
-    const message = `Promised response from onMessage listener at ${location} went out of scope`;
-    MessageChannel.abortChannel(channel, {message});
+  observe(subject, topic, channelId) {
+    channelId = Number(channelId);
+    let location = this.locations.get(channelId);
+    this.locations.delete(channelId);
+
+    const message = `Promised response from onMessage listener went out of scope`;
+    const error = ChromeUtils.createError(message, location);
+    error.mozWebExtLocation = location;
+    MessageChannel.abortChannel(channelId, error);
   },
 };
 Services.obs.addObserver(StrongPromise, "extensions-sendMessage-witness");
 
 /**
  * Abstraction for a Port object in the extension API.
  *
  * @param {BaseContext} context The context that owns this port.
@@ -376,32 +384,32 @@ class Messenger {
   sendMessage(messageManager, msg, recipient, responseCallback) {
     let holder = new StructuredCloneHolder(msg);
 
     let promise = this._sendMessage(messageManager, "Extension:Message", holder, recipient)
       .catch(error => {
         if (error.result == MessageChannel.RESULT_NO_HANDLER) {
           return Promise.reject({message: "Could not establish connection. Receiving end does not exist."});
         } else if (error.result != MessageChannel.RESULT_NO_RESPONSE) {
-          return Promise.reject({message: error.message});
+          return Promise.reject(error);
         }
       });
     holder = null;
 
     return this.context.wrapPromise(promise, responseCallback);
   }
 
   sendNativeMessage(messageManager, msg, recipient, responseCallback) {
     msg = NativeApp.encodeMessage(this.context, msg);
     return this.sendMessage(messageManager, msg, recipient, responseCallback);
   }
 
   _onMessage(name, filter) {
     return new EventManager(this.context, name, fire => {
-      const [location] = new this.context.cloneScope.Error().stack.split("\n", 1);
+      const caller = this.context.getCaller();
 
       let listener = {
         messageFilterPermissive: this.optionalFilter,
         messageFilterStrict: this.filter,
 
         filterMessage: (sender, recipient) => {
           // Exclude messages coming from content scripts for the devtools extension contexts
           // (See Bug 1383310).
@@ -435,19 +443,19 @@ class Messenger {
           sendResponse = Cu.exportFunction(sendResponse, this.context.cloneScope);
 
           // Note: We intentionally do not use runSafe here so that any
           // errors are propagated to the message sender.
           let result = fire.raw(message, sender, sendResponse);
           message = null;
 
           if (result instanceof this.context.cloneScope.Promise) {
-            return StrongPromise.wrap(result, channelId, location);
+            return StrongPromise.wrap(result, channelId, caller);
           } else if (result === true) {
-            return StrongPromise.wrap(promise, channelId, location);
+            return StrongPromise.wrap(promise, channelId, caller);
           }
           return response;
         },
       };
 
       MessageChannel.addListener(this.messageManagers, "Extension:Message", listener);
       return () => {
         MessageChannel.removeListener(this.messageManagers, "Extension:Message", listener);
--- a/toolkit/components/extensions/ExtensionCommon.jsm
+++ b/toolkit/components/extensions/ExtensionCommon.jsm
@@ -267,39 +267,43 @@ class BaseContext {
   runSafe(callback, ...args) {
     return this.applySafe(callback, args);
   }
 
   runSafeWithoutClone(callback, ...args) {
     return this.applySafeWithoutClone(callback, args);
   }
 
-  applySafe(callback, args) {
+  applySafe(callback, args, caller) {
     if (this.unloaded) {
-      Cu.reportError("context.runSafe called after context unloaded");
+      Cu.reportError("context.runSafe called after context unloaded",
+                     caller);
     } else if (!this.active) {
-      Cu.reportError("context.runSafe called while context is inactive");
+      Cu.reportError("context.runSafe called while context is inactive",
+                     caller);
     } else {
       try {
         let {cloneScope} = this;
         args = args.map(arg => Cu.cloneInto(arg, cloneScope));
       } catch (e) {
         Cu.reportError(e);
         dump(`runSafe failure: cloning into ${this.cloneScope}: ${e}\n\n${filterStack(Error())}`);
       }
 
-      return this.applySafeWithoutClone(callback, args);
+      return this.applySafeWithoutClone(callback, args, caller);
     }
   }
 
-  applySafeWithoutClone(callback, args) {
+  applySafeWithoutClone(callback, args, caller) {
     if (this.unloaded) {
-      Cu.reportError("context.runSafeWithoutClone called after context unloaded");
+      Cu.reportError("context.runSafeWithoutClone called after context unloaded",
+                     caller);
     } else if (!this.active) {
-      Cu.reportError("context.runSafeWithoutClone called while context is inactive");
+      Cu.reportError("context.runSafeWithoutClone called while context is inactive",
+                     caller);
     } else {
       try {
         return Reflect.apply(callback, null, args);
       } catch (e) {
         dump(`Extension error: ${e} ${e.fileName} ${e.lineNumber}\n[[Exception stack\n${filterStack(e)}Current stack\n${filterStack(Error())}]]\n`);
         Cu.reportError(e);
       }
     }
@@ -380,59 +384,84 @@ class BaseContext {
    * the target is an error object which belongs to that scope, it is
    * returned as-is. If it is an ordinary object with a `message`
    * property, it is converted into an error belonging to the target
    * scope. If it is an Error object which does *not* belong to the
    * clone scope, it is reported, and converted to an unexpected
    * exception error.
    *
    * @param {Error|object} error
+   * @param {SavedFrame?} [caller]
    * @returns {Error}
    */
-  normalizeError(error) {
+  normalizeError(error, caller) {
     if (error instanceof this.cloneScope.Error) {
       return error;
     }
     let message, fileName;
-    if (error && typeof error === "object" &&
-        (ChromeUtils.getClassName(error) === "Object" ||
-         error instanceof ExtensionError ||
-         this.principal.subsumes(Cu.getObjectPrincipal(error)))) {
-      message = error.message;
-      fileName = error.fileName;
-    } else {
+    if (error && typeof error === "object") {
+      const isPlain = ChromeUtils.getClassName(error) === "Object";
+      if (isPlain && error.mozWebExtLocation) {
+        caller = error.mozWebExtLocation;
+      }
+      if (isPlain && caller && (error.mozWebExtLocation || !error.fileName)) {
+        caller = Cu.cloneInto(caller, this.cloneScope);
+        return ChromeUtils.createError(error.message, caller);
+      }
+
+      if (isPlain ||
+          error instanceof ExtensionError ||
+          this.principal.subsumes(Cu.getObjectPrincipal(error))) {
+        message = error.message;
+        fileName = error.fileName;
+      }
+    }
+
+    if (!message) {
       Cu.reportError(error);
+      message = "An unexpected error occurred";
     }
-    message = message || "An unexpected error occurred";
     return new this.cloneScope.Error(message, fileName);
   }
 
   /**
    * Sets the value of `.lastError` to `error`, calls the given
    * callback, and reports an error if the value has not been checked
    * when the callback returns.
    *
    * @param {object} error An object with a `message` property. May
    *     optionally be an `Error` object belonging to the target scope.
+   * @param {SavedFrame?} caller
+   *        The optional caller frame which triggered this callback, to be used
+   *        in error reporting.
    * @param {function} callback The callback to call.
    * @returns {*} The return value of callback.
    */
-  withLastError(error, callback) {
+  withLastError(error, caller, callback) {
     this.lastError = this.normalizeError(error);
     try {
       return callback();
     } finally {
       if (!this.checkedLastError) {
-        Cu.reportError(`Unchecked lastError value: ${this.lastError}`);
+        Cu.reportError(`Unchecked lastError value: ${this.lastError}`, caller);
       }
       this.lastError = null;
     }
   }
 
   /**
+   * Captures the most recent stack frame which belongs to the extension.
+   *
+   * @returns {SavedFrame?}
+   */
+  getCaller() {
+    return ChromeUtils.getCallerLocation(this.principal);
+  }
+
+  /**
    * Wraps the given promise so it can be safely returned to extension
    * code in this context.
    *
    * If `callback` is provided, however, it is used as a completion
    * function for the promise, and no promise is returned. In this case,
    * the callback is called when the promise resolves or rejects. In the
    * latter case, `lastError` is set to the rejection value, and the
    * callback function must check `browser.runtime.lastError` or
@@ -448,71 +477,83 @@ class BaseContext {
    *     `callback` function or resolving the wrapped promise.
    *
    * @param {function} [callback] The callback function to wrap
    *
    * @returns {Promise|undefined} If callback is null, a promise object
    *     belonging to the target scope. Otherwise, undefined.
    */
   wrapPromise(promise, callback = null) {
+    let caller = this.getCaller();
     let applySafe = this.applySafe.bind(this);
     if (Cu.getGlobalForObject(promise) === this.cloneScope) {
       applySafe = this.applySafeWithoutClone.bind(this);
     }
 
     if (callback) {
       promise.then(
         args => {
           if (this.unloaded) {
-            dump(`Promise resolved after context unloaded\n`);
+            Cu.reportError(`Promise resolved after context unloaded\n`,
+                           caller);
           } else if (!this.active) {
-            dump(`Promise resolved while context is inactive\n`);
+            Cu.reportError(`Promise resolved while context is inactive\n`,
+                           caller);
           } else if (args instanceof NoCloneSpreadArgs) {
-            this.applySafeWithoutClone(callback, args.unwrappedValues);
+            this.applySafeWithoutClone(callback, args.unwrappedValues, caller);
           } else if (args instanceof SpreadArgs) {
-            applySafe(callback, args);
+            applySafe(callback, args, caller);
           } else {
-            applySafe(callback, [args]);
+            applySafe(callback, [args], caller);
           }
         },
         error => {
-          this.withLastError(error, () => {
+          this.withLastError(error, caller, () => {
             if (this.unloaded) {
-              dump(`Promise rejected after context unloaded\n`);
+              Cu.reportError(`Promise rejected after context unloaded\n`,
+                             caller);
             } else if (!this.active) {
-              dump(`Promise rejected while context is inactive\n`);
+              Cu.reportError(`Promise rejected while context is inactive\n`,
+                             caller);
             } else {
-              this.applySafeWithoutClone(callback, []);
+              this.applySafeWithoutClone(callback, [], caller);
             }
           });
         });
     } else {
       return new this.cloneScope.Promise((resolve, reject) => {
         promise.then(
           value => {
             if (this.unloaded) {
-              dump(`Promise resolved after context unloaded\n`);
+              Cu.reportError(`Promise resolved after context unloaded\n`,
+                             caller);
             } else if (!this.active) {
-              dump(`Promise resolved while context is inactive\n`);
+              Cu.reportError(`Promise resolved while context is inactive\n`,
+                             caller);
             } else if (value instanceof NoCloneSpreadArgs) {
               let values = value.unwrappedValues;
-              this.applySafeWithoutClone(resolve, values.length == 1 ? [values[0]] : [values]);
+              this.applySafeWithoutClone(resolve, values.length == 1 ? [values[0]] : [values],
+                                         caller);
             } else if (value instanceof SpreadArgs) {
-              applySafe(resolve, value.length == 1 ? value : [value]);
+              applySafe(resolve, value.length == 1 ? value : [value],
+                        caller);
             } else {
-              applySafe(resolve, [value]);
+              applySafe(resolve, [value], caller);
             }
           },
           value => {
             if (this.unloaded) {
-              dump(`Promise rejected after context unloaded: ${value && value.message}\n`);
+              Cu.reportError(`Promise rejected after context unloaded: ${value && value.message}\n`,
+                             caller);
             } else if (!this.active) {
-              dump(`Promise rejected while context is inactive: ${value && value.message}\n`);
+              Cu.reportError(`Promise rejected while context is inactive: ${value && value.message}\n`,
+                             caller);
             } else {
-              this.applySafeWithoutClone(reject, [this.normalizeError(value)]);
+              this.applySafeWithoutClone(reject, [this.normalizeError(value, caller)],
+                                         caller);
             }
           });
       });
     }
   }
 
   unload() {
     this.unloaded = true;
--- a/toolkit/components/extensions/MessageChannel.jsm
+++ b/toolkit/components/extensions/MessageChannel.jsm
@@ -968,17 +968,18 @@ this.MessageChannel = {
 
         if (error && typeof(error) == "object") {
           if (error.result) {
             response.result = error.result;
           }
           // Error objects are not structured-clonable, so just copy
           // over the important properties.
           for (let key of ["fileName", "filename", "lineNumber",
-                           "columnNumber", "message", "stack", "result"]) {
+                           "columnNumber", "message", "stack", "result",
+                           "mozWebExtLocation"]) {
             if (key in error) {
               response.error[key] = error[key];
             }
           }
         }
 
         target.sendAsyncMessage(MESSAGE_RESPONSE, response);
       })
--- a/toolkit/components/extensions/test/xpcshell/head.js
+++ b/toolkit/components/extensions/test/xpcshell/head.js
@@ -80,16 +80,17 @@ var promiseConsoleOutput = async functio
   let listener;
   let messages = [];
   let awaitListener = new Promise(resolve => {
     listener = msg => {
       if (msg == DONE) {
         resolve();
       } else {
         void (msg instanceof Ci.nsIConsoleMessage);
+        void (msg instanceof Ci.nsIScriptError);
         messages.push(msg);
       }
     };
   });
 
   Services.console.registerListener(listener);
   try {
     let result = await task();
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contexts.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contexts.js
@@ -16,16 +16,20 @@ class StubContext extends BaseContext {
     let fakeExtension = {id: "test@web.extension"};
     super("testEnv", fakeExtension);
     this.sandbox = Cu.Sandbox(global);
   }
 
   get cloneScope() {
     return this.sandbox;
   }
+
+  get principal() {
+    return Cu.getObjectPrincipal(this.sandbox);
+  }
 }
 
 
 add_task(async function test_post_unload_promises() {
   let context = new StubContext();
 
   let fail = result => {
     ok(false, `Unexpected callback: ${result}`);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_error_location.js
@@ -0,0 +1,48 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(async function test_error_location() {
+  let extension = ExtensionTestUtils.loadExtension({
+    async background() {
+      let {fileName} = new Error();
+
+      browser.test.sendMessage("fileName", fileName);
+
+      browser.runtime.sendMessage("Meh.", () => {});
+
+      await browser.test.assertRejects(
+        browser.runtime.sendMessage("Meh"),
+        error => {
+          return error.fileName === fileName && error.lineNumber === 9;
+        });
+
+      browser.test.notifyPass("error-location");
+    },
+  });
+
+  let fileName;
+  const {messages} = await promiseConsoleOutput(async () => {
+    await extension.startup();
+
+    fileName = await extension.awaitMessage("fileName");
+
+    await extension.awaitFinish("error-location");
+
+    await extension.unload();
+  });
+
+  let [msg] = messages.filter(m => m.message.includes("Unchecked lastError"));
+
+  equal(msg.sourceName, fileName, "Message source");
+  equal(msg.lineNumber, 6, "Message line");
+
+  let frame = msg.stack;
+  if (frame) {
+    equal(frame.source, fileName, "Frame source");
+    equal(frame.line, 6, "Frame line");
+    equal(frame.column, 7, "Frame column");
+    equal(frame.functionDisplayName, "background", "Frame function name");
+  }
+});
+
--- a/toolkit/components/extensions/test/xpcshell/test_ext_runtime_sendMessage.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_runtime_sendMessage.js
@@ -151,20 +151,33 @@ add_task(async function sendMessageRespo
     browser.test.onMessage.addListener(msg => {
       browser.runtime.sendMessage(msg)
         .then(
           response => {
             if (response) {
               browser.test.log(`Got response: ${response}`);
               browser.test.sendMessage(response);
             }
-          }, ({message}) => {
+          }, error => {
+            browser.test.assertEq(
+              "Promised response from onMessage listener went out of scope",
+              error.message,
+              `Promise rejected with the correct error message`);
+
             browser.test.assertTrue(
-              /at background@moz-extension:\/\/[\w-]+\/%7B[\w-]+%7D\.js:4:\d went out/.test(message),
-              `Promise rejected with the correct error message: ${message}`);
+              /^moz-extension:\/\/[\w-]+\/%7B[\w-]+%7D\.js/.test(error.fileName),
+              `Promise rejected with the correct error filename: ${error.fileName}`);
+
+            browser.test.assertEq(
+              4, error.lineNumber,
+              `Promise rejected with the correct error line number`);
+
+            browser.test.assertTrue(
+              /moz-extension:\/\/[\w-]+\/%7B[\w-]+%7D\.js:4/.test(error.stack),
+              `Promise rejected with the correct error stack: ${error.stack}`);
             browser.test.sendMessage("rejected");
           });
     });
     browser.test.sendMessage("ready");
   }
 
   let extension = ExtensionTestUtils.loadExtension({
     background,
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
@@ -25,16 +25,17 @@ skip-if = os == "android" # Containers a
 [test_ext_downloads_download.js]
 skip-if = os == "android"
 [test_ext_downloads_misc.js]
 skip-if = os == "android" || (os=='linux' && bits==32) # linux32: bug 1324870
 [test_ext_downloads_private.js]
 skip-if = os == "android"
 [test_ext_downloads_search.js]
 skip-if = os == "android"
+[test_ext_error_location.js]
 [test_ext_experiments.js]
 [test_ext_extension.js]
 [test_ext_extensionPreferencesManager.js]
 [test_ext_extensionSettingsStore.js]
 [test_ext_extension_content_telemetry.js]
 skip-if = os == "android" # checking for telemetry needs to be updated: 1384923
 [test_ext_extension_startup_telemetry.js]
 [test_ext_idle.js]
--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -133,18 +133,17 @@ nsFormFillController::~nsFormFillControl
   }
 }
 
 ////////////////////////////////////////////////////////////////////////
 //// nsIMutationObserver
 //
 
 void
-nsFormFillController::AttributeChanged(nsIDocument* aDocument,
-                                       mozilla::dom::Element* aElement,
+nsFormFillController::AttributeChanged(mozilla::dom::Element* aElement,
                                        int32_t aNameSpaceID,
                                        nsAtom* aAttribute, int32_t aModType,
                                        const nsAttrValue* aOldValue)
 {
   if ((aAttribute == nsGkAtoms::type || aAttribute == nsGkAtoms::readonly ||
        aAttribute == nsGkAtoms::autocomplete) &&
       aNameSpaceID == kNameSpaceID_None) {
     RefPtr<HTMLInputElement> focusedInput(mFocusedInput);
@@ -154,81 +153,71 @@ nsFormFillController::AttributeChanged(n
     // to avoid ending up in an endless loop due to re-registering our
     // mutation observer (which would notify us again for *this* event).
     nsCOMPtr<nsIRunnable> event =
       mozilla::NewRunnableMethod<RefPtr<HTMLInputElement>>(
         "nsFormFillController::MaybeStartControllingInput",
         this,
         &nsFormFillController::MaybeStartControllingInput,
         focusedInput);
-    aDocument->Dispatch(TaskCategory::Other, event.forget());
+    aElement->OwnerDoc()->Dispatch(TaskCategory::Other, event.forget());
   }
 
   if (mListNode && mListNode->Contains(aElement)) {
     RevalidateDataList();
   }
 }
 
 void
-nsFormFillController::ContentAppended(nsIDocument* aDocument,
-                                      nsIContent* aContainer,
-                                      nsIContent* aChild)
+nsFormFillController::ContentAppended(nsIContent* aChild)
 {
-  if (mListNode && mListNode->Contains(aContainer)) {
+  if (mListNode && mListNode->Contains(aChild->GetParent())) {
     RevalidateDataList();
   }
 }
 
 void
-nsFormFillController::ContentInserted(nsIDocument* aDocument,
-                                      nsIContent* aContainer,
-                                      nsIContent* aChild)
+nsFormFillController::ContentInserted(nsIContent* aChild)
 {
-  if (mListNode && mListNode->Contains(aContainer)) {
+  if (mListNode && mListNode->Contains(aChild->GetParent())) {
     RevalidateDataList();
   }
 }
 
 void
-nsFormFillController::ContentRemoved(nsIDocument* aDocument,
-                                     nsIContent* aContainer,
-                                     nsIContent* aChild,
+nsFormFillController::ContentRemoved(nsIContent* aChild,
                                      nsIContent* aPreviousSibling)
 {
-  if (mListNode && mListNode->Contains(aContainer)) {
+  if (mListNode && mListNode->Contains(aChild->GetParent())) {
     RevalidateDataList();
   }
 }
 
 void
-nsFormFillController::CharacterDataWillChange(nsIDocument* aDocument,
-                                              nsIContent* aContent,
+nsFormFillController::CharacterDataWillChange(nsIContent* aContent,
                                               const CharacterDataChangeInfo&)
 {
 }
 
 void
-nsFormFillController::CharacterDataChanged(nsIDocument* aDocument,
-                                           nsIContent* aContent,
+nsFormFillController::CharacterDataChanged(nsIContent* aContent,
                                            const CharacterDataChangeInfo&)
 {
 }
 
 void
-nsFormFillController::AttributeWillChange(nsIDocument* aDocument,
-                                          mozilla::dom::Element* aElement,
+nsFormFillController::AttributeWillChange(mozilla::dom::Element* aElement,
                                           int32_t aNameSpaceID,
                                           nsAtom* aAttribute, int32_t aModType,
                                           const nsAttrValue* aNewValue)
 {
 }
 
 void
-nsFormFillController::NativeAnonymousChildListChange(nsIDocument* aDocument,
-                                                     nsIContent* aContent,
+nsFormFillController::NativeAnonymousChildListChange(nsIContent* aContent,
                                                      bool aIsRemove)
 {
 }
 
 void
 nsFormFillController::ParentChainChanged(nsIContent* aContent)
 {
 }
--- a/toolkit/crashreporter/client/crashreporter.cpp
+++ b/toolkit/crashreporter/client/crashreporter.cpp
@@ -642,54 +642,46 @@ GetProgramPath(const string& exename)
   path.erase(pos);
   path.append(exename + BIN_SUFFIX);
 
   return path;
 }
 
 int main(int argc, char** argv)
 {
-  bool minidumpAllThreads = false;
-
   gArgc = argc;
   gArgv = argv;
 
   string autoSubmitEnv = UIGetEnv("MOZ_CRASHREPORTER_AUTO_SUBMIT");
-  if (!autoSubmitEnv.empty()) {
-    gAutoSubmit = true;
-  }
+  gAutoSubmit = !autoSubmitEnv.empty();
 
   if (!ReadConfig()) {
     UIError("Couldn't read configuration.");
     return 0;
   }
 
   if (!UIInit()) {
     return 0;
   }
 
-  if (argc == 3) {
-    if (!strcmp(argv[1], "--full")) {
-      minidumpAllThreads = true;
-    }
-    gReporterDumpFile = argv[2];
-  } else if (argc == 2) {
+  if (argc > 1) {
     gReporterDumpFile = argv[1];
   }
 
   if (gReporterDumpFile.empty()) {
     // no dump file specified, run the default UI
     if (!gAutoSubmit) {
       UIShowDefaultUI();
     }
   } else {
     // Start by running minidump analyzer to gather stack traces.
     string reporterDumpFile = gReporterDumpFile;
     vector<string> args = { reporterDumpFile };
-    if (minidumpAllThreads) {
+    string dumpAllThreadsEnv = UIGetEnv("MOZ_CRASHREPORTER_DUMP_ALL_THREADS");
+    if (!dumpAllThreadsEnv.empty()) {
       args.insert(args.begin(), "--full");
     }
     UIRunProgram(GetProgramPath(UI_MINIDUMP_ANALYZER_FILENAME),
                  args, /* wait */ true);
 
     // go ahead with the crash reporter
     gExtraFile = GetAdditionalFilename(gReporterDumpFile, kExtraDataExtension);
     if (gExtraFile.empty()) {
--- a/toolkit/crashreporter/client/crashreporter_unix_common.cpp
+++ b/toolkit/crashreporter/client/crashreporter_unix_common.cpp
@@ -22,17 +22,17 @@ struct FileData
   string path;
 };
 
 static bool CompareFDTime(const FileData& fd1, const FileData& fd2)
 {
   return fd1.timestamp > fd2.timestamp;
 }
 
-void UIPruneSavedDumps(const std::string& directory)
+void UIPruneSavedDumps(const string& directory)
 {
   DIR *dirfd = opendir(directory.c_str());
   if (!dirfd)
     return;
 
   vector<FileData> dumpfiles;
 
   while (dirent *dir = readdir(dirfd)) {
@@ -67,40 +67,37 @@ void UIPruneSavedDumps(const std::string
     // s/.dmp/.extra/
     path.replace(path.size() - 4, 4, ".extra");
     UIDeleteFile(path.c_str());
 
     dumpfiles.pop_back();
   }
 }
 
-bool UIRunProgram(const std::string& exename,
-                  const std::vector<std::string>& args,
-                  bool wait)
+bool UIRunProgram(const string& exename, const vector<string>& args, bool wait)
 {
   pid_t pid = fork();
 
   if (pid == -1) {
     return false;
   } else if (pid == 0) {
     // Child
     size_t argvLen = args.size() + 2;
-    char** argv = new char*[argvLen];
+    vector<char*> argv(argvLen);
 
     argv[0] = const_cast<char*>(exename.c_str());
 
     for (size_t i = 0; i < args.size(); i++) {
       argv[i + 1] = const_cast<char*>(args[i].c_str());
     }
 
     argv[argvLen - 1] = nullptr;
 
     // Run the program
-    int rv = execv(exename.c_str(), argv);
-    delete[] argv;
+    int rv = execv(exename.c_str(), argv.data());
 
     if (rv == -1) {
       exit(EXIT_FAILURE);
     }
   } else {
     // Parent
     if (wait) {
       waitpid(pid, nullptr, 0);
--- a/toolkit/crashreporter/client/ping.cpp
+++ b/toolkit/crashreporter/client/ping.cpp
@@ -281,23 +281,18 @@ GenerateSubmissionUrl(const string& aUrl
 static bool
 WritePing(const string& aPath, const string& aPing)
 {
   ofstream* f = UIOpenWrite(aPath.c_str());
   bool success = false;
 
   if (f->is_open()) {
     *f << aPing;
-    f->flush();
-
-    if (f->good()) {
-      success = true;
-    }
-
     f->close();
+    success = f->good();
   }
 
   delete f;
   return success;
 }
 
 // Assembles the crash ping using the strings extracted from the .extra file
 // and sends it using the crash sender. All the telemetry specific data but the
--- a/toolkit/crashreporter/docs/index.rst
+++ b/toolkit/crashreporter/docs/index.rst
@@ -210,8 +210,64 @@ are annotated with ``Hang=1``.
 about:crashes
 =============
 
 If the crash reporter subsystem is enabled, the *about:crashes*
 page will be registered with the application. This page provides
 information about previous and submitted crashes.
 
 It is also possible to submit crashes from *about:crashes*.
+
+Environment variables affecting crash reporting
+===============================================
+
+The exception handler and crash reporter client behavior can be altered by
+setting certain environment variables, some of these variables are used for
+testing but quite a few have only internal users.
+
+User-specified environment variables
+------------------------------------
+
+- ``MOZ_CRASHREPORTER`` - The opposite of MOZ_CRASHREPORTER_DISABLE, force
+  crash reporting on even if disabled in application.ini. You must use this to
+  enable crash reporting on debug builds.
+- ``MOZ_CRASHREPORTER_DISABLE`` - Disable Breakpad crash reporting completely
+  in non-debug builds. You can use this if you would rather use the JIT
+  debugger on Windows with the symbol server, for example.
+- ``MOZ_CRASHREPORTER_FULLDUMP`` - Store full application memory in the
+  minidump, so you can open it in a Microsoft debugger. Don't submit it to the
+  server. (Windows only.)
+- ``MOZ_CRASHREPORTER_NO_DELETE_DUMP`` - Don't delete the crash report dump
+  file after submitting it to the server. Minidumps will still be moved to the
+  "Crash Reports/pending" directory.
+- ``MOZ_CRASHREPORTER_NO_REPORT`` - Save the minidump file but don't launch the
+  crash reporting UI or send the report to the server. Minidumps will be stored
+  in the user's profile directory, in a subdirectory named "minidumps".
+- ``MOZ_CRASHREPORTER_SHUTDOWN`` - Save the minidump and then force the
+  application to close. This is useful for content crashes that don't normally
+  close the chrome (main application) processes. This variable would cause the
+  application to close as well.
+- ``MOZ_CRASHREPORTER_URL`` - Sets the URL that the crash reporter will submit
+  reports to.
+
+Environment variables used internally
+-------------------------------------
+
+- ``MOZ_CRASHREPORTER_AUTO_SUBMIT`` - When set causes the crash reporter client
+  to skip the UI flow and submit the crash report directly.
+- ``MOZ_CRASHREPORTER_DATA_DIRECTORY`` - Platform dependent data directory, the
+  pending crash reports will be stored in a subdirectory of this path. This
+  overrides the default one generated by the client's code.
+- ``MOZ_CRASHREPORTER_DUMP_ALL_THREADS`` - When set to 1 stack traces for
+  all threads are generated and sent in the crash ping, when not set only the
+  trace for the crashing thread will be generated instead.
+- ``MOZ_CRASHREPORTER_EVENTS_DIRECTORY`` - Path of the directory holding the
+  crash event files.
+- ``MOZ_CRASHREPORTER_PING_DIRECTORY`` - Path of the directory holding the
+  pending crash ping files.
+- ``MOZ_CRASHREPORTER_RESTART_ARG_<n>`` - Each of these variable specifies one
+  of the arguments that had been passed to the application, the crash reporter
+  client uses them for restarting it.
+- ``MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE`` - If a XUL app file was specified
+  when starting the app it has to be stored in this variable so that the crash
+  reporter client can restart the application.
+- ``MOZ_CRASHREPORTER_STRINGS_OVERRIDE`` - Overrides the path used to load the
+  .ini file holding the strings used in the crash reporter client UI.
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -195,17 +195,16 @@ static XP_CHAR* pendingDirectory;
 static XP_CHAR* crashReporterPath;
 static XP_CHAR* memoryReportPath;
 #ifdef XP_MACOSX
 static XP_CHAR* libraryPath; // Path where the NSS library is
 #endif // XP_MACOSX
 
 // Where crash events should go.
 static XP_CHAR* eventsDirectory;
-static char* eventsEnv = nullptr;
 
 // The current telemetry session ID to write to the event file
 static char* currentSessionId = nullptr;
 
 // If this is false, we don't launch the crash reporter
 static bool doReport = true;
 
 // if this is true, we pass the exception on to the OS crash reporter
@@ -227,17 +226,16 @@ static char* androidUserSerial = nullptr
 static Mutex* crashReporterAPILock;
 static Mutex* notesFieldLock;
 static AnnotationTable* crashReporterAPIData_Hash;
 static nsCString* crashReporterAPIData = nullptr;
 static nsCString* crashEventAPIData = nullptr;
 static nsCString* notesField = nullptr;
 static bool isGarbageCollecting;
 static uint32_t eventloopNestingLevel = 0;
-static bool minidumpAnalysisAllThreads = false;
 
 // Avoid a race during application termination.
 static Mutex* dumpSafetyLock;
 static bool isSafeToDump = false;
 
 // Whether to include heap regions of the crash context.
 static bool sIncludeContextHeap = false;
 
@@ -749,31 +747,26 @@ WriteGlobalMemoryStatus(PlatformWriter* 
  * Launches the program specified in aProgramPath with aMinidumpPath as its
  * sole argument.
  *
  * @param aProgramPath The path of the program to be launched
  * @param aMinidumpPath The path of the minidump file, passed as an argument
  *        to the launched program
  */
 static bool
-LaunchProgram(const XP_CHAR* aProgramPath, const XP_CHAR* aMinidumpPath,
-              bool aAllThreads)
+LaunchProgram(const XP_CHAR* aProgramPath, const XP_CHAR* aMinidumpPath)
 {
 #ifdef XP_WIN
   XP_CHAR cmdLine[CMDLINE_SIZE];
   XP_CHAR* p;
 
   size_t size = CMDLINE_SIZE;
   p = Concat(cmdLine, L"\"", &size);
   p = Concat(p, aProgramPath, &size);
-  p = Concat(p, L"\" ", &size);
-  if (aAllThreads) {
-    p = Concat(p, L"--full ", &size);
-  }
-  p = Concat(p, L"\"", &size);
+  p = Concat(p, L"\" \"", &size);
   p = Concat(p, aMinidumpPath, &size);
   Concat(p, L"\"", &size);
 
   PROCESS_INFORMATION pi = {};
   STARTUPINFO si = {};
   si.cb = sizeof(si);
 
   // If CreateProcess() fails don't do anything
@@ -783,30 +776,22 @@ LaunchProgram(const XP_CHAR* aProgramPat
     CloseHandle(pi.hProcess);
     CloseHandle(pi.hThread);
   }
 #elif defined(XP_MACOSX)
   // Needed to locate NSS and its dependencies
   setenv("DYLD_LIBRARY_PATH", libraryPath, /* overwrite */ 1);
 
   pid_t pid = 0;
-  char* my_argv[] = {
+  char* const my_argv[] = {
     const_cast<char*>(aProgramPath),
     const_cast<char*>(aMinidumpPath),
-    nullptr,
     nullptr
   };
 
-  char fullArg[] = "--full";
-
-  if (aAllThreads) {
-    my_argv[2] = my_argv[1];
-    my_argv[1] = fullArg;
-  }
-
   char **env = nullptr;
   char ***nsEnv = _NSGetEnviron();
   if (nsEnv) {
     env = *nsEnv;
   }
 
   int rv = posix_spawnp(&pid, my_argv[0], nullptr, nullptr, my_argv, env);
 
@@ -818,23 +803,18 @@ LaunchProgram(const XP_CHAR* aProgramPat
 
   if (pid == -1) {
     return false;
   } else if (pid == 0) {
     // need to clobber this, as libcurl might load NSS,
     // and we want it to load the system NSS.
     unsetenv("LD_LIBRARY_PATH");
 
-    if (aAllThreads) {
-      Unused << execl(aProgramPath,
-                      aProgramPath, "--full", aMinidumpPath, (char*)0);
-    } else {
-      Unused << execl(aProgramPath,
-                      aProgramPath, aMinidumpPath, (char*)0);
-    }
+    Unused << execl(aProgramPath,
+                    aProgramPath, aMinidumpPath, nullptr);
     _exit(1);
   }
 #endif // XP_MACOSX
 
   return true;
 }
 
 #else
@@ -1140,18 +1120,17 @@ MinidumpCallback(
 #endif // XP_WIN
     return returnValue;
   }
 
 #if defined(MOZ_WIDGET_ANDROID) // Android
   returnValue = LaunchCrashReporterActivity(crashReporterPath, minidumpPath,
                                             succeeded);
 #else // Windows, Mac, Linux, etc...
-  returnValue = LaunchProgram(crashReporterPath, minidumpPath,
-                              minidumpAnalysisAllThreads);
+  returnValue = LaunchProgram(crashReporterPath, minidumpPath);
 #ifdef XP_WIN
   TerminateProcess(GetCurrentProcess(), 1);
 #endif
 #endif
 
   return returnValue;
 }
 
@@ -1867,38 +1846,30 @@ SetupCrashReporterDirectory(nsIFile* aAp
   nsCOMPtr<nsIFile> directory;
   nsresult rv = aAppDataDirectory->Clone(getter_AddRefs(directory));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = directory->AppendNative(nsDependentCString(aDirName));
   NS_ENSURE_SUCCESS(rv, rv);
 
   EnsureDirectoryExists(directory);
-
-  xpstring dirEnv(aEnvVarName);
-  dirEnv.append(XP_TEXT("="));
-
   xpstring* directoryPath = CreatePathFromFile(directory);
 
   if (!directoryPath) {
     return NS_ERROR_FAILURE;
   }
 
-  dirEnv.append(*directoryPath);
-  delete directoryPath;
-
 #if defined(XP_WIN32)
-  _wputenv(dirEnv.c_str());
+  SetEnvironmentVariableW(aEnvVarName, directoryPath->c_str());
 #else
-  XP_CHAR* str = new XP_CHAR[dirEnv.size() + 1];
-  strncpy(str, dirEnv.c_str(), dirEnv.size() + 1);
-  // |PR_SetEnv| requires str to leak.
-  PR_SetEnv(str);
+  setenv(aEnvVarName, directoryPath->c_str(), /* overwrite */ 1);
 #endif
 
+  delete directoryPath;
+
   if (aDirectory) {
     directory.forget(aDirectory);
   }
 
   return NS_OK;
 }
 
 // Annotate the crash report with a Unique User ID and time
@@ -2209,17 +2180,18 @@ nsresult SetGarbageCollecting(bool colle
 
 void SetEventloopNestingLevel(uint32_t level)
 {
   eventloopNestingLevel = level;
 }
 
 void SetMinidumpAnalysisAllThreads()
 {
-  minidumpAnalysisAllThreads = true;
+  char* env = strdup("MOZ_CRASHREPORTER_DUMP_ALL_THREADS=1");
+  PR_SetEnv(env);
 }
 
 nsresult AppendAppNotesToCrashReport(const nsACString& data)
 {
   if (!GetEnabled())
     return NS_ERROR_NOT_INITIALIZED;
 
   if (FindInReadable(NS_LITERAL_CSTRING("\0"), data))
@@ -2619,57 +2591,46 @@ nsresult SetSubmitReports(bool aSubmitRe
 
     obsServ->NotifyObservers(nullptr, "submit-reports-pref-changed", nullptr);
     return NS_OK;
 }
 
 static void
 SetCrashEventsDir(nsIFile* aDir)
 {
+  static const XP_CHAR eventsDirectoryEnv[] =
+    XP_TEXT("MOZ_CRASHREPORTER_EVENTS_DIRECTORY");
+
   nsCOMPtr<nsIFile> eventsDir = aDir;
 
   const char *env = PR_GetEnv("CRASHES_EVENTS_DIR");
   if (env && *env) {
     NS_NewNativeLocalFile(nsDependentCString(env),
                           false, getter_AddRefs(eventsDir));
     EnsureDirectoryExists(eventsDir);
   }
 
   if (eventsDirectory) {
     free(eventsDirectory);
   }
 
+  xpstring* path = CreatePathFromFile(eventsDir);
+  if (!path) {
+    return; // There's no clean failure from this
+  }
+
 #ifdef XP_WIN
-  nsString path;
-  eventsDir->GetPath(path);
-  eventsDirectory = reinterpret_cast<wchar_t*>(ToNewUnicode(path));
-
-  // Save the path in the environment for the crash reporter application.
-  nsAutoString eventsDirEnv(NS_LITERAL_STRING("MOZ_CRASHREPORTER_EVENTS_DIRECTORY="));
-  eventsDirEnv.Append(path);
-  _wputenv(eventsDirEnv.get());
+  eventsDirectory = wcsdup(path->c_str());
+  SetEnvironmentVariableW(eventsDirectoryEnv, path->c_str());
 #else
-  nsCString path;
-  eventsDir->GetNativePath(path);
-  eventsDirectory = ToNewCString(path);
-
-  // Save the path in the environment for the crash reporter application.
-  nsAutoCString eventsDirEnv("MOZ_CRASHREPORTER_EVENTS_DIRECTORY=");
-  eventsDirEnv.Append(path);
-
-  // PR_SetEnv() wants the string to be available for the lifetime
-  // of the app, so dup it here.
-  char* oldEventsEnv = eventsEnv;
-  eventsEnv = ToNewCString(eventsDirEnv);
-  PR_SetEnv(eventsEnv);
-
-  if (oldEventsEnv) {
-    free(oldEventsEnv);
-  }
+  eventsDirectory = strdup(path->c_str());
+  setenv(eventsDirectoryEnv, path->c_str(), /* overwrite */ 1);
 #endif
+
+  delete path;
 }
 
 void
 SetProfileDirectory(nsIFile* aDir)
 {
   nsCOMPtr<nsIFile> dir;
   aDir->Clone(getter_AddRefs(dir));
 
--- a/toolkit/mozapps/update/UpdateTelemetry.jsm
+++ b/toolkit/mozapps/update/UpdateTelemetry.jsm
@@ -169,18 +169,16 @@ var AUSTLMY = {
   DWNLD_ERR_NO_UPDATE_PATCH: 6,
   DWNLD_ERR_PATCH_SIZE_LARGER: 8,
   DWNLD_ERR_PATCH_SIZE_NOT_EQUAL: 9,
   DWNLD_ERR_BINDING_ABORTED: 10,
   DWNLD_ERR_ABORT: 11,
   DWNLD_ERR_DOCUMENT_NOT_CACHED: 12,
   DWNLD_ERR_VERIFY_NO_REQUEST: 13,
   DWNLD_ERR_VERIFY_PATCH_SIZE_NOT_EQUAL: 14,
-  // DWNLD_ERR_VERIFY_NO_HASH_MATCH: 15 - no longer in use
-  DWNLD_RESUME_FAILURE: 16,
 
   /**
    * Submit a telemetry ping for the update download result code.
    *
    * @param  aIsComplete
    *         If true the histogram is for a patch type complete, if false the
    *         histogram is for a patch type partial, and when undefined the
    *         histogram is for an unknown patch type. This is used to determine
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -14,16 +14,17 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm", this);
 Cu.importGlobalProperties(["XMLHttpRequest"]);
 
 const UPDATESERVICE_CID = Components.ID("{B3C290A6-3943-4B89-8BBE-C01EB7B3B311}");
 const UPDATESERVICE_CONTRACTID = "@mozilla.org/updates/update-service;1";
 
 const PREF_APP_UPDATE_ALTWINDOWTYPE        = "app.update.altwindowtype";
 const PREF_APP_UPDATE_AUTO                 = "app.update.auto";
+const PREF_APP_UPDATE_BACKGROUNDINTERVAL   = "app.update.download.backgroundInterval";
 const PREF_APP_UPDATE_BACKGROUNDERRORS     = "app.update.backgroundErrors";
 const PREF_APP_UPDATE_BACKGROUNDMAXERRORS  = "app.update.backgroundMaxErrors";
 const PREF_APP_UPDATE_CANCELATIONS         = "app.update.cancelations";
 const PREF_APP_UPDATE_CANCELATIONS_OSX     = "app.update.cancelations.osx";
 const PREF_APP_UPDATE_CANCELATIONS_OSX_MAX = "app.update.cancelations.osx.max";
 const PREF_APP_UPDATE_DOORHANGER           = "app.update.doorhanger";
 const PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS    = "app.update.download.attempts";
 const PREF_APP_UPDATE_DOWNLOAD_MAXATTEMPTS = "app.update.download.maxAttempts";
@@ -149,16 +150,20 @@ const INVALID_UPDATER_STATUS_CODE       
 
 // Custom update error codes
 const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110;
 const NETWORK_ERROR_OFFLINE             = 111;
 
 // Error codes should be < 1000. Errors above 1000 represent http status codes
 const HTTP_ERROR_OFFSET                 = 1000;
 
+const DOWNLOAD_CHUNK_SIZE           = 300000; // bytes
+const DOWNLOAD_BACKGROUND_INTERVAL  = 600; // seconds
+const DOWNLOAD_FOREGROUND_INTERVAL  = 0;
+
 const UPDATE_WINDOW_NAME      = "Update:Wizard";
 
 // The number of consecutive failures when updating using the service before
 // setting the app.update.service.enabled preference to false.
 const DEFAULT_SERVICE_MAX_ERRORS = 10;
 
 // The number of consecutive socket errors to allow before falling back to
 // downloading a different MAR file or failing if already downloading the full.
@@ -179,36 +184,30 @@ const APPID_TO_TOPIC = {
   // SeaMonkey
   "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}": "sessionstore-windows-restored",
   // Thunderbird
   "{3550f703-e582-4d05-9a08-453d09bdfdc6}": "mail-startup-done",
   // Instantbird
   "{33cb9019-c295-46dd-be21-8c4936574bee}": "xul-window-visible",
 };
 
-// Download progress notifications are throttled to fire at least this many
-// milliseconds apart, to keep the UI from updating too fast to read.
-const DOWNLOAD_PROGRESS_INTERVAL = 500; // ms
-
 // A var is used for the delay so tests can set a smaller value.
 var gSaveUpdateXMLDelay = 2000;
 var gUpdateMutexHandle = null;
 
 ChromeUtils.defineModuleGetter(this, "UpdateUtils",
                                "resource://gre/modules/UpdateUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "WindowsRegistry",
                                "resource://gre/modules/WindowsRegistry.jsm");
 ChromeUtils.defineModuleGetter(this, "AsyncShutdown",
                                "resource://gre/modules/AsyncShutdown.jsm");
 ChromeUtils.defineModuleGetter(this, "OS",
                                "resource://gre/modules/osfile.jsm");
 ChromeUtils.defineModuleGetter(this, "DeferredTask",
                                "resource://gre/modules/DeferredTask.jsm");
-ChromeUtils.defineModuleGetter(this, "NetUtil",
-                               "resource://gre/modules/NetUtil.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gLogEnabled", function aus_gLogEnabled() {
   return Services.prefs.getBoolPref(PREF_APP_UPDATE_LOG, false);
 });
 
 XPCOMUtils.defineLazyGetter(this, "gUpdateBundle", function aus_gUpdateBundle() {
   return Services.strings.createBundle(URI_UPDATES_PROPERTIES);
 });
@@ -1122,19 +1121,16 @@ function pingStateAndStatusCodes(aUpdate
 function UpdatePatch(patch) {
   this._properties = {};
   for (var i = 0; i < patch.attributes.length; ++i) {
     var attr = patch.attributes.item(i);
     switch (attr.name) {
       case "selected":
         this.selected = attr.value == "true";
         break;
-      case "entityID":
-        this.setProperty("entityID", attr.value);
-        break;
       case "size":
         if (0 == parseInt(attr.value)) {
           LOG("UpdatePatch:init - 0-sized patch!");
           throw Cr.NS_ERROR_ILLEGAL_VALUE;
         }
         // fall through
       default:
         this[attr.name] = attr.value;
@@ -1254,16 +1250,18 @@ UpdatePatch.prototype = {
  */
 function Update(update) {
   this._properties = {};
   this._patches = [];
   this.isCompleteUpdate = false;
   this.unsupported = false;
   this.channel = "default";
   this.promptWaitTime = Services.prefs.getIntPref(PREF_APP_UPDATE_PROMPTWAITTIME, 43200);
+  this.backgroundInterval = Services.prefs.getIntPref(PREF_APP_UPDATE_BACKGROUNDINTERVAL,
+                                                      DOWNLOAD_BACKGROUND_INTERVAL);
 
   // Null <update>, assume this is a message container and do no
   // further initialization
   if (!update) {
     return;
   }
 
   const ELEMENT_NODE = Ci.nsIDOMNode.ELEMENT_NODE;
@@ -1310,16 +1308,20 @@ function Update(update) {
         this.errorCode = val;
       }
     } else if (attr.name == "isCompleteUpdate") {
       this.isCompleteUpdate = attr.value == "true";
     } else if (attr.name == "promptWaitTime") {
       if (!isNaN(attr.value)) {
         this.promptWaitTime = parseInt(attr.value);
       }
+    } else if (attr.name == "backgroundInterval") {
+      if (!isNaN(attr.value)) {
+        this.backgroundInterval = parseInt(attr.value);
+      }
     } else if (attr.name == "unsupported") {
       this.unsupported = attr.value == "true";
     } else {
       this[attr.name] = attr.value;
 
       switch (attr.name) {
         case "appVersion":
         case "buildID":
@@ -1340,16 +1342,19 @@ function Update(update) {
       }
     }
   }
 
   if (!this.displayVersion) {
     this.displayVersion = this.appVersion;
   }
 
+  // Don't allow the background download interval to be greater than 10 minutes.
+  this.backgroundInterval = Math.min(this.backgroundInterval, 600);
+
   // The Update Name is either the string provided by the <update> element, or
   // the string: "<App Name> <Update App Version>"
   var name = "";
   if (update.hasAttribute("name")) {
     name = update.getAttribute("name");
   } else {
     var brandBundle = Services.strings.createBundle(URI_BRAND_PROPERTIES);
     var appName = brandBundle.GetStringFromName("brandShortName");
@@ -1445,16 +1450,17 @@ Update.prototype = {
   serialize: function Update_serialize(updates) {
     // If appVersion isn't defined just return null. This happens when cleaning
     // up invalid updates (e.g. incorrect channel).
     if (!this.appVersion) {
       return null;
     }
     var update = updates.createElementNS(URI_UPDATE_NS, "update");
     update.setAttribute("appVersion", this.appVersion);
+    update.setAttribute("backgroundInterval", this.backgroundInterval);
     update.setAttribute("buildID", this.buildID);
     update.setAttribute("channel", this.channel);
     update.setAttribute("displayVersion", this.displayVersion);
     update.setAttribute("installDate", this.installDate);
     update.setAttribute("isCompleteUpdate", this.isCompleteUpdate);
     update.setAttribute("name", this.name);
     update.setAttribute("promptWaitTime", this.promptWaitTime);
     update.setAttribute("serviceURL", this.serviceURL);
@@ -2438,17 +2444,17 @@ UpdateService.prototype = {
         LOG("UpdateService:downloadUpdate - no support for downloading more " +
             "than one update at a time");
         return readStatusFile(getUpdatesDir());
       }
       this._downloader.cancel();
     }
     // Set the previous application version prior to downloading the update.
     update.previousAppVersion = Services.appinfo.version;
-    this._downloader = getDownloader(background, this);
+    this._downloader = new Downloader(background, this);
     return this._downloader.downloadUpdate(update);
   },
 
   /**
    * See nsIUpdateService.idl
    */
   pauseDownload: function AUS_pauseDownload() {
     if (this.isDownloading) {
@@ -3175,91 +3181,108 @@ Checker.prototype = {
 
 /**
  * Manages the download of updates
  * @param   background
  *          Whether or not this downloader is operating in background
  *          update mode.
  * @param   updateService
  *          The update service that created this downloader.
+ * @constructor
  */
-class CommonDownloader {
-  constructor(background, updateService) {
-    this.background = background;
-    this.updateService = updateService;
-
-    /**
-     * The nsIUpdatePatch that we are downloading
-     */
-    this._patch = null;
-
-    /**
-     * The nsIUpdate that we are downloading
-     */
-    this._update = null;
-
-    /**
-     * Whether or not the update being downloaded is a complete replacement of
-     * the user's existing installation or a patch representing the difference
-     * between the new version and the previous version.
-     */
-    this.isCompleteUpdate = null;
-
-    /**
-     * An array of download listeners to notify when we receive
-     * nsIRequestObserver or nsIProgressEventSink method calls.
-     */
-    this._listeners = [];
-  }
+function Downloader(background, updateService) {
+  LOG("Creating Downloader");
+  this.background = background;
+  this.updateService = updateService;
+}
+Downloader.prototype = {
+  /**
+   * The nsIUpdatePatch that we are downloading
+   */
+  _patch: null,
+
+  /**
+   * The nsIUpdate that we are downloading
+   */
+  _update: null,
+
+  /**
+   * The nsIIncrementalDownload object handling the download
+   */
+  _request: null,
+
+  /**
+   * Whether or not the update being downloaded is a complete replacement of
+   * the user's existing installation or a patch representing the difference
+   * between the new version and the previous version.
+   */
+  isCompleteUpdate: null,
+
+  /**
+   * Cancels the active download.
+   */
+  cancel: function Downloader_cancel(cancelError) {
+    LOG("Downloader: cancel");
+    if (cancelError === undefined) {
+      cancelError = Cr.NS_BINDING_ABORTED;
+    }
+    if (this._request && this._request instanceof Ci.nsIRequest) {
+      this._request.cancel(cancelError);
+    }
+  },
 
   /**
    * Whether or not a patch has been downloaded and staged for installation.
    */
   get patchIsStaged() {
     var readState = readStatusFile(getUpdatesDir());
     // Note that if we decide to download and apply new updates after another
     // update has been successfully applied in the background, we need to stop
     // checking for the APPLIED state here.
     return readState == STATE_PENDING || readState == STATE_PENDING_SERVICE ||
            readState == STATE_PENDING_ELEVATE ||
            readState == STATE_APPLIED || readState == STATE_APPLIED_SERVICE;
-  }
+  },
 
   /**
    * Verify the downloaded file.  We assume that the download is complete at
    * this point.
-   *
-   * @param patchFile
-   *        nsIFile representing the fully downloaded file
    */
-  _verifyDownload(patchFile) {
-    LOG("CommonDownloader:_verifyDownload called");
+  _verifyDownload: function Downloader__verifyDownload() {
+    LOG("Downloader:_verifyDownload called");
+    if (!this._request) {
+      AUSTLMY.pingDownloadCode(this.isCompleteUpdate,
+                               AUSTLMY.DWNLD_ERR_VERIFY_NO_REQUEST);
+      return false;
+    }
+
+    let destination = this._request.destination;
 
     // Ensure that the file size matches the expected file size.
-    if (patchFile.fileSize != this._patch.size) {
-      LOG("CommonDownloader:_verifyDownload downloaded size != expected size.");
+    if (destination.fileSize != this._patch.size) {
+      LOG("Downloader:_verifyDownload downloaded size != expected size.");
       AUSTLMY.pingDownloadCode(this.isCompleteUpdate,
                                AUSTLMY.DWNLD_ERR_VERIFY_PATCH_SIZE_NOT_EQUAL);
       return false;
     }
 
-    LOG("CommonDownloader:_verifyDownload downloaded size == expected size.");
+    LOG("Downloader:_verifyDownload downloaded size == expected size.");
     return true;
-  }
+  },
 
   /**
    * Select the patch to use given the current state of updateDir and the given
    * set of update patches.
    * @param   update
    *          A nsIUpdate object to select a patch from
    * @param   updateDir
    *          A nsIFile representing the update directory
    * @return  A nsIUpdatePatch object to download
    */
-  _selectPatch(update, updateDir) {
+  _selectPatch: function Downloader__selectPatch(update, updateDir) {
     // Given an update to download, we will always try to download the patch
     // for a partial update over the patch for a full update.
 
     /**
      * Return the first UpdatePatch with the given type.
      * @param   type
      *          The type of the patch ("complete" or "partial")
      * @return  A nsIUpdatePatch object matching the type specified
@@ -3279,32 +3302,32 @@ class CommonDownloader {
     var selectedPatch = update.selectedPatch;
 
     var state = readStatusFile(updateDir);
 
     // If this is a patch that we know about, then select it.  If it is a patch
     // that we do not know about, then remove it and use our default logic.
     var useComplete = false;
     if (selectedPatch) {
-      LOG("CommonDownloader:_selectPatch - found existing patch with state: " +
+      LOG("Downloader:_selectPatch - found existing patch with state: " +
           state);
       if (state == STATE_DOWNLOADING) {
-        LOG("CommonDownloader:_selectPatch - resuming download");
+        LOG("Downloader:_selectPatch - resuming download");
         return selectedPatch;
       }
       if (state == STATE_PENDING || state == STATE_PENDING_SERVICE ||
           state == STATE_PENDING_ELEVATE || state == STATE_APPLIED ||
           state == STATE_APPLIED_SERVICE) {
-        LOG("CommonDownloader:_selectPatch - already downloaded");
+        LOG("Downloader:_selectPatch - already downloaded");
         return null;
       }
 
       if (update && selectedPatch.type == "complete") {
         // This is a pretty fatal error.  Just bail.
-        LOG("CommonDownloader:_selectPatch - failed to apply complete patch!");
+        LOG("Downloader:_selectPatch - failed to apply complete patch!");
         writeStatusFile(updateDir, STATE_NONE);
         writeVersionFile(getUpdatesDir(), null);
         return null;
       }
 
       // Something went wrong when we tried to apply the previous patch.
       // Try the complete patch next time.
       useComplete = true;
@@ -3335,374 +3358,230 @@ class CommonDownloader {
 
     // Reset the Active Update object on the Update Manager and flush the
     // Active Update DB.
     var um = Cc["@mozilla.org/updates/update-manager;1"].
              getService(Ci.nsIUpdateManager);
     um.activeUpdate = update;
 
     return selectedPatch;
-  }
+  },
+
+  /**
+   * Whether or not we are currently downloading something.
+   */
+  get isBusy() {
+    return this._request != null;
+  },
+
+  /**
+   * Download and stage the given update.
+   * @param   update
+   *          A nsIUpdate object to download a patch for. Cannot be null.
+   */
+  downloadUpdate: function Downloader_downloadUpdate(update) {
+    LOG("UpdateService:_downloadUpdate");
+    if (!update) {
+      AUSTLMY.pingDownloadCode(undefined, AUSTLMY.DWNLD_ERR_NO_UPDATE);
+      throw Cr.NS_ERROR_NULL_POINTER;
+    }
+
+    var updateDir = getUpdatesDir();
+
+    this._update = update;
+
+    // This function may return null, which indicates that there are no patches
+    // to download.
+    this._patch = this._selectPatch(update, updateDir);
+    if (!this._patch) {
+      LOG("Downloader:downloadUpdate - no patch to download");
+      AUSTLMY.pingDownloadCode(undefined, AUSTLMY.DWNLD_ERR_NO_UPDATE_PATCH);
+      return readStatusFile(updateDir);
+    }
+    this.isCompleteUpdate = this._patch.type == "complete";
+
+    let patchFile = getUpdatesDir().clone();
+    patchFile.append(FILE_UPDATE_MAR);
+    update.QueryInterface(Ci.nsIPropertyBag);
+    let interval = this.background ? update.getProperty("backgroundInterval")
+                                   : DOWNLOAD_FOREGROUND_INTERVAL;
+
+    LOG("Downloader:downloadUpdate - url: " + this._patch.URL + ", path: " +
+        patchFile.path + ", interval: " + interval);
+    var uri = Services.io.newURI(this._patch.URL);
+
+    this._request = Cc["@mozilla.org/network/incremental-download;1"].
+                    createInstance(Ci.nsIIncrementalDownload);
+    this._request.init(uri, patchFile, DOWNLOAD_CHUNK_SIZE, interval);
+    this._request.start(this, null);
+
+    writeStatusFile(updateDir, STATE_DOWNLOADING);
+    this._patch.QueryInterface(Ci.nsIWritablePropertyBag);
+    this._patch.state = STATE_DOWNLOADING;
+    var um = Cc["@mozilla.org/updates/update-manager;1"].
+             getService(Ci.nsIUpdateManager);
+    um.saveUpdates();
+    return STATE_DOWNLOADING;
+  },
+
+  /**
+   * An array of download listeners to notify when we receive
+   * nsIRequestObserver or nsIProgressEventSink method calls.
+   */
+  _listeners: [],
 
   /**
    * Adds a listener to the download process
    * @param   listener
    *          A download listener, implementing nsIRequestObserver and
    *          nsIProgressEventSink
    */
-  addDownloadListener(listener) {
-    for (let i = 0; i < this._listeners.length; ++i) {
-      if (this._listeners[i] == listener) {
+  addDownloadListener: function Downloader_addDownloadListener(listener) {
+    for (var i = 0; i < this._listeners.length; ++i) {
+      if (this._listeners[i] == listener)
         return;
-      }
     }
     this._listeners.push(listener);
-  }
+  },
 
   /**
    * Removes a download listener
    * @param   listener
    *          The listener to remove.
    */
-  removeDownloadListener(listener) {
-    for (let i = 0; i < this._listeners.length; ++i) {
+  removeDownloadListener: function Downloader_removeDownloadListener(listener) {
+    for (var i = 0; i < this._listeners.length; ++i) {
       if (this._listeners[i] == listener) {
         this._listeners.splice(i, 1);
         return;
       }
     }
-  }
-}
-
-/**
- * Update downloader which uses an nsHttpChannel
- */
-class ChannelDownloader extends CommonDownloader {
-  constructor(background, updateService) {
-    LOG("Creating ChannelDownloader");
-
-    super(background, updateService);
-
-    /**
-     * Background file saver, for writing the downloaded file on another thread
-     */
-    this._bkgFileSaver = null;
-
-    /**
-     * The channel object handling the download
-     */
-    this._channel = null;
-
-    /**
-     * Timestamp of the last time we notified listeners for OnProgress.
-     * Used to throttle how frequently those notifications can be sent,
-     * to avoid updating user interfaces faster than they can be read.
-     */
-    this._lastProgressTimeMs = 0;
-
-    /**
-     * If a previous download is being resumed, this is set to the number of
-     * bytes that had already been downloaded.
-     */
-    this._resumedFrom = 0;
-
-    this.QueryInterface = XPCOMUtils.generateQI([Ci.nsIStreamListener,
-                                                 Ci.nsIChannelEventSink,
-                                                 Ci.nsIProgressEventSink,
-                                                 Ci.nsIRequestObserver,
-                                                 Ci.nsIInterfaceRequestor]);
-  }
-
-  /**
-   * Verify the downloaded file.  We assume that the download is complete at
-   * this point.
-   */
-  _verifyDownload() {
-    if (!this._channel) {
-      AUSTLMY.pingDownloadCode(this.isCompleteUpdate,
-                               AUSTLMY.DWNLD_ERR_VERIFY_NO_REQUEST);
-      return false;
-    }
-    let patchFile = getUpdatesDir().clone();
-    patchFile.append(FILE_UPDATE_MAR);
-    return super._verifyDownload(patchFile);
-  }
-
-  /**
-   * Cancels the active download.
-   */
-  cancel(cancelError) {
-    LOG("ChannelDownloader: cancel");
-    if (cancelError === undefined) {
-      cancelError = Cr.NS_BINDING_ABORTED;
-    }
-    if (this._bkgFileSaver) {
-      this._bkgFileSaver.finish(cancelError);
-      this._bkgFileSaver.observer = null;
-      this._bkgFileSaver = null;
-    }
-    if (this._channel) {
-      this._channel.cancel(cancelError);
-      this._channel = null;
-    }
-  }
-
-  /**
-   * Whether or not we are currently downloading something.
-   */
-  get isBusy() {
-    return this._channel != null;
-  }
-
-  /**
-   * Download and stage the given update.
-   * @param   update
-   *          A nsIUpdate object to download a patch for. Cannot be null.
-   */
-  downloadUpdate(update) {
-    LOG("ChannelDownloader:downloadUpdate");
-    if (!update) {
-      AUSTLMY.pingDownloadCode(undefined, AUSTLMY.DWNLD_ERR_NO_UPDATE);
-      throw Cr.NS_ERROR_NULL_POINTER;
-    }
-
-    let updateDir = getUpdatesDir();
-
-    this._update = update;
-
-    // This function may return null, which indicates that there are no patches
-    // to download.
-    this._patch = this._selectPatch(update, updateDir);
-    if (!this._patch) {
-      LOG("ChannelDownloader:downloadUpdate - no patch to download");
-      AUSTLMY.pingDownloadCode(undefined, AUSTLMY.DWNLD_ERR_NO_UPDATE_PATCH);
-      return readStatusFile(updateDir);
-    }
-    this.isCompleteUpdate = this._patch.type == "complete";
-    this._patch.QueryInterface(Ci.nsIWritablePropertyBag);
-
-    let patchFile = getUpdatesDir().clone();
-    patchFile.append(FILE_UPDATE_MAR);
-
-    LOG("ChannelDownloader:downloadUpdate - url: " + this._patch.URL +
-        ", path: " + patchFile.path);
-    let uri = Services.io.newURI(this._patch.URL);
-
-    let BackgroundFileSaver = Components.Constructor(
-      "@mozilla.org/network/background-file-saver;1?mode=streamlistener",
-      "nsIBackgroundFileSaver");
-    this._bkgFileSaver = new BackgroundFileSaver();
-    this._bkgFileSaver.QueryInterface(Ci.nsIStreamListener);
-
-    this._channel = NetUtil.newChannel({uri, loadUsingSystemPrincipal: true});
-    this._channel.notificationCallbacks = this;
-    this._channel.asyncOpen2(this.QueryInterface(Ci.nsIStreamListener));
-
-    if (this._channel instanceof Ci.nsIResumableChannel &&
-        patchFile.exists()) {
-      let resumeFrom;
-      let entityID = this._patch.getProperty("entityID");
-      if (!entityID) {
-        LOG("ChannelDownloader:downloadUpdate - failed to resume download, " +
-            "couldn't get entityID for the selected patch");
-      } else {
-        try {
-          resumeFrom = patchFile.fileSize;
-        } catch (e) {
-          LOG("ChannelDownloader:downloadUpdate - failed to resume download, " +
-              "couldn't open partially downloaded file, exception: " + e);
-        }
-      }
-
-      if (entityID && resumeFrom !== undefined) {
-        this._channel.resumeAt(resumeFrom, entityID);
-        this._bkgFileSaver.enableAppend();
-        this._resumedFrom = resumeFrom;
-        LOG("ChannelDownloader:downloadUpdate - resuming previous download " +
-            "starting after " + resumeFrom + " bytes");
-      } else {
-        AUSTLMY.pingDownloadCode(this.isCompleteUpdate,
-                                 AUSTLMY.DWNLD_RESUME_FAILURE);
-      }
-    }
-
-    this._bkgFileSaver.setTarget(patchFile, true);
-
-    writeStatusFile(updateDir, STATE_DOWNLOADING);
-    this._patch.state = STATE_DOWNLOADING;
-    let um = Cc["@mozilla.org/updates/update-manager;1"].
-             getService(Ci.nsIUpdateManager);
-    um.saveUpdates();
-    return STATE_DOWNLOADING;
-  }
-
-  /**
-   * See nsIChannelEventSink.idl
-   */
-  asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) {
-    LOG("ChannelDownloader: redirected from " + oldChannel.URI +
-        " to " + newChannel.URI);
-    this._patch.finalURL = newChannel.URI;
-    callback.onRedirectVerifyCallback(Cr.NS_OK);
-    this._channel = newChannel;
-  }
-
-  /**
-   * See nsIProgressEventSink.idl
-   */
-  onStatus(request, context, status, statusText) {
-    LOG("ChannelDownloader:onStatus - status: " + status +
-        ", statusText: " + statusText);
-
-    for (let listener of this._listeners) {
-      if (listener instanceof Ci.nsIProgressEventSink) {
-        listener.onStatus(request, context, status, statusText);
-      }
-    }
-  }
+  },
 
   /**
    * When the async request begins
    * @param   request
    *          The nsIRequest object for the transfer
    * @param   context
    *          Additional data
    */
-  onStartRequest(request, context) {
-    if (!this._channel || !this._bkgFileSaver) {
-      // Sometimes the channel calls onStartRequest after being canceled.
-      return;
-    }
-
-    LOG("ChannelDownloader:onStartRequest");
-
-    this._bkgFileSaver.onStartRequest(request, context);
-
-    if (request instanceof Ci.nsIResumableChannel) {
-      // Reading the entityID can throw if the server doesn't allow resuming.
-      try {
-        this._patch.setProperty("entityID", request.entityID);
-      } catch (ex) {
-        if (!(ex instanceof Components.Exception) ||
-            ex.result != Cr.NS_ERROR_NOT_RESUMABLE) {
-          throw ex;
-        }
-      }
-    }
-
+  onStartRequest: function Downloader_onStartRequest(request, context) {
+    if (request instanceof Ci.nsIIncrementalDownload)
+      LOG("Downloader:onStartRequest - original URI spec: " + request.URI.spec +
+          ", final URI spec: " + request.finalURI.spec);
+    // Always set finalURL in onStartRequest since it can change.
+    this._patch.finalURL = request.finalURI.spec;
     var um = Cc["@mozilla.org/updates/update-manager;1"].
              getService(Ci.nsIUpdateManager);
     um.saveUpdates();
 
     var listeners = this._listeners.concat();
     var listenerCount = listeners.length;
-    for (var i = 0; i < listenerCount; ++i) {
+    for (var i = 0; i < listenerCount; ++i)
       listeners[i].onStartRequest(request, context);
-    }
-  }
+  },
 
   /**
-   * See nsIProgressEventSink.idl
+   * When new data has been downloaded
+   * @param   request
+   *          The nsIRequest object for the transfer
+   * @param   context
+   *          Additional data
+   * @param