Merge mozilla-central to autoland. a=merge CLOSED TREE
authorCiure Andrei <aciure@mozilla.com>
Fri, 02 Mar 2018 12:19:09 +0200
changeset 458749 5889cac267e270e4762fe579e8bec0b4b0920532
parent 458728 1245c42d81aaf9bcaa91c2a752228328d34e593c (current diff)
parent 458748 b2a9a4bb5c94de179ae7a3f52fde58c0e2897498 (diff)
child 458750 921a91c6538bf374b9a6eaca557b4f24e8fa552b
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
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
testing/web-platform/meta/webdriver/tests/navigation/current_url.py.ini
toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
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
widget/moz.build
--- 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/accessible/ipc/win/HandlerProvider.cpp
+++ b/accessible/ipc/win/HandlerProvider.cpp
@@ -459,23 +459,16 @@ HandlerProvider::MarshalAs(REFIID aIid)
       aIid == IID_IAccessible2_3) {
     // This should always be the newest IA2 interface ID
     return NEWEST_IA2_IID;
   }
   // Otherwise we juse return the identity.
   return aIid;
 }
 
-HRESULT
-HandlerProvider::DisconnectHandlerRemotes()
-{
-  IUnknown* unk = static_cast<IGeckoBackChannel*>(this);
-  return ::CoDisconnectObject(unk, 0);
-}
-
 REFIID
 HandlerProvider::GetEffectiveOutParamIid(REFIID aCallIid,
                                          ULONG aCallMethod)
 {
   if (aCallIid == IID_IAccessibleTable ||
       aCallIid == IID_IAccessibleTable2 ||
       aCallIid == IID_IAccessibleDocument ||
       aCallIid == IID_IAccessibleTableCell ||
--- a/accessible/ipc/win/HandlerProvider.h
+++ b/accessible/ipc/win/HandlerProvider.h
@@ -42,17 +42,16 @@ public:
 
   // IHandlerProvider
   STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) override;
   STDMETHODIMP GetHandlerPayloadSize(NotNull<mscom::IInterceptor*> aInterceptor,
                                      NotNull<DWORD*> aOutPayloadSize) override;
   STDMETHODIMP WriteHandlerPayload(NotNull<mscom::IInterceptor*> aInterceptor,
                                    NotNull<IStream*> aStream) override;
   STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override;
-  STDMETHODIMP DisconnectHandlerRemotes() override;
   STDMETHODIMP_(REFIID) GetEffectiveOutParamIid(REFIID aCallIid,
                                                 ULONG aCallMethod) override;
   STDMETHODIMP NewInstance(REFIID aIid,
                            mscom::InterceptorTargetPtr<IUnknown> aTarget,
                            NotNull<mscom::IHandlerProvider**> aOutNewPayload) override;
 
   // IGeckoBackChannel
   STDMETHODIMP put_HandlerControl(long aPid, IHandlerControl* aCtrl) override;
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -41,17 +41,16 @@
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsEventMap.h"
 #include "nsArrayUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ReverseIterator.h"
 #include "nsIXULRuntime.h"
 #include "mozilla/mscom/AsyncInvoker.h"
-#include "mozilla/mscom/Interceptor.h"
 
 #include "oleacc.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 const uint32_t USE_ROLE_STRING = 0;
 
@@ -99,34 +98,16 @@ AccessibleWrap::Shutdown()
     auto doc = static_cast<DocAccessibleWrap*>(mDoc.get());
     MOZ_ASSERT(doc);
     if (doc) {
       doc->RemoveID(mID);
       mID = kNoID;
     }
   }
 
-  if (XRE_IsContentProcess()) {
-    // Bug 1434822: To improve performance for cross-process COM, we disable COM
-    // garbage collection. However, this means we never receive Release calls
-    // from clients, so defunct accessibles can never be deleted. Since we
-    // know when an accessible is shutting down, we can work around this by
-    // forcing COM to disconnect this object from all of its remote clients,
-    // which will cause associated references to be released.
-    IUnknown* unk = static_cast<IAccessible*>(this);
-    mscom::Interceptor::DisconnectRemotesForTarget(unk);
-    // If an accessible was retrieved via IAccessibleHypertext::hyperlink*,
-    // it will have a different Interceptor that won't be matched by the above
-    // call, even though it's the same object. Therefore, call it again with
-    // the IAccessibleHyperlink pointer. We can remove this horrible hack once
-    // bug 1440267 is fixed.
-    unk = static_cast<IAccessibleHyperlink*>(this);
-    mscom::Interceptor::DisconnectRemotesForTarget(unk);
-  }
-
   Accessible::Shutdown();
 }
 
 //-----------------------------------------------------
 // IUnknown interface methods - see iunknown.h for documentation
 //-----------------------------------------------------
 
 // Microsoft COM QueryInterface
--- 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/ipc/mscom/FastMarshaler.cpp
+++ b/ipc/mscom/FastMarshaler.cpp
@@ -98,17 +98,17 @@ FastMarshaler::InternalRelease()
 DWORD
 FastMarshaler::GetMarshalFlags(DWORD aDestContext, DWORD aMshlFlags)
 {
   // Only worry about local contexts.
   if (aDestContext != MSHCTX_LOCAL) {
     return aMshlFlags;
   }
 
-  if (IsCallerExternalProcess()) {
+  if (!IsCallerExternalProcess()) {
     return aMshlFlags;
   }
 
   // The caller is our parent main thread. Disable ping functionality.
   return aMshlFlags | MSHLFLAGS_NOPING;
 }
 
 HRESULT
--- a/ipc/mscom/IHandlerProvider.h
+++ b/ipc/mscom/IHandlerProvider.h
@@ -18,17 +18,16 @@ namespace mscom {
 struct IInterceptor;
 
 struct HandlerProvider
 {
   virtual STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) = 0;
   virtual STDMETHODIMP GetHandlerPayloadSize(NotNull<IInterceptor*> aInterceptor, NotNull<DWORD*> aOutPayloadSize) = 0;
   virtual STDMETHODIMP WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor, NotNull<IStream*> aStream) = 0;
   virtual STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) = 0;
-  virtual STDMETHODIMP DisconnectHandlerRemotes() = 0;
 };
 
 struct IHandlerProvider : public IUnknown
                         , public HandlerProvider
 {
   virtual STDMETHODIMP_(REFIID) GetEffectiveOutParamIid(REFIID aCallIid,
                                                         ULONG aCallMethod) = 0;
   virtual STDMETHODIMP NewInstance(REFIID aIid,
--- a/ipc/mscom/Interceptor.cpp
+++ b/ipc/mscom/Interceptor.cpp
@@ -423,17 +423,16 @@ HRESULT
 Interceptor::ReleaseMarshalData(IStream* pStm)
 {
   return mStdMarshal->ReleaseMarshalData(pStm);
 }
 
 HRESULT
 Interceptor::DisconnectObject(DWORD dwReserved)
 {
-  mEventSink->DisconnectHandlerRemotes();
   return mStdMarshal->DisconnectObject(dwReserved);
 }
 
 Interceptor::MapEntry*
 Interceptor::Lookup(REFIID aIid)
 {
   mInterceptorMapMutex.AssertCurrentThreadOwns();
 
@@ -826,35 +825,10 @@ Interceptor::AddRef()
 }
 
 ULONG
 Interceptor::Release()
 {
   return WeakReferenceSupport::Release();
 }
 
-/* static */ HRESULT
-Interceptor::DisconnectRemotesForTarget(IUnknown* aTarget)
-{
-  MOZ_ASSERT(aTarget);
-
-  detail::LiveSetAutoLock lock(GetLiveSet());
-
-  // It is not an error if the interceptor doesn't exist, so we return
-  // S_FALSE instead of an error in that case.
-  RefPtr<IWeakReference> existingWeak(Move(GetLiveSet().Get(aTarget)));
-  if (!existingWeak) {
-    return S_FALSE;
-  }
-
-  RefPtr<IWeakReferenceSource> existingStrong;
-  if (FAILED(existingWeak->ToStrongRef(getter_AddRefs(existingStrong)))) {
-    return S_FALSE;
-  }
-  // Since we now hold a strong ref on the interceptor, we may now release the
-  // lock.
-  lock.Unlock();
-
-  return ::CoDisconnectObject(existingStrong, 0);
-}
-
 } // namespace mscom
 } // namespace mozilla
--- a/ipc/mscom/Interceptor.h
+++ b/ipc/mscom/Interceptor.h
@@ -69,36 +69,16 @@ class Interceptor final : public WeakRef
                         , public IStdMarshalInfo
                         , public IMarshal
                         , public IInterceptor
 {
 public:
   static HRESULT Create(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink,
                         REFIID aInitialIid, void** aOutInterface);
 
-  /**
-   * Disconnect all remote clients for a given target.
-   * Because Interceptors disable COM garbage collection to improve
-   * performance, they never receive Release calls from remote clients. If
-   * the object can be shut down while clients still hold a reference, this
-   * function can be used to force COM to disconnect all remote connections
-   * (using CoDisconnectObject) and thus release the associated references to
-   * the Interceptor, its target and any objects associated with the
-   * HandlerProvider.
-   * Note that the specified target must be the same IUnknown pointer used to
-   * create the Interceptor. Where there is multiple inheritance, querying for
-   * IID_IUnknown and calling this function with that pointer alone will not
-   * disconnect remotes for all interfaces. If you expect that the same object
-   * may be fetched with different initial interfaces, you should call this
-   * function once for each possible IUnknown pointer.
-   * @return S_OK if there was an Interceptor for the given target,
-   *         S_FALSE if there was not.
-   */
-  static HRESULT DisconnectRemotesForTarget(IUnknown* aTarget);
-
   // IUnknown
   STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
   STDMETHODIMP_(ULONG) AddRef() override;
   STDMETHODIMP_(ULONG) Release() override;
 
   // IStdMarshalInfo
   STDMETHODIMP GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
                                   CLSID* aHandlerClsid) override;
--- a/ipc/mscom/MainThreadHandoff.cpp
+++ b/ipc/mscom/MainThreadHandoff.cpp
@@ -585,26 +585,16 @@ MainThreadHandoff::MarshalAs(REFIID aIid
 {
   if (!mHandlerProvider) {
     return aIid;
   }
   return mHandlerProvider->MarshalAs(aIid);
 }
 
 HRESULT
-MainThreadHandoff::DisconnectHandlerRemotes()
-{
-  if (!mHandlerProvider) {
-    return E_NOTIMPL;
-  }
-
-  return mHandlerProvider->DisconnectHandlerRemotes();
-}
-
-HRESULT
 MainThreadHandoff::OnWalkInterface(REFIID aIid, PVOID* aInterface,
                                    BOOL aIsInParam, BOOL aIsOutParam)
 {
   MOZ_ASSERT(aInterface && aIsOutParam);
   if (!aInterface || !aIsOutParam) {
     return E_UNEXPECTED;
   }
 
--- a/ipc/mscom/MainThreadHandoff.h
+++ b/ipc/mscom/MainThreadHandoff.h
@@ -61,17 +61,16 @@ public:
   // IInterceptorSink
   STDMETHODIMP SetInterceptor(IWeakReference* aInterceptor) override;
   STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) override;
   STDMETHODIMP GetHandlerPayloadSize(NotNull<IInterceptor*> aInterceptor,
                                      NotNull<DWORD*> aOutPayloadSize) override;
   STDMETHODIMP WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor,
                                    NotNull<IStream*> aStream) override;
   STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override;
-  STDMETHODIMP DisconnectHandlerRemotes() override;
 
   // ICallFrameWalker
   STDMETHODIMP OnWalkInterface(REFIID aIid, PVOID* aInterface, BOOL aIsInParam,
                                BOOL aIsOutParam) override;
 
 private:
   explicit MainThreadHandoff(IHandlerProvider* aHandlerProvider);
   ~MainThreadHandoff();
--- 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/reftests/css-disabled/select/reftest.list
+++ b/layout/reftests/css-disabled/select/reftest.list
@@ -1,9 +1,9 @@
 fuzzy-if(Android,12,1) == select-fieldset-1.html select-fieldset-ref.html
 fuzzy-if(Android,12,1) fuzzy-if(skiaContent&&!Android,2,17) == select-fieldset-2.html select-fieldset-ref-disabled.html
 fuzzy-if(skiaContent&&!Android,2,17) == select-fieldset-3.html select-fieldset-ref-disabled.html
 fuzzy-if(Android,12,1) fuzzy-if(skiaContent&&!Android,2,13) == select-fieldset-4.html select-fieldset-ref.html
 == select-fieldset-legend-1.html select-fieldset-legend-ref-1.html
 fuzzy-if(Android,12,1) fuzzy-if(skiaContent&&!Android,2,6) == select-fieldset-legend-2.html select-fieldset-legend-ref-2.html
 fuzzy-if(Android,12,1) fuzzy-if(skiaContent&&!Android,2,8) == select-fieldset-legend-3.html select-fieldset-legend-ref-3.html
-fuzzy-if(skiaContent,2,12) == select-fieldset-legend-4.html select-fieldset-legend-ref-4.html
+fuzzy-if(Android,12,1) fuzzy-if(skiaContent,2,12) == select-fieldset-legend-4.html select-fieldset-legend-ref-4.html
 fuzzy-if(skiaContent,2,5) == select-fieldset-legend-5.html select-fieldset-legend-ref-5.html
--- 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) {
         }
 
deleted file mode 100644
--- a/testing/web-platform/meta/webdriver/tests/navigation/current_url.py.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[current_url.py]
-  [test_get_current_url_after_modified_location]
-    expected:
-      if debug and stylo and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and stylo and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-
--- 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);