Merge m-c to autoland, a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 19 Sep 2017 14:54:09 -0700
changeset 433774 fa4de7ce4de0d9d5fcb4baffa600e439992b9e4b
parent 433773 0ed0593535e0d6fe78550c0e9f5520e79a164a4f (current diff)
parent 433734 a0eb21bf55e1c1ae0ba311e6f2273da05c712799 (diff)
child 433775 a6f36a2ff650f7a0cda479bfb5dbf38f199c729d
push id1567
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 12:36:05 +0000
treeherdermozilla-release@e512c14a0406 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone57.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 m-c to autoland, a=merge MozReview-Commit-ID: BLgnf5zTCU3
dom/webauthn/u2f-hid-rs/build.rs
dom/webauthn/u2f-hid-rs/src/runloop.rs
modules/libpref/init/all.js
taskcluster/docker/android-build/buildprops.json
taskcluster/docker/android-build/oauth.txt
taskcluster/docker/recipes/install-mercurial.sh
taskcluster/scripts/builder/build-android-dependencies/after.sh
taskcluster/scripts/builder/build-android-dependencies/before.sh
testing/web-platform/meta/FileAPI/url/url_createobjecturl_blob.html.ini
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -39,17 +39,16 @@ function openContextMenu(aMessage) {
   gContextMenuContentData = { context: data.context,
                               isRemote: data.isRemote,
                               popupNodeSelectors: data.popupNodeSelectors,
                               browser,
                               editFlags: data.editFlags,
                               spellInfo,
                               principal: data.principal,
                               customMenuItems: data.customMenuItems,
-                              addonInfo: data.addonInfo,
                               documentURIObject,
                               docLocation: data.docLocation,
                               charSet: data.charSet,
                               referrer: data.referrer,
                               referrerPolicy: data.referrerPolicy,
                               contentType: data.contentType,
                               contentDisposition: data.contentDisposition,
                               frameOuterWindowID: data.frameOuterWindowID,
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -542,17 +542,17 @@ var DownloadsPanel = {
       if (this._state != this.kStateWaitingAnchor) {
         return;
       }
 
       // At this point, if the window is minimized, opening the panel could fail
       // without any notification, and there would be no way to either open or
       // close the panel any more.  To prevent this, check if the window is
       // minimized and in that case force the panel to the closed state.
-      if (window.windowState == Ci.nsIDOMChromeWindow.STATE_MINIMIZED) {
+      if (window.windowState == window.STATE_MINIMIZED) {
         DownloadsButton.releaseAnchor();
         this._state = this.kStateHidden;
         return;
       }
 
       if (!anchor) {
         DownloadsCommon.error("Downloads button cannot be found.");
         return;
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -1066,16 +1066,20 @@ var gPrivacyPane = {
     let safeBrowsingMalwarePref = document.getElementById("browser.safebrowsing.malware.enabled");
 
     let blockDownloadsPref = document.getElementById("browser.safebrowsing.downloads.enabled");
     let malwareTable = document.getElementById("urlclassifier.malwareTable");
 
     let blockUnwantedPref = document.getElementById("browser.safebrowsing.downloads.remote.block_potentially_unwanted");
     let blockUncommonPref = document.getElementById("browser.safebrowsing.downloads.remote.block_uncommon");
 
+    let learnMoreLink = document.getElementById("enableSafeBrowsingLearnMore");
+    let phishingUrl = "https://support.mozilla.org/kb/how-does-phishing-and-malware-protection-work";
+    learnMoreLink.setAttribute("href", phishingUrl);
+
     enableSafeBrowsing.addEventListener("command", function() {
       safeBrowsingPhishingPref.value = enableSafeBrowsing.checked;
       safeBrowsingMalwarePref.value = enableSafeBrowsing.checked;
 
       if (enableSafeBrowsing.checked) {
         if (blockDownloads) {
           blockDownloads.removeAttribute("disabled");
           if (blockDownloads.checked) {
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -757,19 +757,23 @@
       hidden="true"
       data-category="panePrivacy">
   <label class="header-name" flex="1">&security.label;</label>
 </hbox>
 
 <!-- addons, forgery (phishing) UI Security -->
 <groupbox id="browsingProtectionGroup" data-category="panePrivacy" hidden="true">
   <caption><label>&browsingProtection.label;</label></caption>
-  <checkbox id="enableSafeBrowsing"
-            label="&enableSafeBrowsing.label;"
-            accesskey="&enableSafeBrowsing.accesskey;" />
+  <hbox align = "center">
+    <checkbox id="enableSafeBrowsing"
+              label="&enableSafeBrowsing.label;"
+              accesskey="&enableSafeBrowsing.accesskey;" />
+    <label id="enableSafeBrowsingLearnMore"
+           class="learnMore text-link">&enableSafeBrowsingLearnMore.label;</label>
+  </hbox>
   <vbox class="indent">
 #ifdef MOZILLA_OFFICIAL
     <checkbox id="blockDownloads"
               label="&blockDownloads.label;"
               accesskey="&blockDownloads.accesskey;" />
 #endif
     <checkbox id="blockUncommonUnwanted"
               label="&blockUncommonAndUnwanted.label;"
--- a/browser/locales/en-US/chrome/browser/preferences/privacy.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/privacy.dtd
@@ -112,8 +112,9 @@
 <!ENTITY  browserContainersEnabled.label        "Enable Container Tabs">
 <!ENTITY  browserContainersEnabled.accesskey    "n">
 <!ENTITY  browserContainersSettings.label        "Settings…">
 <!ENTITY  browserContainersSettings.accesskey    "i">
 
 <!ENTITY  a11yPrivacy.checkbox.label     "Prevent accessibility services from accessing your browser">
 <!ENTITY  a11yPrivacy.checkbox.accesskey "a">
 <!ENTITY  a11yPrivacy.learnmore.label    "Learn more">
+<!ENTITY enableSafeBrowsingLearnMore.label "Learn more">
\ No newline at end of file
--- a/browser/modules/ContextMenu.jsm
+++ b/browser/modules/ContextMenu.jsm
@@ -505,25 +505,16 @@ class ContextMenu {
 
       defaultPrevented = false;
     }
 
     if (defaultPrevented) {
       return;
     }
 
-    let addonInfo = Object.create(null);
-    let subject = {
-      aEvent,
-      addonInfo,
-    };
-
-    subject.wrappedJSObject = subject;
-    Services.obs.notifyObservers(subject, "content-contextmenu");
-
     let doc = aEvent.target.ownerDocument;
     let {
       mozDocumentURIIfNotForErrorPages: docLocation,
       characterSet: charSet,
       baseURI,
       referrer,
       referrerPolicy
     } = doc;
@@ -607,17 +598,16 @@ class ContextMenu {
     }
 
     let data = {
       context,
       charSet,
       baseURI,
       isRemote,
       referrer,
-      addonInfo,
       editFlags,
       principal,
       spellInfo,
       contentType,
       docLocation,
       loginFillInfo,
       selectionInfo,
       userContextId,
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -14,16 +14,17 @@
 #include "nsIBaseWindow.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIScrollable.h"
 #include "nsITextScroll.h"
 #include "nsIContentViewerContainer.h"
 #include "nsIDOMStorageManager.h"
 #include "nsDocLoader.h"
 #include "mozilla/BasePrincipal.h"
+#include "mozilla/Move.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "GeckoProfiler.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/LinkedList.h"
 #include "jsapi.h"
 
@@ -286,16 +287,51 @@ public:
 
   const Encoding* GetForcedCharset() { return mForcedCharset; }
 
   mozilla::HTMLEditor* GetHTMLEditorInternal();
   nsresult SetHTMLEditorInternal(mozilla::HTMLEditor* aHTMLEditor);
 
   nsDOMNavigationTiming* GetNavigationTiming() const;
 
+  /**
+   * Get the list of ancestor principals for this docshell.  The list is meant
+   * to be the list of principals of the documents this docshell is "nested
+   * through" in the sense of
+   * <https://html.spec.whatwg.org/multipage/browsers.html#browsing-context-nested-through>.
+   * In practice, it is defined as follows:
+   *
+   * If this is an <iframe mozbrowser> or a toplevel content docshell
+   * (i.e. toplevel document in spec terms), the list is empty.
+   *
+   * Otherwise the list is the list for the document we're nested through (again
+   * in the spec sense), with the principal of that document prepended.  Note
+   * that this matches the ordering specified for Location.ancestorOrigins.
+   */
+  const nsTArray<nsCOMPtr<nsIPrincipal>>& AncestorPrincipals() const
+  {
+    return mAncestorPrincipals;
+  }
+
+  /**
+   * Set the list of ancestor principals for this docshell.  This is really only
+   * needed for use by the frameloader.  We can't do this ourselves, inside
+   * docshell, because there's a bunch of state setup that frameloader does
+   * (like telling us whether we're a mozbrowser), some of which comes after the
+   * docshell is added to the docshell tree, which can affect what the ancestor
+   * principals should look like.
+   *
+   * This method steals the data from the passed-in array.
+   */
+  void SetAncestorPrincipals(
+    nsTArray<nsCOMPtr<nsIPrincipal>>&& aAncestorPrincipals)
+  {
+    mAncestorPrincipals = mozilla::Move(aAncestorPrincipals);
+  }
+
 private:
   bool CanSetOriginAttributes();
 
 public:
   const mozilla::OriginAttributes&
   GetOriginAttributes()
   {
     return mOriginAttributes;
@@ -1089,16 +1125,19 @@ private:
   // A depth count of how many times NotifyRunToCompletionStart
   // has been called without a matching NotifyRunToCompletionStop.
   uint32_t mJSRunToCompletionDepth;
 
   // Whether or not touch events are overridden. Possible values are defined
   // as constants in the nsIDocShell.idl file.
   uint32_t mTouchEventsOverride;
 
+  // Our list of ancestor principals.
+  nsTArray<nsCOMPtr<nsIPrincipal>> mAncestorPrincipals;
+
   // Separate function to do the actual name (i.e. not _top, _self etc.)
   // searching for FindItemWithName.
   nsresult DoFindItemWithName(const nsAString& aName,
                               nsIDocShellTreeItem* aRequestor,
                               nsIDocShellTreeItem* aOriginalRequestor,
                               bool aSkipTabGroup,
                               nsIDocShellTreeItem** aResult);
 
--- a/dom/base/DOMIntersectionObserver.cpp
+++ b/dom/base/DOMIntersectionObserver.cpp
@@ -156,16 +156,20 @@ DOMIntersectionObserver::Observe(Element
   aTarget.RegisterIntersectionObserver(this);
   mObservationTargets.AppendElement(&aTarget);
   Connect();
 }
 
 void
 DOMIntersectionObserver::Unobserve(Element& aTarget)
 {
+  if (!mObservationTargets.Contains(&aTarget)) {
+    return;
+  }
+
   if (mObservationTargets.Length() == 1) {
     Disconnect();
     return;
   }
 
   mObservationTargets.RemoveElement(&aTarget);
   aTarget.UnregisterIntersectionObserver(this);
 }
--- a/dom/base/nsDeprecatedOperationList.h
+++ b/dom/base/nsDeprecatedOperationList.h
@@ -45,8 +45,9 @@ DEPRECATED_OPERATION(AppCache)
 DEPRECATED_OPERATION(PrefixedImageSmoothingEnabled)
 DEPRECATED_OPERATION(PrefixedFullscreenAPI)
 DEPRECATED_OPERATION(LenientSetter)
 DEPRECATED_OPERATION(FileLastModifiedDate)
 DEPRECATED_OPERATION(ImageBitmapRenderingContext_TransferImageBitmap)
 DEPRECATED_OPERATION(URLCreateObjectURL_MediaStream)
 DEPRECATED_OPERATION(XMLBaseAttribute)
 DEPRECATED_OPERATION(XMLBaseAttributeForStyleAttr)
+DEPRECATED_OPERATION(WindowContentUntrusted)
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -4960,16 +4960,18 @@ nsIDocument::SetContainer(nsDocShell* aC
     NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
 
     if (sameTypeRoot == aContainer) {
       static_cast<nsDocument*>(this)->SetIsTopLevelContentDocument(true);
     }
 
     static_cast<nsDocument*>(this)->SetIsContentDocument(true);
   }
+
+  mAncestorPrincipals = aContainer->AncestorPrincipals();
 }
 
 nsISupports*
 nsIDocument::GetContainer() const
 {
   return static_cast<nsIDocShell*>(mDocumentContainer);
 }
 
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2527,17 +2527,17 @@ nsFrameLoader::MaybeCreateDocShell()
   if (!frameName.IsEmpty()) {
     mDocShell->SetName(frameName);
   }
 
   // Inform our docShell that it has a new child.
   // Note: This logic duplicates a lot of logic in
   // nsSubDocumentFrame::AttributeChanged.  We should fix that.
 
-  int32_t parentType = docShell->ItemType();
+  const int32_t parentType = docShell->ItemType();
 
   // XXXbz why is this in content code, exactly?  We should handle
   // this some other way.....  Not sure how yet.
   nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
   docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
   NS_ENSURE_STATE(parentTreeOwner);
   mIsTopLevelContent =
     AddTreeItemToTreeOwner(mDocShell, parentTreeOwner, parentType, docShell);
@@ -2706,16 +2706,26 @@ nsFrameLoader::MaybeCreateDocShell()
         // and not inherited by its parent.
         attrs.SyncAttributesWithPrivateBrowsing(isPrivate);
       }
     }
   }
 
   nsDocShell::Cast(mDocShell)->SetOriginAttributes(attrs);
 
+  if (!mDocShell->GetIsMozBrowser() &&
+      parentType == mDocShell->ItemType()) {
+    // Propagate through the ancestor principals.
+    nsTArray<nsCOMPtr<nsIPrincipal>> ancestorPrincipals;
+    // Make a copy, so we can modify it.
+    ancestorPrincipals = doc->AncestorPrincipals();
+    ancestorPrincipals.InsertElementAt(0, doc->NodePrincipal());
+    nsDocShell::Cast(mDocShell)->SetAncestorPrincipals(Move(ancestorPrincipals));
+  }
+
   ReallyLoadFrameScripts();
   InitializeBrowserAPI();
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
                         "inprocess-browser-shown", nullptr);
   }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1139,31 +1139,30 @@ public:
   bool hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy,
               JS::Handle<jsid> id, bool *bp) const override;
   bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> proxy,
                                     JS::AutoIdVector &props) const override;
   const char *className(JSContext *cx,
                         JS::Handle<JSObject*> wrapper) const override;
 
   void finalize(JSFreeOp *fop, JSObject *proxy) const override;
+  size_t objectMoved(JSObject* proxy, JSObject* old) const override;
 
   bool isCallable(JSObject *obj) const override {
     return false;
   }
   bool isConstructor(JSObject *obj) const override {
     return false;
   }
 
   bool watch(JSContext *cx, JS::Handle<JSObject*> proxy,
              JS::Handle<jsid> id, JS::Handle<JSObject*> callable) const override;
   bool unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
                JS::Handle<jsid> id) const override;
 
-  static void ObjectMoved(JSObject *obj, const JSObject *old);
-
   static const nsOuterWindowProxy singleton;
 
 protected:
   static nsGlobalWindow* GetOuterWindow(JSObject *proxy)
   {
     nsGlobalWindow* outerWindow = nsGlobalWindow::FromSupports(
       static_cast<nsISupports*>(js::GetProxyReservedSlot(proxy, 0).toPrivate()));
     MOZ_ASSERT_IF(outerWindow, outerWindow->IsOuterWindow());
@@ -1183,27 +1182,22 @@ protected:
   GetSubframeWindow(JSContext *cx,
                     JS::Handle<JSObject*> proxy,
                     JS::Handle<jsid> id) const;
 
   bool AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
                                   JS::AutoIdVector &props) const;
 };
 
-static const js::ClassExtension OuterWindowProxyClassExtension = PROXY_MAKE_EXT(
-    nsOuterWindowProxy::ObjectMoved
-);
-
 // Give OuterWindowProxyClass 2 reserved slots, like the other wrappers, so
 // JSObject::swap can swap it with CrossCompartmentWrappers without requiring
 // malloc.
-const js::Class OuterWindowProxyClass = PROXY_CLASS_WITH_EXT(
+const js::Class OuterWindowProxyClass = PROXY_CLASS_DEF(
     "Proxy",
-    JSCLASS_HAS_RESERVED_SLOTS(2), /* additional class flags */
-    &OuterWindowProxyClassExtension);
+    JSCLASS_HAS_RESERVED_SLOTS(2)); /* additional class flags */
 
 const char *
 nsOuterWindowProxy::className(JSContext *cx, JS::Handle<JSObject*> proxy) const
 {
     MOZ_ASSERT(js::IsProxy(proxy));
 
     return "Window";
 }
@@ -1521,23 +1515,24 @@ nsOuterWindowProxy::watch(JSContext *cx,
 
 bool
 nsOuterWindowProxy::unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
                             JS::Handle<jsid> id) const
 {
   return js::UnwatchGuts(cx, proxy, id);
 }
 
-void
-nsOuterWindowProxy::ObjectMoved(JSObject *obj, const JSObject *old)
+size_t
+nsOuterWindowProxy::objectMoved(JSObject *obj, JSObject *old) const
 {
   nsGlobalWindow* outerWindow = GetOuterWindow(obj);
   if (outerWindow) {
     outerWindow->UpdateWrapper(obj, old);
   }
+  return 0;
 }
 
 const nsOuterWindowProxy
 nsOuterWindowProxy::singleton;
 
 class nsChromeOuterWindowProxy : public nsOuterWindowProxy
 {
 public:
@@ -4984,16 +4979,19 @@ nsGlobalWindow::GetContentInternal(Error
   // If we're contained in <iframe mozbrowser>, then GetContent is the same as
   // window.top.
   if (mDocShell && mDocShell->GetIsInMozBrowser()) {
     return GetTopOuter();
   }
 
   nsCOMPtr<nsIDocShellTreeItem> primaryContent;
   if (aCallerType != CallerType::System) {
+    if (mDoc) {
+      mDoc->WarnOnceAbout(nsIDocument::eWindowContentUntrusted);
+    }
     // If we're called by non-chrome code, make sure we don't return
     // the primary content window if the calling tab is hidden. In
     // such a case we return the same-type root in the hidden tab,
     // which is "good enough", for now.
     nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(mDocShell));
 
     if (baseWin) {
       bool visible = false;
@@ -14062,179 +14060,114 @@ NS_IMPL_RELEASE_INHERITED(nsGlobalChrome
 /* static */ already_AddRefed<nsGlobalChromeWindow>
 nsGlobalChromeWindow::Create(nsGlobalWindow *aOuterWindow)
 {
   RefPtr<nsGlobalChromeWindow> window = new nsGlobalChromeWindow(aOuterWindow);
   window->InitWasOffline();
   return window.forget();
 }
 
-NS_IMETHODIMP
-nsGlobalChromeWindow::GetWindowState(uint16_t* aWindowState)
-{
-  FORWARD_TO_INNER_CHROME(GetWindowState, (aWindowState), NS_ERROR_UNEXPECTED);
-
-  *aWindowState = WindowState();
-  return NS_OK;
-}
+enum WindowState {
+  // These constants need to match the constants in Window.webidl
+  STATE_MAXIMIZED = 1,
+  STATE_MINIMIZED = 2,
+  STATE_NORMAL = 3,
+  STATE_FULLSCREEN = 4
+};
 
 uint16_t
 nsGlobalWindow::WindowState()
 {
   MOZ_ASSERT(IsInnerWindow());
   nsCOMPtr<nsIWidget> widget = GetMainWidget();
 
   int32_t mode = widget ? widget->SizeMode() : 0;
 
   switch (mode) {
     case nsSizeMode_Minimized:
-      return nsIDOMChromeWindow::STATE_MINIMIZED;
+      return STATE_MINIMIZED;
     case nsSizeMode_Maximized:
-      return nsIDOMChromeWindow::STATE_MAXIMIZED;
+      return STATE_MAXIMIZED;
     case nsSizeMode_Fullscreen:
-      return nsIDOMChromeWindow::STATE_FULLSCREEN;
+      return STATE_FULLSCREEN;
     case nsSizeMode_Normal:
-      return nsIDOMChromeWindow::STATE_NORMAL;
+      return STATE_NORMAL;
     default:
       NS_WARNING("Illegal window state for this chrome window");
       break;
   }
 
-  return nsIDOMChromeWindow::STATE_NORMAL;
+  return STATE_NORMAL;
 }
 
 bool
 nsGlobalWindow::IsFullyOccluded()
 {
   MOZ_ASSERT(IsInnerWindow());
 
   nsCOMPtr<nsIWidget> widget = GetMainWidget();
   return widget && widget->IsFullyOccluded();
 }
 
-NS_IMETHODIMP
-nsGlobalChromeWindow::Maximize()
-{
-  FORWARD_TO_INNER_CHROME(Maximize, (), NS_ERROR_UNEXPECTED);
-
-  nsGlobalWindow::Maximize();
-  return NS_OK;
-}
-
 void
 nsGlobalWindow::Maximize()
 {
   MOZ_ASSERT(IsInnerWindow());
 
   nsCOMPtr<nsIWidget> widget = GetMainWidget();
 
   if (widget) {
     widget->SetSizeMode(nsSizeMode_Maximized);
   }
 }
 
-NS_IMETHODIMP
-nsGlobalChromeWindow::Minimize()
-{
-  FORWARD_TO_INNER_CHROME(Minimize, (), NS_ERROR_UNEXPECTED);
-
-  nsGlobalWindow::Minimize();
-  return NS_OK;
-}
-
 void
 nsGlobalWindow::Minimize()
 {
   MOZ_ASSERT(IsInnerWindow());
 
   nsCOMPtr<nsIWidget> widget = GetMainWidget();
 
   if (widget) {
     widget->SetSizeMode(nsSizeMode_Minimized);
   }
 }
 
-NS_IMETHODIMP
-nsGlobalChromeWindow::Restore()
-{
-  FORWARD_TO_INNER_CHROME(Restore, (), NS_ERROR_UNEXPECTED);
-
-  nsGlobalWindow::Restore();
-  return NS_OK;
-}
-
 void
 nsGlobalWindow::Restore()
 {
   MOZ_ASSERT(IsInnerWindow());
 
   nsCOMPtr<nsIWidget> widget = GetMainWidget();
 
   if (widget) {
     widget->SetSizeMode(nsSizeMode_Normal);
   }
 }
 
-NS_IMETHODIMP
-nsGlobalChromeWindow::GetAttention()
-{
-  FORWARD_TO_INNER_CHROME(GetAttention, (), NS_ERROR_UNEXPECTED);
-
-  ErrorResult rv;
-  GetAttention(rv);
-  return rv.StealNSResult();
-}
-
 void
 nsGlobalWindow::GetAttention(ErrorResult& aResult)
 {
   MOZ_ASSERT(IsInnerWindow());
   return GetAttentionWithCycleCount(-1, aResult);
 }
 
-NS_IMETHODIMP
-nsGlobalChromeWindow::GetAttentionWithCycleCount(int32_t aCycleCount)
-{
-  FORWARD_TO_INNER_CHROME(GetAttentionWithCycleCount, (aCycleCount), NS_ERROR_UNEXPECTED);
-
-  ErrorResult rv;
-  GetAttentionWithCycleCount(aCycleCount, rv);
-  return rv.StealNSResult();
-}
-
 void
 nsGlobalWindow::GetAttentionWithCycleCount(int32_t aCycleCount,
                                            ErrorResult& aError)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   nsCOMPtr<nsIWidget> widget = GetMainWidget();
 
   if (widget) {
     aError = widget->GetAttention(aCycleCount);
   }
 }
 
-NS_IMETHODIMP
-nsGlobalChromeWindow::BeginWindowMove(nsIDOMEvent *aMouseDownEvent, nsIDOMElement* aPanel)
-{
-  FORWARD_TO_INNER_CHROME(BeginWindowMove, (aMouseDownEvent, aPanel), NS_ERROR_UNEXPECTED);
-
-  NS_ENSURE_TRUE(aMouseDownEvent, NS_ERROR_FAILURE);
-  Event* mouseDownEvent = aMouseDownEvent->InternalDOMEvent();
-  NS_ENSURE_TRUE(mouseDownEvent, NS_ERROR_FAILURE);
-
-  nsCOMPtr<Element> panel = do_QueryInterface(aPanel);
-  NS_ENSURE_TRUE(panel || !aPanel, NS_ERROR_FAILURE);
-
-  ErrorResult rv;
-  BeginWindowMove(*mouseDownEvent, panel, rv);
-  return rv.StealNSResult();
-}
-
 void
 nsGlobalWindow::BeginWindowMove(Event& aMouseDownEvent, Element* aPanel,
                                 ErrorResult& aError)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   nsCOMPtr<nsIWidget> widget;
 
@@ -14280,26 +14213,16 @@ nsGlobalWindow::GetWindowRootOuter()
 already_AddRefed<nsWindowRoot>
 nsGlobalWindow::GetWindowRoot(mozilla::ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetWindowRootOuter, (), aError, nullptr);
 }
 
 //Note: This call will lock the cursor, it will not change as it moves.
 //To unlock, the cursor must be set back to CURSOR_AUTO.
-NS_IMETHODIMP
-nsGlobalChromeWindow::SetCursor(const nsAString& aCursor)
-{
-  FORWARD_TO_INNER_CHROME(SetCursor, (aCursor), NS_ERROR_UNEXPECTED);
-
-  ErrorResult rv;
-  SetCursor(aCursor, rv);
-  return rv.StealNSResult();
-}
-
 void
 nsGlobalWindow::SetCursorOuter(const nsAString& aCursor, ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
 
   int32_t cursor;
 
   if (aCursor.EqualsLiteral("auto"))
@@ -14374,55 +14297,31 @@ nsGlobalWindow::GetBrowserDOMWindowOuter
 }
 
 nsIBrowserDOMWindow*
 nsGlobalWindow::GetBrowserDOMWindow(ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetBrowserDOMWindowOuter, (), aError, nullptr);
 }
 
-NS_IMETHODIMP
-nsGlobalChromeWindow::SetBrowserDOMWindow(nsIBrowserDOMWindow *aBrowserWindow)
-{
-  FORWARD_TO_INNER_CHROME(SetBrowserDOMWindow, (aBrowserWindow), NS_ERROR_UNEXPECTED);
-
-  ErrorResult rv;
-  SetBrowserDOMWindow(aBrowserWindow, rv);
-  return rv.StealNSResult();
-}
-
 void
 nsGlobalWindow::SetBrowserDOMWindowOuter(nsIBrowserDOMWindow* aBrowserWindow)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
   MOZ_ASSERT(IsChromeWindow());
   static_cast<nsGlobalChromeWindow*>(this)->mBrowserDOMWindow = aBrowserWindow;
 }
 
 void
 nsGlobalWindow::SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserWindow,
                                     ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(SetBrowserDOMWindowOuter, (aBrowserWindow), aError, );
 }
 
-NS_IMETHODIMP
-nsGlobalChromeWindow::NotifyDefaultButtonLoaded(nsIDOMElement* aDefaultButton)
-{
-  FORWARD_TO_INNER_CHROME(NotifyDefaultButtonLoaded,
-                          (aDefaultButton), NS_ERROR_UNEXPECTED);
-
-  nsCOMPtr<Element> defaultButton = do_QueryInterface(aDefaultButton);
-  NS_ENSURE_ARG(defaultButton);
-
-  ErrorResult rv;
-  NotifyDefaultButtonLoaded(*defaultButton, rv);
-  return rv.StealNSResult();
-}
-
 void
 nsGlobalWindow::NotifyDefaultButtonLoaded(Element& aDefaultButton,
                                           ErrorResult& aError)
 {
   MOZ_ASSERT(IsInnerWindow());
 #ifdef MOZ_XUL
   // Don't snap to a disabled button.
   nsCOMPtr<nsIDOMXULControlElement> xulControl =
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -441,21 +441,35 @@ public:
   }
 
   void ClearHSTSPrimingLocation(nsIURI* aContentLocation)
   {
     mHSTSPrimingURIList.Remove(aContentLocation);
   }
 
   /**
-   * Set the principal responsible for this document.
+   * Set the principal responsible for this document.  Chances are,
+   * you do not want to be using this.
    */
   virtual void SetPrincipal(nsIPrincipal *aPrincipal) = 0;
 
   /**
+   * Get the list of ancestor principals for a document.  This is the same as
+   * the ancestor list for the document's docshell the last time SetContainer()
+   * was called with a non-null argument. See the documentation for the
+   * corresponding getter in docshell for how this list is determined.  We store
+   * a copy of the list, because we may lose the ability to reach our docshell
+   * before people stop asking us for this information.
+   */
+  const nsTArray<nsCOMPtr<nsIPrincipal>>& AncestorPrincipals() const
+  {
+    return mAncestorPrincipals;
+  }
+
+  /**
    * Return the LoadGroup for the document. May return null.
    */
   already_AddRefed<nsILoadGroup> GetDocumentLoadGroup() const
   {
     nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
     return group.forget();
   }
 
@@ -3590,16 +3604,20 @@ protected:
   // calling NoteScriptTrackingStatus().  Currently we assume that a URL not
   // existing in the set means the corresponding script isn't a tracking script.
   nsTHashtable<nsCStringHashKey> mTrackingScripts;
 
   // CSP violation reports that have been buffered up due to a call to
   // StartBufferingCSPViolations.
   nsTArray<nsCOMPtr<nsIRunnable>> mBufferedCSPViolations;
 
+  // List of ancestor principals.  This is set at the point a document
+  // is connected to a docshell and not mutated thereafter.
+  nsTArray<nsCOMPtr<nsIPrincipal>> mAncestorPrincipals;
+
   // Restyle root for servo's style system.
   //
   // We store this as an nsINode, rather than as an Element, so that we can store
   // the Document node as the restyle root if the entire document (along with all
   // document-level native-anonymous content) needs to be restyled.
   //
   // We also track which "descendant" bits (normal/animation-only/lazy-fc) the
   // root corresponds to.
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -618,16 +618,17 @@ skip-if = toolkit == 'android'
 [test_bug1295852.html]
 [test_bug1307730.html]
 [test_bug1308069.html]
 [test_bug1314032.html]
 [test_bug1318303.html]
 [test_bug1375050.html]
 [test_bug1381710.html]
 [test_bug1384658.html]
+[test_bug1399605.html]
 skip-if = toolkit == 'android'
 [test_caretPositionFromPoint.html]
 [test_change_policy.html]
 [test_clearTimeoutIntervalNoArg.html]
 [test_constructor-assignment.html]
 [test_constructor.html]
 [test_copyimage.html]
 subsuite = clipboard
@@ -809,16 +810,17 @@ skip-if = toolkit == 'android'
 skip-if = toolkit == 'android'
 [test_websocket3.html]
 skip-if = toolkit == 'android'
 [test_websocket4.html]
 skip-if = toolkit == 'android'
 [test_websocket5.html]
 skip-if = toolkit == 'android'
 [test_window_constructor.html]
+[test_window_content.html]
 [test_window_cross_origin_props.html]
 [test_window_define_nonconfigurable.html]
 [test_window_define_symbol.html]
 [test_window_element_enumeration.html]
 [test_window_enumeration.html]
 [test_window_extensible.html]
 [test_window_indexing.html]
 [test_window_keys.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1399605.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1399605</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1399605">Mozilla Bug 1399605</a>
+<p id="display"></p>
+<div id="content">
+  <div id="target"></div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+  let observer = new IntersectionObserver(function() {
+    ok(true, "we are still observing");
+    SimpleTest.finish();
+  });
+  observer.observe(document.getElementById('target'));
+  observer.unobserve(document.getElementById('content'));
+  SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<div id="log">
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_window_content.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1400139
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1400139</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1400139 **/
+  var props = [];
+  for (var prop in window) props.push(prop);
+  is(props.indexOf("content"), -1, "Should not have a property named 'content'");
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1400139">Mozilla Bug 1400139</a>
+<p id="display"></p>
+<div id="notcontent" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -539,33 +539,26 @@ class CGDOMProxyJSClass(CGThing):
         # We need one reserved slot (DOM_OBJECT_SLOT).
         flags = ["JSCLASS_IS_DOMJSCLASS",
                  "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount]
         # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because
         # we don't want people ever adding that to any interface other than
         # HTMLAllCollection.  So just hardcode it here.
         if self.descriptor.interface.identifier.name == "HTMLAllCollection":
             flags.append("JSCLASS_EMULATES_UNDEFINED")
-        objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
         return fill(
             """
-            static const js::ClassExtension sClassExtension = PROXY_MAKE_EXT(
-                ${objectMoved}
-            );
-
             static const DOMJSClass sClass = {
-              PROXY_CLASS_WITH_EXT("${name}",
-                                   ${flags},
-                                   &sClassExtension),
+              PROXY_CLASS_DEF("${name}",
+                              ${flags}),
               $*{descriptor}
             };
             """,
             name=self.descriptor.interface.identifier.name,
             flags=" | ".join(flags),
-            objectMoved=objectMovedHook,
             descriptor=DOMClass(self.descriptor))
 
 
 class CGXrayExpandoJSClass(CGThing):
     """
     Generate a JSClass for an Xray expando object.  This is only
     needed if we have members in slots (for [Cached] or [StoreInSlot]
     stuff).
@@ -1726,30 +1719,42 @@ class CGClassFinalizeHook(CGAbstractClas
         CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
                                      'void', args)
 
     def generate_code(self):
         return finalizeHook(self.descriptor, self.name,
                             self.args[0].name, self.args[1].name).define()
 
 
+def objectMovedHook(descriptor, hookName, obj, old):
+    assert descriptor.wrapperCache
+    return fill("""
+	if (self) {
+	  UpdateWrapper(self, self, ${obj}, ${old});
+	}
+
+	return 0;
+	""",
+	obj=obj,
+	old=old)
+
+
 class CGClassObjectMovedHook(CGAbstractClassHook):
     """
     A hook for objectMovedOp, used to update the wrapper cache when an object it
     is holding moves.
     """
     def __init__(self, descriptor):
-        args = [Argument('JSObject*', 'obj'), Argument('const JSObject*', 'old')]
+        args = [Argument('JSObject*', 'obj'), Argument('JSObject*', 'old')]
         CGAbstractClassHook.__init__(self, descriptor, OBJECT_MOVED_HOOK_NAME,
-                                     'void', args)
+                                     'size_t', args)
 
     def generate_code(self):
-        assert self.descriptor.wrapperCache
-        return CGIfWrapper(CGGeneric("UpdateWrapper(self, self, obj, old);\n"),
-                           "self").define()
+        return objectMovedHook(self.descriptor, self.name,
+                               self.args[0].name, self.args[1].name)
 
 
 def JSNativeArguments():
     return [Argument('JSContext*', 'cx'),
             Argument('unsigned', 'argc'),
             Argument('JS::Value*', 'vp')]
 
 
@@ -2356,16 +2361,22 @@ def MakeClearCachedValueNativeName(membe
 def MakeJSImplClearCachedValueNativeName(member):
     return "_" + MakeClearCachedValueNativeName(member)
 
 
 def IDLToCIdentifier(name):
     return name.replace("-", "_")
 
 
+def EnumerabilityFlags(member):
+    if member.getExtendedAttribute("NonEnumerable"):
+        return "0"
+    return "JSPROP_ENUMERATE"
+
+
 class MethodDefiner(PropertyDefiner):
     """
     A class for defining methods on a prototype object.
     """
     def __init__(self, descriptor, name, static, unforgeable=False):
         assert not (static and unforgeable)
         PropertyDefiner.__init__(self, descriptor, name)
 
@@ -2416,28 +2427,21 @@ class MethodDefiner(PropertyDefiner):
                     "name": 'QueryInterface',
                     "methodInfo": False,
                     "length": 1,
                     "flags": "0",
                     "condition": MemberCondition(func=condition)
                 })
                 continue
 
-            # Iterable methods should be enumerable, maplike/setlike methods
-            # should not.
-            isMaplikeOrSetlikeMethod = (m.isMaplikeOrSetlikeOrIterableMethod() and
-                                        (m.maplikeOrSetlikeOrIterable.isMaplike() or
-                                         m.maplikeOrSetlikeOrIterable.isSetlike()))
             method = {
                 "name": m.identifier.name,
                 "methodInfo": not m.isStatic(),
                 "length": methodLength(m),
-                # Methods generated for a maplike/setlike declaration are not
-                # enumerable.
-                "flags": "JSPROP_ENUMERATE" if not isMaplikeOrSetlikeMethod else "0",
+                "flags": EnumerabilityFlags(m),
                 "condition": PropertyDefiner.getControllingCondition(m, descriptor),
                 "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
                 "returnsPromise": m.returnsPromise(),
                 "hasIteratorAlias": "@@iterator" in m.aliases
             }
 
             if m.isStatic():
                 method["nativeName"] = CppKeywords.checkMethodName(IDLToCIdentifier(m.identifier.name))
@@ -2723,19 +2727,17 @@ class AttrDefiner(PropertyDefiner):
                 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
 
     def generateArray(self, array, name):
         if len(array) == 0:
             return ""
 
         def flags(attr):
             unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
-            # Attributes generated as part of a maplike/setlike declaration are
-            # not enumerable.
-            enumerable = " | JSPROP_ENUMERATE" if not attr.isMaplikeOrSetlikeAttr() else ""
+            enumerable = " | %s" % EnumerabilityFlags(attr)
             return ("JSPROP_SHARED" + enumerable + unforgeable)
 
         def getter(attr):
             if self.static:
                 if attr.type.isPromise():
                     raise TypeError("Don't know how to handle "
                                     "static Promise-returning "
                                     "attribute %s.%s" %
@@ -6846,18 +6848,17 @@ def getWrapTemplateForType(type, descrip
                 wrapMethod = "WrapNewBindingNonWrapperCachedObject"
                 wrapArgs = "cx, ${obj}, %s, ${jsvalHandle}" % result
             if isConstructorRetval:
                 wrapArgs += ", desiredProto"
             wrap = "%s(%s)" % (wrapMethod, wrapArgs)
             if not descriptor.hasXPConnectImpls:
                 # Can only fail to wrap as a new-binding object
                 # if they already threw an exception.
-                # XXX Assertion disabled for now, see bug 991271.
-                failed = ("MOZ_ASSERT(true || JS_IsExceptionPending(cx));\n" +
+                failed = ("MOZ_ASSERT(JS_IsExceptionPending(cx));\n" +
                           exceptionCode)
             else:
                 if descriptor.notflattened:
                     raise TypeError("%s has XPConnect impls but not flattened; "
                                     "fallback won't work correctly" %
                                     descriptor.interface.identifier.name)
                 # Try old-style wrapping for bindings which might be XPConnect impls.
                 failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalHandle})" % result)
@@ -12403,16 +12404,30 @@ class CGDOMJSProxyHandler_finalize(Class
 
     def getBody(self):
         return (("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(proxy);\n" %
                  (self.descriptor.nativeType, self.descriptor.nativeType)) +
                 finalizeHook(self.descriptor, FINALIZE_HOOK_NAME,
                              self.args[0].name, self.args[1].name).define())
 
 
+class CGDOMJSProxyHandler_objectMoved(ClassMethod):
+    def __init__(self, descriptor):
+        args = [Argument('JSObject*', 'obj'), Argument('JSObject*', 'old')]
+        ClassMethod.__init__(self, "objectMoved", "size_t", args,
+                             virtual=True, override=True, const=True)
+        self.descriptor = descriptor
+
+    def getBody(self):
+        return (("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" %
+                 (self.descriptor.nativeType, self.descriptor.nativeType)) +
+                objectMovedHook(self.descriptor, OBJECT_MOVED_HOOK_NAME,
+                                self.args[0].name, self.args[1].name))
+
+
 class CGDOMJSProxyHandler_getElements(ClassMethod):
     def __init__(self, descriptor):
         assert descriptor.supportsIndexedProperties()
 
         args = [Argument('JSContext*', 'cx'),
                 Argument('JS::Handle<JSObject*>', 'proxy'),
                 Argument('uint32_t', 'begin'),
                 Argument('uint32_t', 'end'),
@@ -12578,16 +12593,18 @@ class CGDOMJSProxyHandler(CGClass):
         if descriptor.operations['LegacyCaller']:
             methods.append(CGDOMJSProxyHandler_call())
             methods.append(CGDOMJSProxyHandler_isCallable())
         if descriptor.interface.hasProbablyShortLivingWrapper():
             if not descriptor.wrapperCache:
                 raise TypeError("Need a wrapper cache to support nursery "
                                 "allocation of DOM objects")
             methods.append(CGDOMJSProxyHandler_canNurseryAllocate())
+        if descriptor.wrapperCache:
+            methods.append(CGDOMJSProxyHandler_objectMoved(descriptor))
 
         if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
             parentClass = 'ShadowingDOMProxyHandler'
         else:
             parentClass = 'mozilla::dom::DOMProxyHandler'
 
         CGClass.__init__(self, 'DOMProxyHandler',
                          bases=[ClassBase(parentClass)],
@@ -12834,17 +12851,17 @@ class CGDescriptor(CGThing):
         if descriptor.concrete and not descriptor.proxy:
             if wantsAddProperty(descriptor):
                 cgThings.append(CGAddPropertyHook(descriptor))
 
             # Always have a finalize hook, regardless of whether the class
             # wants a custom hook.
             cgThings.append(CGClassFinalizeHook(descriptor))
 
-        if descriptor.concrete and descriptor.wrapperCache:
+        if descriptor.concrete and descriptor.wrapperCache and not descriptor.proxy:
             cgThings.append(CGClassObjectMovedHook(descriptor))
 
         # Generate the _ClearCachedFooValue methods before the property arrays that use them.
         if descriptor.interface.isJSImplemented():
             for m in clearableCachedAttrs(descriptor):
                 cgThings.append(CGJSImplClearCachedValueMethod(descriptor, m))
 
         # Need to output our generated hasinstance bits before
@@ -15366,18 +15383,17 @@ class CGJSImplMethod(CGJSImplMember):
             constructorArgs.append("js::GetObjectCompartment(scopeObj)")
             initCall = fill(
                 """
                 // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
                 JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject());
                 MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx));
                 JS::Rooted<JS::Value> wrappedVal(cx);
                 if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal, aGivenProto)) {
-                  //XXX Assertion disabled for now, see bug 991271.
-                  MOZ_ASSERT(true || JS_IsExceptionPending(cx));
+                  MOZ_ASSERT(JS_IsExceptionPending(cx));
                   aRv.Throw(NS_ERROR_UNEXPECTED);
                   return nullptr;
                 }
                 // Initialize the object with the constructor arguments.
                 impl->mImpl->__Init(${args});
                 if (aRv.Failed()) {
                   return nullptr;
                 }
--- a/dom/bindings/SimpleGlobalObject.cpp
+++ b/dom/bindings/SimpleGlobalObject.cpp
@@ -45,22 +45,23 @@ static void
 SimpleGlobal_finalize(js::FreeOp *fop, JSObject *obj)
 {
   SimpleGlobalObject* globalObject =
     static_cast<SimpleGlobalObject*>(JS_GetPrivate(obj));
   globalObject->ClearWrapper(obj);
   NS_RELEASE(globalObject);
 }
 
-static void
-SimpleGlobal_moved(JSObject *obj, const JSObject *old)
+static size_t
+SimpleGlobal_moved(JSObject *obj, JSObject *old)
 {
   SimpleGlobalObject* globalObject =
     static_cast<SimpleGlobalObject*>(JS_GetPrivate(obj));
   globalObject->UpdateWrapper(obj, old);
+  return 0;
 }
 
 static const js::ClassOps SimpleGlobalClassOps = {
     nullptr,
     nullptr,
     nullptr,
     JS_NewEnumerateStandardClasses,
     JS_ResolveStandardClass,
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -3698,16 +3698,21 @@ class IDLMaplikeOrSetlikeOrIterableBase(
                 [IDLExtendedAttribute(self.location, ("DependsOn", "Everything")),
                  IDLExtendedAttribute(self.location, ("Affects", "Nothing"))])
         if newObject:
             method.addExtendedAttributes(
                 [IDLExtendedAttribute(self.location, ("NewObject",))])
         if isIteratorAlias:
             method.addExtendedAttributes(
                 [IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))])
+        # Methods generated for iterables should be enumerable, but the ones for
+        # maplike/setlike should not be.
+        if not self.isIterable():
+            method.addExtendedAttributes(
+                [IDLExtendedAttribute(self.location, ("NonEnumerable",))])
         members.append(method)
 
     def resolve(self, parentScope):
         if self.keyType:
             self.keyType.resolveType(parentScope)
         if self.valueType:
             self.valueType.resolveType(parentScope)
 
@@ -3821,21 +3826,25 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSe
 
     def expand(self, members, isJSImplemented):
         """
         In order to take advantage of all of the method machinery in Codegen,
         we generate our functions as if they were part of the interface
         specification during parsing.
         """
         # Both maplike and setlike have a size attribute
-        members.append(IDLAttribute(self.location,
-                                    IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
-                                    BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
-                                    True,
-                                    maplikeOrSetlike=self))
+        sizeAttr = IDLAttribute(self.location,
+                                IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
+                                BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
+                                True,
+                                maplikeOrSetlike=self)
+        # This should be non-enumerable.
+        sizeAttr.addExtendedAttributes(
+                [IDLExtendedAttribute(self.location, ("NonEnumerable",))])
+        members.append(sizeAttr)
         self.reserved_ro_names = ["size"]
         self.disallowedMemberNames.append("size")
 
         # object entries()
         self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
                        affectsNothing=True, isIteratorAlias=self.isMaplike())
         # object keys()
         self.addMethod("keys", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
@@ -3961,17 +3970,18 @@ class IDLConst(IDLInterfaceMember):
 
     def handleExtendedAttribute(self, attr):
         identifier = attr.identifier()
         if identifier == "Exposed":
             convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
         elif (identifier == "Pref" or
               identifier == "ChromeOnly" or
               identifier == "Func" or
-              identifier == "SecureContext"):
+              identifier == "SecureContext" or
+              identifier == "NonEnumerable"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
             raise WebIDLError("Unknown extended attribute %s on constant" % identifier,
                               [attr.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
     def _getDependentObjects(self):
@@ -4339,17 +4349,18 @@ class IDLAttribute(IDLInterfaceMember):
               identifier == "Func" or
               identifier == "SecureContext" or
               identifier == "Frozen" or
               identifier == "NewObject" or
               identifier == "UnsafeInPrerendering" or
               identifier == "NeedsSubjectPrincipal" or
               identifier == "NeedsCallerType" or
               identifier == "ReturnValueNeedsContainsHack" or
-              identifier == "BinaryName"):
+              identifier == "BinaryName" or
+              identifier == "NonEnumerable"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
             raise WebIDLError("Unknown extended attribute %s on attribute" % identifier,
                               [attr.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
     def resolve(self, parentScope):
@@ -5076,17 +5087,18 @@ class IDLMethod(IDLInterfaceMember, IDLS
               identifier == "UnsafeInPrerendering" or
               identifier == "Pref" or
               identifier == "Deprecated" or
               identifier == "Func" or
               identifier == "SecureContext" or
               identifier == "BinaryName" or
               identifier == "NeedsSubjectPrincipal" or
               identifier == "NeedsCallerType" or
-              identifier == "StaticClassOverride"):
+              identifier == "StaticClassOverride" or
+              identifier == "NonEnumerable"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
             raise WebIDLError("Unknown extended attribute %s on method" % identifier,
                               [attr.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
     def returnsPromise(self):
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -973,16 +973,20 @@ public:
   void PassArgsWithDefaults(JSContext*, const Optional<int32_t>&,
                             TestInterface*, const Dict&, double,
                             const Optional<float>&);
 
   void SetDashed_attribute(int8_t);
   int8_t Dashed_attribute();
   void Dashed_method();
 
+  bool NonEnumerableAttr() const;
+  void SetNonEnumerableAttr(bool);
+  void NonEnumerableMethod();
+
   // Methods and properties imported via "implements"
   bool ImplementedProperty();
   void SetImplementedProperty(bool);
   void ImplementedMethod();
   bool ImplementedParentProperty();
   void SetImplementedParentProperty(bool);
   void ImplementedParentMethod();
   bool IndirectlyImplementedProperty();
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -970,16 +970,24 @@ interface TestInterface {
   attribute any jsonifierShouldSkipThis;
   attribute TestParentInterface jsonifierShouldSkipThis2;
   attribute TestCallbackInterface jsonifierShouldSkipThis3;
   jsonifier;
 
   attribute byte dashed-attribute;
   void dashed-method();
 
+  // [NonEnumerable] tests
+  [NonEnumerable]
+  attribute boolean nonEnumerableAttr;
+  [NonEnumerable]
+  const boolean nonEnumerableConst = true;
+  [NonEnumerable]
+  void nonEnumerableMethod();
+
   // If you add things here, add them to TestExampleGen and TestJSImplGen as well
 };
 
 interface TestParentInterface {
 };
 
 interface TestChildInterface : TestParentInterface {
 };
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -797,16 +797,24 @@ interface TestExampleInterface {
   attribute any jsonifierShouldSkipThis;
   attribute TestParentInterface jsonifierShouldSkipThis2;
   attribute TestCallbackInterface jsonifierShouldSkipThis3;
   jsonifier;
 
   attribute byte dashed-attribute;
   void dashed-method();
 
+  // [NonEnumerable] tests
+  [NonEnumerable]
+  attribute boolean nonEnumerableAttr;
+  [NonEnumerable]
+  const boolean nonEnumerableConst = true;
+  [NonEnumerable]
+  void nonEnumerableMethod();
+
   // If you add things here, add them to TestCodeGen and TestJSImplGen as well
 };
 
 interface TestExampleProxyInterface {
   getter long longIndexedGetter(unsigned long ix);
   setter creator void longIndexedSetter(unsigned long y, long z);
   readonly attribute unsigned long length;
   stringifier DOMString myStringifier();
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -817,16 +817,24 @@ interface TestJSImplInterface {
   attribute any jsonifierShouldSkipThis;
   attribute TestParentInterface jsonifierShouldSkipThis2;
   attribute TestCallbackInterface jsonifierShouldSkipThis3;
   jsonifier;
 
   attribute byte dashed-attribute;
   void dashed-method();
 
+  // [NonEnumerable] tests
+  [NonEnumerable]
+  attribute boolean nonEnumerableAttr;
+  [NonEnumerable]
+  const boolean nonEnumerableConst = true;
+  [NonEnumerable]
+  void nonEnumerableMethod();
+
   // If you add things here, add them to TestCodeGen as well
 };
 
 [NavigatorProperty="TestNavigator", JSImplementation="@mozilla.org/test;1"]
 interface TestNavigator {
 };
 
 [Constructor, NavigatorProperty="TestNavigatorWithConstructor", JSImplementation="@mozilla.org/test;1"]
--- a/dom/file/MultipartBlobImpl.cpp
+++ b/dom/file/MultipartBlobImpl.cpp
@@ -80,17 +80,17 @@ MultipartBlobImpl::GetInternalStream(nsI
     }
 
     aRv = stream->AppendStream(scratchStream);
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
   }
 
-  stream.forget(aStream);
+  CallQueryInterface(stream, aStream);
 }
 
 already_AddRefed<BlobImpl>
 MultipartBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
                                const nsAString& aContentType,
                                ErrorResult& aRv)
 {
   // If we clamped to nothing we create an empty blob
--- a/dom/html/HTMLFormSubmission.cpp
+++ b/dom/html/HTMLFormSubmission.cpp
@@ -389,18 +389,23 @@ FSURLEncoded::URLEncode(const nsAString&
 } // anonymous namespace
 
 // --------------------------------------------------------------------------
 
 FSMultipartFormData::FSMultipartFormData(NotNull<const Encoding*> aEncoding,
                                          nsIContent* aOriginatingElement)
   : EncodingFormSubmission(aEncoding, aOriginatingElement)
 {
-  mPostDataStream =
+  mPostData =
     do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
+
+  nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(mPostData);
+  MOZ_ASSERT(SameCOMIdentity(mPostData, inputStream));
+  mPostDataStream = inputStream;
+
   mTotalLength = 0;
 
   mBoundary.AssignLiteral("---------------------------");
   mBoundary.AppendInt(rand());
   mBoundary.AppendInt(rand());
   mBoundary.AppendInt(rand());
 }
 
@@ -595,17 +600,17 @@ FSMultipartFormData::AddDataChunk(const 
 
   // We should not try to append an invalid stream. That will happen for example
   // if we try to update a file that actually do not exist.
   if (aInputStream) {
     // We need to dump the data up to this point into the POST data stream
     // here, since we're about to add the file input stream
     AddPostDataStream();
 
-    mPostDataStream->AppendStream(aInputStream);
+    mPostData->AppendStream(aInputStream);
     mTotalLength += aInputStreamSize;
   }
 
   // CRLF after file
   mPostDataChunk.AppendLiteral(CRLF);
 }
 
 nsresult
@@ -635,17 +640,17 @@ FSMultipartFormData::AddPostDataStream()
 {
   nsresult rv = NS_OK;
 
   nsCOMPtr<nsIInputStream> postDataChunkStream;
   rv = NS_NewCStringInputStream(getter_AddRefs(postDataChunkStream),
                                 mPostDataChunk);
   NS_ASSERTION(postDataChunkStream, "Could not open a stream for POST!");
   if (postDataChunkStream) {
-    mPostDataStream->AppendStream(postDataChunkStream);
+    mPostData->AppendStream(postDataChunkStream);
     mTotalLength += mPostDataChunk.Length();
   }
 
   mPostDataChunk.Truncate();
 
   return rv;
 }
 
--- a/dom/html/HTMLFormSubmission.h
+++ b/dom/html/HTMLFormSubmission.h
@@ -188,17 +188,23 @@ private:
                     const nsACString& aContentType,
                     nsIInputStream* aInputStream,
                     uint64_t aInputStreamSize);
   /**
    * The post data stream as it is so far.  This is a collection of smaller
    * chunks--string streams and file streams interleaved to make one big POST
    * stream.
    */
-  nsCOMPtr<nsIMultiplexInputStream> mPostDataStream;
+  nsCOMPtr<nsIMultiplexInputStream> mPostData;
+
+  /**
+   * The same stream, but as an nsIInputStream.
+   * Raw pointers because it is just QI of mInputStream.
+   */
+  nsIInputStream* mPostDataStream;
 
   /**
    * The current string chunk.  When a file is hit, the string chunk gets
    * wrapped up into an input stream and put into mPostDataStream so that the
    * file input stream can then be appended and everything is in the right
    * order.  Then the string chunk gets appended to again as we process more
    * name/value pairs.
    */
--- a/dom/interfaces/base/nsIDOMChromeWindow.idl
+++ b/dom/interfaces/base/nsIDOMChromeWindow.idl
@@ -6,76 +6,46 @@
 #include "domstubs.idl"
 
 interface nsIBrowserDOMWindow;
 interface nsIDOMElement;
 interface nsIDOMEvent;
 interface nsIMessageBroadcaster;
 interface mozIDOMWindowProxy;
 
-[scriptable, uuid(78bdcb41-1efa-409f-aaba-70842213f80f)]
+// Scriptable only so Components.interfaces.nsIDOMChromeWindow works.
+[scriptable, builtinclass, uuid(78bdcb41-1efa-409f-aaba-70842213f80f)]
 interface nsIDOMChromeWindow : nsISupports
 {
-  const unsigned short STATE_MAXIMIZED = 1;
-  const unsigned short STATE_MINIMIZED = 2;
-  const unsigned short STATE_NORMAL = 3;
-  const unsigned short STATE_FULLSCREEN = 4;
-
-  readonly attribute unsigned short              windowState;
-
   /**
    * browserDOMWindow provides access to yet another layer of
    * utility functions implemented by chrome script. It will be null
    * for DOMWindows not corresponding to browsers.
    */
-           attribute nsIBrowserDOMWindow browserDOMWindow;
-
-  void                      getAttention();
-
-  void                      getAttentionWithCycleCount(in long aCycleCount);
-
-  void                      setCursor(in DOMString cursor);
+  [noscript]
+  readonly attribute nsIBrowserDOMWindow browserDOMWindow;
 
-  void                      maximize();
-  void                      minimize();
-  void                      restore();
-
-  /**
-   * Notify a default button is loaded on a dialog or a wizard.
-   * defaultButton is the default button.
-   */
-  void notifyDefaultButtonLoaded(in nsIDOMElement defaultButton);
-
+  [noscript]
   readonly attribute nsIMessageBroadcaster messageManager;
 
   /**
    * Returns the message manager identified by the given group name that
    * manages all frame loaders belonging to that group.
    */
+  [noscript]
   nsIMessageBroadcaster getGroupMessageManager(in AString group);
 
   /**
-   * On some operating systems, we must allow the window manager to
-   * handle window dragging. This function tells the window manager to
-   * start dragging the window. This function will fail unless called
-   * while the left mouse button is held down, callers must check this.
-   *
-   * The optional panel argument should be set when moving a panel.
-   *
-   * Returns NS_ERROR_NOT_IMPLEMENTED (and thus throws in JS) if the OS
-   * doesn't support this.
-   */
-  void beginWindowMove(in nsIDOMEvent mouseDownEvent, [optional] in nsIDOMElement panel);
-
-  /**
    * These methods provide a way to specify the opener value for the content in
    * the window before the content itself is created. This is important in order
    * to set the DocGroup of a document, as the opener must be set before the
    * document is created.
    *
    * SetOpenerForInitialContentBrowser is used to set which opener will be used,
    * and TakeOpenerForInitialContentBrowser is used by nsXULElement in order to
    * take the value set earlier, and null out the value in the
    * nsIDOMChromeWindow.
    */
+  [noscript]
   void setOpenerForInitialContentBrowser(in mozIDOMWindowProxy aOpener);
+  [noscript]
   mozIDOMWindowProxy takeOpenerForInitialContentBrowser();
 };
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -325,16 +325,18 @@ LargeAllocationNonWin32=This page would 
 # LOCALIZATION NOTE: Do not translate URL.createObjectURL(MediaStream).
 URLCreateObjectURL_MediaStreamWarning=URL.createObjectURL(MediaStream) is deprecated and will be removed soon.
 # LOCALIZATION NOTE: Do not translate MozAutoGainControl or autoGainControl.
 MozAutoGainControlWarning=mozAutoGainControl is deprecated. Use autoGainControl instead.
 # LOCALIZATION NOTE: Do not translate mozNoiseSuppression or noiseSuppression.
 MozNoiseSuppressionWarning=mozNoiseSuppression is deprecated. Use noiseSuppression instead.
 # LOCALIZATION NOTE: Do not translate xml:base.
 XMLBaseAttributeWarning=Use of xml:base attribute is deprecated and will be removed soon. Please remove any use of it.
+# LOCALIZATION NOTE: Do not translate "content", "Window", and "window.top"
+WindowContentUntrustedWarning=The ‘content’ attribute of Window objects is deprecated.  Please use ‘window.top’ instead.
 # LOCALIZATION NOTE: %S is the tag name of the element that starts the loop
 SVGReferenceLoopWarning=There is an SVG <%S> reference loop in this document, which will prevent the document rendering correctly.
 # LOCALIZATION NOTE: %S is the tag name of the element that starts the chain
 SVGReferenceChainLengthExceededWarning=There is an SVG <%S> reference chain which is too long in this document, which will prevent the document rendering correctly.
 # LOCALIZATION NOTE: Do not translate "<script>".
 ScriptSourceEmpty=‘%S’ attribute of <script> element is empty.
 # LOCALIZATION NOTE: Do not translate "<script>".
 ScriptSourceInvalidUri=‘%S’ attribute of <script> element is not a valid URI: “%S”
--- a/dom/network/TCPSocket.cpp
+++ b/dom/network/TCPSocket.cpp
@@ -205,25 +205,26 @@ TCPSocket::CreateStream()
     mInputStreamScriptable = do_CreateInstance("@mozilla.org/scriptableinputstream;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mInputStreamScriptable->Init(mSocketInputStream);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   mMultiplexStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mMultiplexStream);
 
   mMultiplexStreamCopier = do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsISocketTransportService> sts =
       do_GetService("@mozilla.org/network/socket-transport-service;1");
 
   nsCOMPtr<nsIEventTarget> target = do_QueryInterface(sts);
-  rv = mMultiplexStreamCopier->Init(mMultiplexStream,
+  rv = mMultiplexStreamCopier->Init(stream,
                                     mSocketOutputStream,
                                     target,
                                     true, /* source buffered */
                                     false, /* sink buffered */
                                     BUFFER_SIZE,
                                     false, /* close source */
                                     false); /* close sink */
   NS_ENSURE_SUCCESS(rv, rv);
@@ -591,17 +592,18 @@ TCPSocket::Ssl()
 uint64_t
 TCPSocket::BufferedAmount()
 {
   if (mSocketBridgeChild) {
     return mBufferedAmount;
   }
   if (mMultiplexStream) {
     uint64_t available = 0;
-    mMultiplexStream->Available(&available);
+    nsCOMPtr<nsIInputStream> stream(do_QueryInterface(mMultiplexStream));
+    stream->Available(&available);
     return available;
   }
   return 0;
 }
 
 void
 TCPSocket::Suspend()
 {
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -227,45 +227,39 @@ public:
   }
   bool construct(JSContext* cx, JS::Handle<JSObject*> proxy,
                  const JS::CallArgs& args) const override;
 
   bool finalizeInBackground(const JS::Value& priv) const override {
     return false;
   }
   void finalize(JSFreeOp* fop, JSObject* proxy) const override;
+
+  size_t objectMoved(JSObject* obj, JSObject* old) const override;
 };
 
 const char NPObjWrapperProxyHandler::family = 0;
 const NPObjWrapperProxyHandler NPObjWrapperProxyHandler::singleton;
 
 static bool
 NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                      bool* resolved, JS::MutableHandle<JSObject*> method);
 
-static void
-NPObjWrapper_ObjectMoved(JSObject *obj, const JSObject *old);
-
 static bool
 NPObjWrapper_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp);
 
 static bool
 CreateNPObjectMember(NPP npp, JSContext *cx,
                      JS::Handle<JSObject*> obj, NPObject* npobj,
                      JS::Handle<jsid> id,  NPVariant* getPropertyResult,
                      JS::MutableHandle<JS::Value> vp);
 
-static const js::ClassExtension sNPObjWrapperProxyClassExtension = PROXY_MAKE_EXT(
-    NPObjWrapper_ObjectMoved
-);
-
-const js::Class sNPObjWrapperProxyClass = PROXY_CLASS_WITH_EXT(
+const js::Class sNPObjWrapperProxyClass = PROXY_CLASS_DEF(
     NPRUNTIME_JSCLASS_NAME,
-    JSCLASS_HAS_RESERVED_SLOTS(1),
-    &sNPObjWrapperProxyClassExtension);
+    JSCLASS_HAS_RESERVED_SLOTS(1));
 
 typedef struct NPObjectMemberPrivate {
     JS::Heap<JSObject *> npobjWrapper;
     JS::Heap<JS::Value> fieldValue;
     JS::Heap<jsid> methodName;
     NPP   npp;
 } NPObjectMemberPrivate;
 
@@ -1782,39 +1776,40 @@ NPObjWrapperProxyHandler::finalize(JSFre
     }
   }
 
   if (!sDelayedReleases)
     sDelayedReleases = new nsTArray<NPObject*>;
   sDelayedReleases->AppendElement(npobj);
 }
 
-static void
-NPObjWrapper_ObjectMoved(JSObject *obj, const JSObject *old)
+size_t
+NPObjWrapperProxyHandler::objectMoved(JSObject *obj, JSObject *old) const
 {
   // The wrapper JSObject has been moved, so we need to update the entry in the
   // sNPObjWrappers hash table, if present.
 
   if (!sNPObjWrappers) {
-    return;
+    return 0;
   }
 
   NPObject *npobj = (NPObject *)js::GetProxyPrivate(obj).toPrivate();
   if (!npobj) {
-    return;
+    return 0;
   }
 
   // Calling PLDHashTable::Search() will not result in GC.
   JS::AutoSuppressGCAnalysis nogc;
 
   auto entry =
     static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
   MOZ_ASSERT(entry && entry->mJSObj);
   MOZ_ASSERT(entry->mJSObj == old);
   entry->mJSObj = obj;
+  return 0;
 }
 
 bool
 NPObjWrapperProxyHandler::call(JSContext* cx, JS::Handle<JSObject*> proxy,
                                const JS::CallArgs& args) const
 {
   return CallNPMethodInternal(cx, proxy, args.length(), args.array(),
                               args.rval().address(), false);
--- a/dom/presentation/PresentationTCPSessionTransport.cpp
+++ b/dom/presentation/PresentationTCPSessionTransport.cpp
@@ -235,30 +235,31 @@ PresentationTCPSessionTransport::CreateS
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mMultiplexStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
+  nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mMultiplexStream);
 
   mMultiplexStreamCopier = do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<nsISocketTransportService> sts =
     do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
   if (NS_WARN_IF(!sts)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsCOMPtr<nsIEventTarget> target = do_QueryInterface(sts);
-  rv = mMultiplexStreamCopier->Init(mMultiplexStream,
+  rv = mMultiplexStreamCopier->Init(stream,
                                     mSocketOutputStream,
                                     target,
                                     true, /* source buffered */
                                     false, /* sink buffered */
                                     BUFFER_SIZE,
                                     false, /* close source */
                                     false); /* close sink */
   if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/u2f/U2F.cpp
+++ b/dom/u2f/U2F.cpp
@@ -218,17 +218,17 @@ U2F::Register(const nsAString& aAppId,
 
   RefPtr<U2FManager> mgr = U2FManager::GetOrCreate();
   MOZ_ASSERT(mgr);
   if (!mgr || mRegisterCallback.isSome()) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  MOZ_ASSERT(!mPromiseHolder.Exists());
+  Cancel();
   MOZ_ASSERT(mRegisterCallback.isNothing());
   mRegisterCallback = Some(nsMainThreadPtrHandle<U2FRegisterCallback>(
                         new nsMainThreadPtrHolder<U2FRegisterCallback>(
                             "U2F::Register::callback", &aCallback)));
 
   uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
 
   // Evaluate the AppID
@@ -315,17 +315,17 @@ U2F::Sign(const nsAString& aAppId,
 
   RefPtr<U2FManager> mgr = U2FManager::GetOrCreate();
   MOZ_ASSERT(mgr);
   if (!mgr || mSignCallback.isSome()) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  MOZ_ASSERT(!mPromiseHolder.Exists());
+  Cancel();
   MOZ_ASSERT(mSignCallback.isNothing());
   mSignCallback = Some(nsMainThreadPtrHandle<U2FSignCallback>(
                     new nsMainThreadPtrHolder<U2FSignCallback>(
                         "U2F::Sign::callback", &aCallback)));
 
   uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
 
   // Evaluate the AppID
@@ -380,10 +380,37 @@ U2F::Sign(const nsAString& aAppId,
               response.mErrorCode.Construct(static_cast<uint32_t>(aErrorCode));
 
               ExecuteCallback(response, localCb);
               localReqHolder.Complete();
           })
   ->Track(mPromiseHolder);
 }
 
+void
+U2F::Cancel()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  const ErrorCode errorCode = ErrorCode::OTHER_ERROR;
+
+  if (mRegisterCallback.isSome()) {
+    RegisterResponse response;
+    response.mErrorCode.Construct(static_cast<uint32_t>(errorCode));
+    ExecuteCallback(response, mRegisterCallback);
+  }
+
+  if (mSignCallback.isSome()) {
+    SignResponse response;
+    response.mErrorCode.Construct(static_cast<uint32_t>(errorCode));
+    ExecuteCallback(response, mSignCallback);
+  }
+
+  RefPtr<U2FManager> mgr = U2FManager::Get();
+  if (mgr) {
+    mgr->Cancel(NS_ERROR_DOM_OPERATION_ERR);
+  }
+
+  mPromiseHolder.DisconnectIfExists();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/u2f/U2F.h
+++ b/dom/u2f/U2F.h
@@ -65,16 +65,19 @@ public:
   Sign(const nsAString& aAppId,
        const nsAString& aChallenge,
        const Sequence<RegisteredKey>& aRegisteredKeys,
        U2FSignCallback& aCallback,
        const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
        ErrorResult& aRv);
 
 private:
+  void
+  Cancel();
+
   nsString mOrigin;
   nsCOMPtr<nsPIDOMWindowInner> mParent;
   nsCOMPtr<nsISerialEventTarget> mEventTarget;
   Maybe<nsMainThreadPtrHandle<U2FRegisterCallback>> mRegisterCallback;
   Maybe<nsMainThreadPtrHandle<U2FSignCallback>> mSignCallback;
   MozPromiseRequestHolder<U2FPromise> mPromiseHolder;
 
   ~U2F();
--- a/dom/u2f/tests/frame_register_sign.html
+++ b/dom/u2f/tests/frame_register_sign.html
@@ -99,27 +99,30 @@ async function doTests() {
   .then(function(params){
     state.appParam = params.appParam;
     state.challengeParam = params.challengeParam;
     return state.attestationCert.getPublicKey();
   }).then(function(attestationPublicKey) {
     var signedData = assembleRegistrationSignedData(state.appParam, state.challengeParam, state.keyHandleBytes, state.publicKeyBytes);
     return verifySignature(attestationPublicKey, signedData, state.attestationSig);
   }).then(function(verified) {
-    local_ok(verified, "Attestation Certificate signature verified")
+    local_ok(verified, "Attestation Certificate signature verified");
      // Import the public key of the U2F token into WebCrypto
-    return importPublicKey(state.publicKeyBytes)
+    return importPublicKey(state.publicKeyBytes);
   }).then(function(key) {
     state.publicKey = key;
-    local_ok(true, "Imported public key")
+    local_ok(true, "Imported public key");
 
     // Ensure the attestation certificate is properly self-signed
-    return state.attestationCert.verify()
+    return state.attestationCert.verify();
   }).then(function(verified) {
-    local_ok(verified, "Register attestation signature verified")
+    if (!verified) {
+      local_ok(verified, "Cert problem: " + bytesToBase64UrlSafe(state.attestation));
+    }
+    local_ok(verified, "Register attestation signature verified");
   });
 
   state.regKey = {
     version: state.version,
     keyHandle: state.keyHandle,
   };
 
   // Test that we don't re-register if we provide regKey as an
--- a/dom/webauthn/U2FHIDTokenManager.cpp
+++ b/dom/webauthn/U2FHIDTokenManager.cpp
@@ -13,34 +13,34 @@ namespace dom {
 static StaticMutex gInstanceMutex;
 static U2FHIDTokenManager* gInstance;
 static nsIThread* gPBackgroundThread;
 
 static void
 u2f_register_callback(uint64_t aTransactionId, rust_u2f_result* aResult)
 {
   StaticMutexAutoLock lock(gInstanceMutex);
-  if (NS_WARN_IF(!gInstance || !gPBackgroundThread)) {
+  if (!gInstance || NS_WARN_IF(!gPBackgroundThread)) {
     return;
   }
 
   UniquePtr<U2FResult> rv = MakeUnique<U2FResult>(aTransactionId, aResult);
   nsCOMPtr<nsIRunnable> r(NewNonOwningRunnableMethod<UniquePtr<U2FResult>&&>(
       "U2FHIDTokenManager::HandleRegisterResult", gInstance,
       &U2FHIDTokenManager::HandleRegisterResult, Move(rv)));
 
   MOZ_ALWAYS_SUCCEEDS(gPBackgroundThread->Dispatch(r.forget(),
                                                    NS_DISPATCH_NORMAL));
 }
 
 static void
 u2f_sign_callback(uint64_t aTransactionId, rust_u2f_result* aResult)
 {
   StaticMutexAutoLock lock(gInstanceMutex);
-  if (NS_WARN_IF(!gInstance || !gPBackgroundThread)) {
+  if (!gInstance || NS_WARN_IF(!gPBackgroundThread)) {
     return;
   }
 
   UniquePtr<U2FResult> rv = MakeUnique<U2FResult>(aTransactionId, aResult);
   nsCOMPtr<nsIRunnable> r(NewNonOwningRunnableMethod<UniquePtr<U2FResult>&&>(
       "U2FHIDTokenManager::HandleSignResult", gInstance,
       &U2FHIDTokenManager::HandleSignResult, Move(rv)));
 
@@ -58,25 +58,31 @@ U2FHIDTokenManager::U2FHIDTokenManager()
   mU2FManager = rust_u2f_mgr_new();
   gPBackgroundThread = NS_GetCurrentThread();
   MOZ_ASSERT(gPBackgroundThread, "This should never be null!");
   gInstance = this;
 }
 
 U2FHIDTokenManager::~U2FHIDTokenManager()
 {
-  StaticMutexAutoLock lock(gInstanceMutex);
-  MOZ_ASSERT(NS_GetCurrentThread() == gPBackgroundThread);
+  {
+    StaticMutexAutoLock lock(gInstanceMutex);
+    MOZ_ASSERT(NS_GetCurrentThread() == gPBackgroundThread);
+
+    mRegisterPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
+    mSignPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
 
-  mRegisterPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
-  mSignPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
+    gInstance = nullptr;
+  }
 
+  // Release gInstanceMutex before we call U2FManager::drop(). It will wait
+  // for the work queue thread to join, and that requires the
+  // u2f_{register,sign}_callback to lock and return.
   rust_u2f_mgr_free(mU2FManager);
   mU2FManager = nullptr;
-  gInstance = nullptr;
 }
 
 // A U2F Register operation causes a new key pair to be generated by the token.
 // The token then returns the public key of the key pair, and a handle to the
 // private key, which is a fancy way of saying "key wrapped private key", as
 // well as the generated attestation certificate and a signature using that
 // certificate's private key.
 //
--- a/dom/webauthn/u2f-hid-rs/Cargo.toml
+++ b/dom/webauthn/u2f-hid-rs/Cargo.toml
@@ -1,25 +1,25 @@
 [package]
 name = "u2fhid"
 version = "0.1.0"
 authors = ["Kyle Machulis <kyle@nonpolynomial.com>", "J.C. Jones <jc@mozilla.com>", "Tim Taubert <ttaubert@mozilla.com>"]
-build = "build.rs"
 
 [target.'cfg(target_os = "linux")'.dependencies]
 libudev = "^0.2"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation-sys = "0.3.1"
 
 [target.'cfg(target_os = "windows")'.dependencies]
 winapi = "0.2.8"
 
 [dependencies]
 rand = "0.3"
 log = "0.3"
 env_logger = "0.4.1"
 libc = "^0.2"
 boxfnonce = "0.0.3"
+runloop = "0.1.0"
 
 [dev-dependencies]
 rust-crypto = "^0.2"
 base64 = "^0.4"
deleted file mode 100644
--- a/dom/webauthn/u2f-hid-rs/build.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-/* 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/. */
-
-fn main() {
-    #[cfg(any(target_os = "macos"))]
-    println!("cargo:rustc-link-lib=framework=IOKit");
-}
--- a/dom/webauthn/u2f-hid-rs/src/lib.rs
+++ b/dom/webauthn/u2f-hid-rs/src/lib.rs
@@ -27,19 +27,19 @@ pub mod platform;
 #[path = "stub/mod.rs"]
 pub mod platform;
 
 #[macro_use]
 extern crate log;
 extern crate rand;
 extern crate libc;
 extern crate boxfnonce;
+extern crate runloop;
 
 mod consts;
-mod runloop;
 mod u2ftypes;
 mod u2fprotocol;
 
 mod manager;
 pub use manager::U2FManager;
 
 mod capi;
 pub use capi::*;
--- a/dom/webauthn/u2f-hid-rs/src/linux/mod.rs
+++ b/dom/webauthn/u2f-hid-rs/src/linux/mod.rs
@@ -35,17 +35,17 @@ impl PlatformManager {
         application: Vec<u8>,
         callback: OnceCallback<Vec<u8>>,
     ) {
         // Abort any prior register/sign calls.
         self.cancel();
 
         let cbc = callback.clone();
 
-        let thread = RunLoop::new(
+        let thread = RunLoop::new_with_timeout(
             move |alive| {
                 let mut devices = DeviceMap::new();
                 let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
 
                 while alive() && monitor.alive() {
                     // Add/remove devices.
                     for event in monitor.events() {
                         devices.process_event(event);
@@ -82,17 +82,17 @@ impl PlatformManager {
         key_handles: Vec<Vec<u8>>,
         callback: OnceCallback<(Vec<u8>, Vec<u8>)>,
     ) {
         // Abort any prior register/sign calls.
         self.cancel();
 
         let cbc = callback.clone();
 
-        let thread = RunLoop::new(
+        let thread = RunLoop::new_with_timeout(
             move |alive| {
                 let mut devices = DeviceMap::new();
                 let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
 
                 while alive() && monitor.alive() {
                     // Add/remove devices.
                     for event in monitor.events() {
                         devices.process_event(event);
--- a/dom/webauthn/u2f-hid-rs/src/linux/monitor.rs
+++ b/dom/webauthn/u2f-hid-rs/src/linux/monitor.rs
@@ -56,58 +56,55 @@ pub struct Monitor {
     // Handle to the thread loop.
     thread: RunLoop,
 }
 
 impl Monitor {
     pub fn new() -> io::Result<Self> {
         let (tx, rx) = channel();
 
-        let thread = RunLoop::new(
-            move |alive| -> io::Result<()> {
-                let ctx = libudev::Context::new()?;
-                let mut enumerator = libudev::Enumerator::new(&ctx)?;
-                enumerator.match_subsystem(UDEV_SUBSYSTEM)?;
+        let thread = RunLoop::new(move |alive| -> io::Result<()> {
+            let ctx = libudev::Context::new()?;
+            let mut enumerator = libudev::Enumerator::new(&ctx)?;
+            enumerator.match_subsystem(UDEV_SUBSYSTEM)?;
 
-                // Iterate all existing devices.
-                for dev in enumerator.scan_devices()? {
-                    if let Some(path) = dev.devnode().map(|p| p.to_owned().into_os_string()) {
-                        tx.send(Event::Add(path)).map_err(to_io_err)?;
-                    }
+            // Iterate all existing devices.
+            for dev in enumerator.scan_devices()? {
+                if let Some(path) = dev.devnode().map(|p| p.to_owned().into_os_string()) {
+                    tx.send(Event::Add(path)).map_err(to_io_err)?;
                 }
+            }
 
-                let mut monitor = libudev::Monitor::new(&ctx)?;
-                monitor.match_subsystem(UDEV_SUBSYSTEM)?;
+            let mut monitor = libudev::Monitor::new(&ctx)?;
+            monitor.match_subsystem(UDEV_SUBSYSTEM)?;
 
-                // Start listening for new devices.
-                let mut socket = monitor.listen()?;
-                let mut fds = vec![
-                    ::libc::pollfd {
-                        fd: socket.as_raw_fd(),
-                        events: POLLIN,
-                        revents: 0,
-                    },
-                ];
+            // Start listening for new devices.
+            let mut socket = monitor.listen()?;
+            let mut fds = vec![
+                ::libc::pollfd {
+                    fd: socket.as_raw_fd(),
+                    events: POLLIN,
+                    revents: 0,
+                },
+            ];
 
-                // Loop until we're stopped by the controlling thread, or fail.
-                while alive() {
-                    // Wait for new events, break on failure.
-                    poll(&mut fds)?;
+            // Loop until we're stopped by the controlling thread, or fail.
+            while alive() {
+                // Wait for new events, break on failure.
+                poll(&mut fds)?;
 
-                    // Send the event over.
-                    let udev_event = socket.receive_event();
-                    if let Some(event) = udev_event.and_then(Event::from_udev) {
-                        tx.send(event).map_err(to_io_err)?;
-                    }
+                // Send the event over.
+                let udev_event = socket.receive_event();
+                if let Some(event) = udev_event.and_then(Event::from_udev) {
+                    tx.send(event).map_err(to_io_err)?;
                 }
+            }
 
-                Ok(())
-            },
-            0, /* no timeout */
-        )?;
+            Ok(())
+        })?;
 
         Ok(Self { rx, thread })
     }
 
     pub fn events<'a>(&'a self) -> TryIter<'a, Event> {
         self.rx.try_iter()
     }
 
--- a/dom/webauthn/u2f-hid-rs/src/macos/iokit.rs
+++ b/dom/webauthn/u2f-hid-rs/src/macos/iokit.rs
@@ -45,16 +45,17 @@ pub struct __IOHIDManager {
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
 pub struct IOHIDDeviceRef(*const c_void);
 
 unsafe impl Send for IOHIDDeviceRef {}
 unsafe impl Sync for IOHIDDeviceRef {}
 
+#[link(name = "IOKit", kind = "framework")]
 extern "C" {
     // IOHIDManager
     pub fn IOHIDManagerCreate(
         allocator: CFAllocatorRef,
         options: IOHIDManagerOptions,
     ) -> IOHIDManagerRef;
     pub fn IOHIDManagerSetDeviceMatching(manager: IOHIDManagerRef, matching: CFDictionaryRef);
     pub fn IOHIDManagerRegisterDeviceMatchingCallback(
--- a/dom/webauthn/u2f-hid-rs/src/macos/mod.rs
+++ b/dom/webauthn/u2f-hid-rs/src/macos/mod.rs
@@ -40,17 +40,17 @@ impl PlatformManager {
         application: Vec<u8>,
         callback: OnceCallback<Vec<u8>>,
     ) {
         // Abort any prior register/sign calls.
         self.cancel();
 
         let cbc = callback.clone();
 
-        let thread = RunLoop::new(
+        let thread = RunLoop::new_with_timeout(
             move |alive| {
                 let mut devices = DeviceMap::new();
                 let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
 
                 'top: while alive() && monitor.alive() {
                     for event in monitor.events() {
                         devices.process_event(event);
                     }
@@ -93,17 +93,17 @@ impl PlatformManager {
         key_handles: Vec<Vec<u8>>,
         callback: OnceCallback<(Vec<u8>, Vec<u8>)>,
     ) {
         // Abort any prior register/sign calls.
         self.cancel();
 
         let cbc = callback.clone();
 
-        let thread = RunLoop::new(
+        let thread = RunLoop::new_with_timeout(
             move |alive| {
                 let mut devices = DeviceMap::new();
                 let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
 
                 'top: while alive() && monitor.alive() {
                     for event in monitor.events() {
                         devices.process_event(event);
                     }
--- a/dom/webauthn/u2f-hid-rs/src/macos/monitor.rs
+++ b/dom/webauthn/u2f-hid-rs/src/macos/monitor.rs
@@ -26,63 +26,60 @@ pub struct Monitor {
     // Handle to the thread loop.
     thread: RunLoop,
 }
 
 impl Monitor {
     pub fn new() -> io::Result<Self> {
         let (tx, rx) = channel();
 
-        let thread = RunLoop::new(
-            move |alive| -> io::Result<()> {
-                let tx_box = Box::new(tx);
-                let tx_ptr = Box::into_raw(tx_box) as *mut libc::c_void;
+        let thread = RunLoop::new(move |alive| -> io::Result<()> {
+            let tx_box = Box::new(tx);
+            let tx_ptr = Box::into_raw(tx_box) as *mut libc::c_void;
+
+            // This will keep `tx` alive only for the scope.
+            let _tx = unsafe { Box::from_raw(tx_ptr as *mut Sender<Event>) };
 
-                // This will keep `tx` alive only for the scope.
-                let _tx = unsafe { Box::from_raw(tx_ptr as *mut Sender<Event>) };
+            // Create and initialize a scoped HID manager.
+            let manager = IOHIDManager::new()?;
 
-                // Create and initialize a scoped HID manager.
-                let manager = IOHIDManager::new()?;
-
-                // Match only U2F devices.
-                let dict = IOHIDDeviceMatcher::new();
-                unsafe { IOHIDManagerSetDeviceMatching(manager.get(), dict.get()) };
+            // Match only U2F devices.
+            let dict = IOHIDDeviceMatcher::new();
+            unsafe { IOHIDManagerSetDeviceMatching(manager.get(), dict.get()) };
 
-                // Register callbacks.
-                unsafe {
-                    IOHIDManagerRegisterDeviceMatchingCallback(
-                        manager.get(),
-                        Monitor::device_add_cb,
-                        tx_ptr,
-                    );
-                    IOHIDManagerRegisterDeviceRemovalCallback(
-                        manager.get(),
-                        Monitor::device_remove_cb,
-                        tx_ptr,
-                    );
-                }
+            // Register callbacks.
+            unsafe {
+                IOHIDManagerRegisterDeviceMatchingCallback(
+                    manager.get(),
+                    Monitor::device_add_cb,
+                    tx_ptr,
+                );
+                IOHIDManagerRegisterDeviceRemovalCallback(
+                    manager.get(),
+                    Monitor::device_remove_cb,
+                    tx_ptr,
+                );
+            }
 
-                // Run the Event Loop. CFRunLoopRunInMode() will dispatch HID
-                // input reports into the various callbacks
-                while alive() {
-                    trace!("OSX Runloop running, handle={:?}", thread::current());
+            // Run the Event Loop. CFRunLoopRunInMode() will dispatch HID
+            // input reports into the various callbacks
+            while alive() {
+                trace!("OSX Runloop running, handle={:?}", thread::current());
 
-                    if unsafe { CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, 0) } ==
-                        kCFRunLoopRunStopped
-                    {
-                        debug!("OSX Runloop device stopped.");
-                        break;
-                    }
+                if unsafe { CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, 0) } ==
+                    kCFRunLoopRunStopped
+                {
+                    debug!("OSX Runloop device stopped.");
+                    break;
                 }
-                debug!("OSX Runloop completed, handle={:?}", thread::current());
+            }
+            debug!("OSX Runloop completed, handle={:?}", thread::current());
 
-                Ok(())
-            },
-            0, /* no timeout */
-        )?;
+            Ok(())
+        })?;
 
         Ok(Self { rx, thread })
     }
 
     pub fn events(&self) -> TryIter<Event> {
         self.rx.try_iter()
     }
 
--- a/dom/webauthn/u2f-hid-rs/src/manager.rs
+++ b/dom/webauthn/u2f-hid-rs/src/manager.rs
@@ -33,58 +33,55 @@ pub struct U2FManager {
     tx: Sender<QueueAction>,
 }
 
 impl U2FManager {
     pub fn new() -> io::Result<Self> {
         let (tx, rx) = channel();
 
         // Start a new work queue thread.
-        let queue = try!(RunLoop::new(
-            move |alive| {
-                let mut pm = PlatformManager::new();
+        let queue = RunLoop::new(move |alive| {
+            let mut pm = PlatformManager::new();
 
-                while alive() {
-                    match rx.recv_timeout(Duration::from_millis(50)) {
-                        Ok(QueueAction::Register {
-                               timeout,
-                               challenge,
-                               application,
-                               callback,
-                           }) => {
-                            // This must not block, otherwise we can't cancel.
-                            pm.register(timeout, challenge, application, callback);
-                        }
-                        Ok(QueueAction::Sign {
-                               timeout,
-                               challenge,
-                               application,
-                               key_handles,
-                               callback,
-                           }) => {
-                            // This must not block, otherwise we can't cancel.
-                            pm.sign(timeout, challenge, application, key_handles, callback);
-                        }
-                        Ok(QueueAction::Cancel) => {
-                            // Cancelling must block so that we don't start a new
-                            // polling thread before the old one has shut down.
-                            pm.cancel();
-                        }
-                        Err(RecvTimeoutError::Disconnected) => {
-                            break;
-                        }
-                        _ => { /* continue */ }
+            while alive() {
+                match rx.recv_timeout(Duration::from_millis(50)) {
+                    Ok(QueueAction::Register {
+                           timeout,
+                           challenge,
+                           application,
+                           callback,
+                       }) => {
+                        // This must not block, otherwise we can't cancel.
+                        pm.register(timeout, challenge, application, callback);
                     }
+                    Ok(QueueAction::Sign {
+                           timeout,
+                           challenge,
+                           application,
+                           key_handles,
+                           callback,
+                       }) => {
+                        // This must not block, otherwise we can't cancel.
+                        pm.sign(timeout, challenge, application, key_handles, callback);
+                    }
+                    Ok(QueueAction::Cancel) => {
+                        // Cancelling must block so that we don't start a new
+                        // polling thread before the old one has shut down.
+                        pm.cancel();
+                    }
+                    Err(RecvTimeoutError::Disconnected) => {
+                        break;
+                    }
+                    _ => { /* continue */ }
                 }
+            }
 
-                // Cancel any ongoing activity.
-                pm.cancel();
-            },
-            0, /* no timeout */
-        ));
+            // Cancel any ongoing activity.
+            pm.cancel();
+        })?;
 
         Ok(Self {
             queue: queue,
             tx: tx,
         })
     }
 
     pub fn register<F>(
--- a/dom/webauthn/u2f-hid-rs/src/windows/mod.rs
+++ b/dom/webauthn/u2f-hid-rs/src/windows/mod.rs
@@ -35,17 +35,17 @@ impl PlatformManager {
         application: Vec<u8>,
         callback: OnceCallback<Vec<u8>>,
     ) {
         // Abort any prior register/sign calls.
         self.cancel();
 
         let cbc = callback.clone();
 
-        let thread = RunLoop::new(
+        let thread = RunLoop::new_with_timeout(
             move |alive| {
                 let mut devices = DeviceMap::new();
                 let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
 
                 while alive() && monitor.alive() {
                     // Add/remove devices.
                     for event in monitor.events() {
                         devices.process_event(event);
@@ -81,17 +81,17 @@ impl PlatformManager {
         key_handles: Vec<Vec<u8>>,
         callback: OnceCallback<(Vec<u8>, Vec<u8>)>,
     ) {
         // Abort any prior register/sign calls.
         self.cancel();
 
         let cbc = callback.clone();
 
-        let thread = RunLoop::new(
+        let thread = RunLoop::new_with_timeout(
             move |alive| {
                 let mut devices = DeviceMap::new();
                 let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
 
                 while alive() && monitor.alive() {
                     // Add/remove devices.
                     for event in monitor.events() {
                         devices.process_event(event);
--- a/dom/webauthn/u2f-hid-rs/src/windows/monitor.rs
+++ b/dom/webauthn/u2f-hid-rs/src/windows/monitor.rs
@@ -32,45 +32,42 @@ pub struct Monitor {
     // Handle to the thread loop.
     thread: RunLoop,
 }
 
 impl Monitor {
     pub fn new() -> io::Result<Self> {
         let (tx, rx) = channel();
 
-        let thread = RunLoop::new(
-            move |alive| -> io::Result<()> {
-                let mut stored = HashSet::new();
-
-                while alive() {
-                    let device_info_set = DeviceInfoSet::new()?;
-                    let devices = HashSet::from_iter(device_info_set.devices());
+        let thread = RunLoop::new(move |alive| -> io::Result<()> {
+            let mut stored = HashSet::new();
 
-                    // Remove devices that are gone.
-                    for path in stored.difference(&devices) {
-                        tx.send(Event::Remove(path.clone())).map_err(to_io_err)?;
-                    }
+            while alive() {
+                let device_info_set = DeviceInfoSet::new()?;
+                let devices = HashSet::from_iter(device_info_set.devices());
 
-                    // Add devices that were plugged in.
-                    for path in devices.difference(&stored) {
-                        tx.send(Event::Add(path.clone())).map_err(to_io_err)?;
-                    }
-
-                    // Remember the new set.
-                    stored = devices;
-
-                    // Wait a little before looking for devices again.
-                    thread::sleep(Duration::from_millis(100));
+                // Remove devices that are gone.
+                for path in stored.difference(&devices) {
+                    tx.send(Event::Remove(path.clone())).map_err(to_io_err)?;
                 }
 
-                Ok(())
-            },
-            0, /* no timeout */
-        )?;
+                // Add devices that were plugged in.
+                for path in devices.difference(&stored) {
+                    tx.send(Event::Add(path.clone())).map_err(to_io_err)?;
+                }
+
+                // Remember the new set.
+                stored = devices;
+
+                // Wait a little before looking for devices again.
+                thread::sleep(Duration::from_millis(100));
+            }
+
+            Ok(())
+        })?;
 
         Ok(Self {
             rx: rx,
             thread: thread,
         })
     }
 
     pub fn events<'a>(&'a self) -> TryIter<'a, Event> {
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -335,17 +335,18 @@ partial interface Window {
                                                                    optional DOMString name = "",
                                                                    optional DOMString options = "",
                                                                    any... extraArguments);
 
   [
 #ifdef NIGHTLY_BUILD
    ChromeOnly,
 #endif
-   Replaceable, Throws, NeedsCallerType] readonly attribute object? content;
+   NonEnumerable, Replaceable, Throws, NeedsCallerType]
+  readonly attribute object? content;
 
   [Throws, ChromeOnly] any getInterface(IID iid);
 
   /**
    * Same as nsIDOMWindow.windowRoot, useful for event listener targeting.
    */
   [ChromeOnly, Throws]
   readonly attribute WindowRoot? windowRoot;
@@ -369,16 +370,17 @@ partial interface Window {
 partial interface Window {
   [Replaceable, Throws, UseCounter]
   readonly attribute (External or WindowProxy) sidebar;
 };
 #endif
 
 [Func="IsChromeOrXBL"]
 interface ChromeWindow {
+  // The STATE_* constants need to match the corresponding enum in nsGlobalWindow.cpp.
   [Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
   const unsigned short STATE_MAXIMIZED = 1;
   [Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
   const unsigned short STATE_MINIMIZED = 2;
   [Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
   const unsigned short STATE_NORMAL = 3;
   [Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
   const unsigned short STATE_FULLSCREEN = 4;
--- a/dom/workers/FileReaderSync.cpp
+++ b/dom/workers/FileReaderSync.cpp
@@ -227,17 +227,19 @@ FileReaderSync::ReadAsText(Blob& aBlob,
     aRv = multiplexStream->AppendStream(syncStream);
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
   }
 
   nsAutoCString charset;
   encoding->Name(charset);
-  aRv = ConvertStream(multiplexStream, charset.get(), aResult);
+
+  nsCOMPtr<nsIInputStream> multiplex(do_QueryInterface(multiplexStream));
+  aRv = ConvertStream(multiplex, charset.get(), aResult);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 }
 
 void
 FileReaderSync::ReadAsDataURL(Blob& aBlob, nsAString& aResult,
                               ErrorResult& aRv)
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
@@ -1,9 +1,9 @@
-52365
+52366
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -50429,16 +50429,17 @@ van/SM
 vanadium/M
 vandal/SM
 vandalism/M
 vandalize/DSG
 vane/MS
 vanguard/MS
 vanilla/SM
 vanish/JDSG
+vanishingly
 vanity/SM
 vanned
 vanning
 vanquish/ZGDRS
 vanquisher/M
 vantage/SM
 vape/GDS
 vapid/YP
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -48,16 +48,17 @@
 #include "gfxCrashReporterUtils.h"
 #include "gfxFailure.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "GLBlitHelper.h"
 #include "GLContextEGL.h"
 #include "GLContextProvider.h"
 #include "GLLibraryEGL.h"
+#include "LayersLogging.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "nsDebug.h"
 #include "nsIWidget.h"
 #include "nsThreadUtils.h"
@@ -164,44 +165,44 @@ private:
 
 already_AddRefed<GLContext>
 GLContextEGLFactory::Create(EGLNativeWindowType aWindow,
                             bool aWebRender)
 {
     MOZ_ASSERT(aWindow);
     nsCString discardFailureId;
     if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
-        MOZ_CRASH("GFX: Failed to load EGL library 3!\n");
+        gfxCriticalNote << "Failed to load EGL library 3!";
         return nullptr;
     }
 
     bool doubleBuffered = true;
 
     EGLConfig config;
     if (!CreateConfig(&config, aWebRender)) {
-        MOZ_CRASH("GFX: Failed to create EGLConfig!\n");
+        gfxCriticalNote << "Failed to create EGLConfig!";
         return nullptr;
     }
 
     EGLSurface surface = mozilla::gl::CreateSurfaceFromNativeWindow(aWindow, config);
 
     if (!surface) {
-        MOZ_CRASH("GFX: Failed to create EGLSurface!\n");
+        gfxCriticalNote << "Failed to create EGLSurface!";
         return nullptr;
     }
 
     CreateContextFlags flags = CreateContextFlags::NONE;
     if (aWebRender) {
         flags |= CreateContextFlags::PREFER_ES3;
     }
     SurfaceCaps caps = SurfaceCaps::Any();
     RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(flags, caps, false, config,
                                                             surface, &discardFailureId);
     if (!gl) {
-        MOZ_CRASH("GFX: Failed to create EGLContext!\n");
+        gfxCriticalNote << "Failed to create EGLContext!";
         mozilla::gl::DestroySurface(surface);
         return nullptr;
     }
 
     gl->MakeCurrent();
     gl->SetIsDoubleBuffered(doubleBuffered);
 
     return gl.forget();
@@ -712,17 +713,17 @@ CreateConfig(EGLConfig* aConfig, bool aE
     }
 }
 
 already_AddRefed<GLContext>
 GLContextProviderEGL::CreateWrappingExisting(void* aContext, void* aSurface)
 {
     nsCString discardFailureId;
     if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
-        MOZ_CRASH("GFX: Failed to load EGL library 2!\n");
+        MOZ_CRASH("GFX: Failed to load EGL library 2!");
         return nullptr;
     }
 
     if (!aContext || !aSurface)
         return nullptr;
 
     SurfaceCaps caps = SurfaceCaps::Any();
     EGLConfig config = EGL_NO_CONFIG;
@@ -753,52 +754,52 @@ GLContextProviderEGL::CreateForWindow(ns
                                        aWebRender);
 }
 
 #if defined(MOZ_WIDGET_ANDROID)
 EGLSurface
 GLContextEGL::CreateCompatibleSurface(void* aWindow)
 {
     if (mConfig == EGL_NO_CONFIG) {
-        MOZ_CRASH("GFX: Failed with invalid EGLConfig 2!\n");
+        MOZ_CRASH("GFX: Failed with invalid EGLConfig 2!");
     }
 
     return GLContextProviderEGL::CreateEGLSurface(aWindow, mConfig);
 }
 
 /* static */ EGLSurface
 GLContextProviderEGL::CreateEGLSurface(void* aWindow, EGLConfig aConfig)
 {
     // NOTE: aWindow is an ANativeWindow
     nsCString discardFailureId;
     if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
-        MOZ_CRASH("GFX: Failed to load EGL library 4!\n");
+        MOZ_CRASH("GFX: Failed to load EGL library 4!");
     }
     EGLConfig config = aConfig;
     if (!config && !CreateConfig(&config, /* aEnableDepthBuffer */ false)) {
-        MOZ_CRASH("GFX: Failed to create EGLConfig 2!\n");
+        MOZ_CRASH("GFX: Failed to create EGLConfig 2!");
     }
 
     MOZ_ASSERT(aWindow);
 
     EGLSurface surface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, aWindow,
                                                           0);
     if (surface == EGL_NO_SURFACE) {
-        MOZ_CRASH("GFX: Failed to create EGLSurface 2!\n");
+        MOZ_CRASH("GFX: Failed to create EGLSurface 2!");
     }
 
     return surface;
 }
 
 /* static */ void
 GLContextProviderEGL::DestroyEGLSurface(EGLSurface surface)
 {
     nsCString discardFailureId;
     if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
-        MOZ_CRASH("GFX: Failed to load EGL library 5!\n");
+        MOZ_CRASH("GFX: Failed to load EGL library 5!");
     }
 
     sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface);
 }
 #endif // defined(ANDROID)
 
 static void
 FillContextAttribs(bool alpha, bool depth, bool stencil, bool bpp16,
--- a/gfx/layers/SourceSurfaceVolatileData.h
+++ b/gfx/layers/SourceSurfaceVolatileData.h
@@ -27,16 +27,17 @@ class SourceSurfaceVolatileData : public
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceVolatileData, override)
 
   SourceSurfaceVolatileData()
     : mMutex("SourceSurfaceVolatileData")
     , mStride(0)
     , mMapCount(0)
     , mFormat(SurfaceFormat::UNKNOWN)
+    , mWasPurged(false)
   {
   }
 
   bool Init(const IntSize &aSize,
             int32_t aStride,
             SurfaceFormat aFormat);
 
   uint8_t *GetData() override { return mVBufPtr; }
@@ -61,32 +62,37 @@ public:
   // we want to allow it for SourceSurfaceVolatileData since it should
   // always be fine (for reading at least).
   //
   // This is the same as the base class implementation except using
   // mMapCount instead of mIsMapped since that breaks for multithread.
   bool Map(MapType, MappedSurface *aMappedSurface) override
   {
     MutexAutoLock lock(mMutex);
+    if (mWasPurged) {
+      return false;
+    }
     if (mMapCount == 0) {
       mVBufPtr = mVBuf;
     }
     if (mVBufPtr.WasBufferPurged()) {
+      mWasPurged = true;
       return false;
     }
     aMappedSurface->mData = mVBufPtr;
     aMappedSurface->mStride = mStride;
     ++mMapCount;
     return true;
   }
 
   void Unmap() override
   {
     MutexAutoLock lock(mMutex);
     MOZ_ASSERT(mMapCount > 0);
+    MOZ_ASSERT(!mWasPurged);
     if (--mMapCount == 0) {
       mVBufPtr = nullptr;
     }
   }
 
 private:
   ~SourceSurfaceVolatileData() override
   {
@@ -95,14 +101,15 @@ private:
 
   Mutex mMutex;
   int32_t mStride;
   int32_t mMapCount;
   IntSize mSize;
   RefPtr<VolatileBuffer> mVBuf;
   VolatileBufferPtr<uint8_t> mVBufPtr;
   SurfaceFormat mFormat;
+  bool mWasPurged;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_SOURCESURFACEVOLATILEDATA_H_ */
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -97,16 +97,17 @@ WebRenderLayerManager::DoDestroy(bool aI
   if (IsDestroyed()) {
     return;
   }
 
   LayerManager::Destroy();
 
   if (WrBridge()) {
     // Just clear ImageKeys, they are deleted during WebRenderAPI destruction.
+    mImageKeysToDeleteLater.Clear();
     mImageKeysToDelete.Clear();
     // CompositorAnimations are cleared by WebRenderBridgeParent.
     mDiscardedCompositorAnimationsIds.Clear();
     WrBridge()->Destroy(aIsSync);
   }
 
   mLastCanvasDatas.Clear();
 
@@ -372,17 +373,16 @@ WebRenderLayerManager::CreateWebRenderCo
 }
 
 void
 WebRenderLayerManager::EndTransactionWithoutLayer(nsDisplayList* aDisplayList,
                                                   nsDisplayListBuilder* aDisplayListBuilder)
 {
   MOZ_ASSERT(aDisplayList && aDisplayListBuilder);
   mEndTransactionWithoutLayers = true;
-  DiscardImages();
   WrBridge()->RemoveExpiredFontKeys();
   EndTransactionInternal(nullptr,
                          nullptr,
                          EndTransactionFlags::END_DEFAULT,
                          aDisplayList,
                          aDisplayListBuilder);
 }
 
@@ -682,17 +682,16 @@ WebRenderLayerManager::PushItemAsImage(n
 }
 
 void
 WebRenderLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
                                       void* aCallbackData,
                                       EndTransactionFlags aFlags)
 {
   mEndTransactionWithoutLayers = false;
-  DiscardImages();
   WrBridge()->RemoveExpiredFontKeys();
   EndTransactionInternal(aCallback, aCallbackData, aFlags);
 }
 
 bool
 WebRenderLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
                                               void* aCallbackData,
                                               EndTransactionFlags aFlags,
@@ -813,16 +812,22 @@ WebRenderLayerManager::EndTransactionInt
     }
     mScrollData.SetPaintSequenceNumber(mPaintSequenceNumber);
   }
 
   bool sync = mTarget != nullptr;
   mLatestTransactionId = mTransactionIdAllocator->GetTransactionId(/*aThrottle*/ true);
   TimeStamp transactionStart = mTransactionIdAllocator->GetTransactionStart();
 
+  for (const auto& key : mImageKeysToDelete) {
+    resourceUpdates.DeleteImage(key);
+  }
+  mImageKeysToDelete.Clear();
+  mImageKeysToDelete.SwapElements(mImageKeysToDeleteLater);
+
   // Skip the synchronization for buffer since we also skip the painting during
   // device-reset status.
   if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
     if (WrBridge()->GetSyncObject() &&
         WrBridge()->GetSyncObject()->IsSyncObjectValid()) {
       WrBridge()->GetSyncObject()->Synchronize();
     }
   }
@@ -918,26 +923,30 @@ WebRenderLayerManager::MakeSnapshotIfReq
   dt->FillRect(dst, pattern);
 
   mTarget = nullptr;
 }
 
 void
 WebRenderLayerManager::AddImageKeyForDiscard(wr::ImageKey key)
 {
-  mImageKeysToDelete.AppendElement(key);
+  mImageKeysToDeleteLater.AppendElement(key);
 }
 
 void
 WebRenderLayerManager::DiscardImages()
 {
   wr::IpcResourceUpdateQueue resources(WrBridge()->GetShmemAllocator());
+  for (const auto& key : mImageKeysToDeleteLater) {
+    resources.DeleteImage(key);
+  }
   for (const auto& key : mImageKeysToDelete) {
     resources.DeleteImage(key);
   }
+  mImageKeysToDeleteLater.Clear();
   mImageKeysToDelete.Clear();
   WrBridge()->UpdateResources(resources);
 }
 
 void
 WebRenderLayerManager::AddCompositorAnimationsIdForDiscard(uint64_t aId)
 {
   mDiscardedCompositorAnimationsIds.AppendElement(aId);
@@ -954,16 +963,17 @@ WebRenderLayerManager::DiscardCompositor
 }
 
 void
 WebRenderLayerManager::DiscardLocalImages()
 {
   // Removes images but doesn't tell the parent side about them
   // This is useful in empty / failed transactions where we created
   // image keys but didn't tell the parent about them yet.
+  mImageKeysToDeleteLater.Clear();
   mImageKeysToDelete.Clear();
 }
 
 void
 WebRenderLayerManager::Mutated(Layer* aLayer)
 {
   LayerManager::Mutated(aLayer);
   AddMutatedLayer(aLayer);
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -245,16 +245,21 @@ private:
                               void* aCallbackData,
                               EndTransactionFlags aFlags,
                               nsDisplayList* aDisplayList = nullptr,
                               nsDisplayListBuilder* aDisplayListBuilder = nullptr);
 
 private:
   nsIWidget* MOZ_NON_OWNING_REF mWidget;
   nsTArray<wr::ImageKey> mImageKeysToDelete;
+  // TODO - This is needed because we have some code that creates image keys
+  // and enqueues them for deletion right away which is bad not only because
+  // of poor texture cache usage, but also because images end up deleted before
+  // they are used. This should hopfully be temporary.
+  nsTArray<wr::ImageKey> mImageKeysToDeleteLater;
   nsTArray<uint64_t> mDiscardedCompositorAnimationsIds;
 
   /* PaintedLayer callbacks; valid at the end of a transaciton,
    * while rendering */
   DrawPaintedLayerCallback mPaintedLayerCallback;
   void *mPaintedLayerCallbackData;
 
   RefPtr<WebRenderBridgeChild> mWrChild;
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -609,22 +609,39 @@ public:
     if (idealArea <= currentArea && currentArea < bestMatchArea) {
       return true;
     }
 
     // This surface isn't an improvement over the current best match.
     return false;
   }
 
+  template<typename Function>
   void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
-                             MallocSizeOf                    aMallocSizeOf)
+                             MallocSizeOf                    aMallocSizeOf,
+                             Function&&                      aRemoveCallback)
   {
     CachedSurface::SurfaceMemoryReport report(aCounters, aMallocSizeOf);
-    for (auto iter = ConstIter(); !iter.Done(); iter.Next()) {
+    for (auto iter = mSurfaces.Iter(); !iter.Done(); iter.Next()) {
       NotNull<CachedSurface*> surface = WrapNotNull(iter.UserData());
+
+      // We don't need the drawable surface for ourselves, but adding a surface
+      // to the report will trigger this indirectly. If the surface was
+      // discarded by the OS because it was in volatile memory, we should remove
+      // it from the cache immediately rather than include it in the report.
+      DrawableSurface drawableSurface;
+      if (!surface->IsPlaceholder()) {
+        drawableSurface = surface->GetDrawableSurface();
+        if (!drawableSurface) {
+          aRemoveCallback(surface);
+          iter.Remove();
+          continue;
+        }
+      }
+
       const IntSize& size = surface->GetSurfaceKey().Size();
       bool factor2Size = false;
       if (mFactor2Mode) {
         factor2Size = (size == SuggestedSize(size));
       }
       report.Add(surface, factor2Size);
     }
   }
@@ -1068,16 +1085,18 @@ public:
   {
     RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
       return;  // No cached surfaces for this image, so nothing to do.
     }
 
     cache->Prune([this, &aAutoLock](NotNull<CachedSurface*> aSurface) -> void {
       StopTracking(aSurface, /* aIsTracked */ true, aAutoLock);
+      // Individual surfaces must be freed outside the lock.
+      mCachedSurfacesDiscard.AppendElement(aSurface);
     });
   }
 
   void DiscardAll(const StaticMutexAutoLock& aAutoLock)
   {
     // Remove in order of cost because mCosts is an array and the other data
     // structures are all hash tables. Note that locked surfaces are not
     // removed, since they aren't present in mCosts.
@@ -1160,25 +1179,31 @@ public:
 "Count of how many times the surface cache has hit its capacity and been "
 "unable to insert a new surface.");
 
     return NS_OK;
   }
 
   void CollectSizeOfSurfaces(const ImageKey                  aImageKey,
                              nsTArray<SurfaceMemoryCounter>& aCounters,
-                             MallocSizeOf                    aMallocSizeOf)
+                             MallocSizeOf                    aMallocSizeOf,
+                             const StaticMutexAutoLock&      aAutoLock)
   {
     RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
       return;  // No surfaces for this image.
     }
 
     // Report all surfaces in the per-image cache.
-    cache->CollectSizeOfSurfaces(aCounters, aMallocSizeOf);
+    cache->CollectSizeOfSurfaces(aCounters, aMallocSizeOf,
+      [this, &aAutoLock](NotNull<CachedSurface*> aSurface) -> void {
+      StopTracking(aSurface, /* aIsTracked */ true, aAutoLock);
+      // Individual surfaces must be freed outside the lock.
+      mCachedSurfacesDiscard.AppendElement(aSurface);
+    });
   }
 
 private:
   already_AddRefed<ImageSurfaceCache> GetImageCache(const ImageKey aImageKey)
   {
     RefPtr<ImageSurfaceCache> imageCache;
     mImageCaches.Get(aImageKey, getter_AddRefs(imageCache));
     return imageCache.forget();
@@ -1539,19 +1564,23 @@ SurfaceCache::RemoveImage(const ImageKey
       discard = sInstance->RemoveImage(aImageKey, lock);
     }
   }
 }
 
 /* static */ void
 SurfaceCache::PruneImage(const ImageKey aImageKey)
 {
-  StaticMutexAutoLock lock(sInstanceMutex);
-  if (sInstance) {
-    sInstance->PruneImage(aImageKey, lock);
+  nsTArray<RefPtr<CachedSurface>> discard;
+  {
+    StaticMutexAutoLock lock(sInstanceMutex);
+    if (sInstance) {
+      sInstance->PruneImage(aImageKey, lock);
+      sInstance->TakeDiscard(discard, lock);
+    }
   }
 }
 
 /* static */ void
 SurfaceCache::DiscardAll()
 {
   nsTArray<RefPtr<CachedSurface>> discard;
   {
@@ -1563,22 +1592,26 @@ SurfaceCache::DiscardAll()
   }
 }
 
 /* static */ void
 SurfaceCache::CollectSizeOfSurfaces(const ImageKey                  aImageKey,
                                     nsTArray<SurfaceMemoryCounter>& aCounters,
                                     MallocSizeOf                    aMallocSizeOf)
 {
-  StaticMutexAutoLock lock(sInstanceMutex);
-  if (!sInstance) {
-    return;
+  nsTArray<RefPtr<CachedSurface>> discard;
+  {
+    StaticMutexAutoLock lock(sInstanceMutex);
+    if (!sInstance) {
+      return;
+    }
+
+    sInstance->CollectSizeOfSurfaces(aImageKey, aCounters, aMallocSizeOf, lock);
+    sInstance->TakeDiscard(discard, lock);
   }
-
-  return sInstance->CollectSizeOfSurfaces(aImageKey, aCounters, aMallocSizeOf);
 }
 
 /* static */ size_t
 SurfaceCache::MaximumCapacity()
 {
   StaticMutexAutoLock lock(sInstanceMutex);
   if (!sInstance) {
     return 0;
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -126,17 +126,17 @@ class CPOWProxyHandler : public BaseProx
     virtual bool hasInstance(JSContext* cx, HandleObject proxy,
                              MutableHandleValue v, bool* bp) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject obj, js::ESClass* cls) const override;
     virtual bool isArray(JSContext* cx, HandleObject obj,
                          IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
     virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override;
-    virtual void objectMoved(JSObject* proxy, const JSObject* old) const override;
+    virtual size_t objectMoved(JSObject* proxy, JSObject* old) const override;
     virtual bool isCallable(JSObject* obj) const override;
     virtual bool isConstructor(JSObject* obj) const override;
     virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const override;
     virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
                                         MutableHandleObject protop) const override;
 
     static const char family;
     static const CPOWProxyHandler singleton;
@@ -890,20 +890,21 @@ CPOWProxyHandler::finalize(JSFreeOp* fop
     AuxCPOWData* aux = AuxCPOWDataOf(proxy);
 
     OwnerOf(proxy)->drop(proxy);
 
     if (aux)
         delete aux;
 }
 
-void
-CPOWProxyHandler::objectMoved(JSObject* proxy, const JSObject* old) const
+size_t
+CPOWProxyHandler::objectMoved(JSObject* proxy, JSObject* old) const
 {
     OwnerOf(proxy)->updatePointer(proxy, old);
+    return 0;
 }
 
 bool
 CPOWProxyHandler::isCallable(JSObject* proxy) const
 {
     AuxCPOWData* aux = AuxCPOWDataOf(proxy);
     return aux->isCallable;
 }
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -505,18 +505,18 @@ typedef bool
  * marking its native structures.
  */
 typedef void
 (* JSTraceOp)(JSTracer* trc, JSObject* obj);
 
 typedef JSObject*
 (* JSWeakmapKeyDelegateOp)(JSObject* obj);
 
-typedef void
-(* JSObjectMovedOp)(JSObject* obj, const JSObject* old);
+typedef size_t
+(* JSObjectMovedOp)(JSObject* obj, JSObject* old);
 
 /* js::Class operation signatures. */
 
 namespace js {
 
 typedef bool
 (* LookupPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                      JS::MutableHandleObject objp, JS::MutableHandle<JS::PropertyResult> propp);
@@ -706,25 +706,32 @@ struct JS_STATIC_CLASS ClassExtension
      * that case, the wrapped object is returned by the wrapper's
      * weakmapKeyDelegateOp hook. As long as the wrapper is used as a weakmap
      * key, it will not be collected (and remain in the weakmap) until the
      * wrapped object is collected.
      */
     JSWeakmapKeyDelegateOp weakmapKeyDelegateOp;
 
     /**
-     * Optional hook called when an object is moved by a compacting GC.
+     * Optional hook called when an object is moved by generational or
+     * compacting GC.
      *
      * There may exist weak pointers to an object that are not traced through
      * when the normal trace APIs are used, for example objects in the wrapper
      * cache. This hook allows these pointers to be updated.
      *
      * Note that this hook can be called before JS_NewObject() returns if a GC
      * is triggered during construction of the object. This can happen for
      * global objects for example.
+     *
+     * The function should return the difference between nursery bytes used and
+     * tenured bytes used, which may be nonzero e.g. if some nursery-allocated
+     * data beyond the actual GC thing is moved into malloced memory.
+     *
+     * This is used to compute the nursery promotion rate.
      */
     JSObjectMovedOp objectMovedOp;
 };
 
 #define JS_NULL_CLASS_SPEC  nullptr
 #define JS_NULL_CLASS_EXT   nullptr
 
 struct JS_STATIC_CLASS ObjectOps
--- a/js/public/GCPolicyAPI.h
+++ b/js/public/GCPolicyAPI.h
@@ -89,30 +89,35 @@ struct StructGCPolicy
 
     static void sweep(T* tp) {
         return tp->sweep();
     }
 
     static bool needsSweep(T* tp) {
         return tp->needsSweep();
     }
+
+    static bool isValid(const T& tp) {
+        return true;
+    }
 };
 
 // The default GC policy attempts to defer to methods on the underlying type.
 // Most C++ structures that contain a default constructor, a trace function and
 // a sweep function will work out of the box with Rooted, Handle, GCVector,
 // and GCHash{Set,Map}.
 template <typename T> struct GCPolicy : public StructGCPolicy<T> {};
 
 // This policy ignores any GC interaction, e.g. for non-GC types.
 template <typename T>
 struct IgnoreGCPolicy {
     static T initial() { return T(); }
     static void trace(JSTracer* trc, T* t, const char* name) {}
     static bool needsSweep(T* v) { return false; }
+    static bool isValid(const T& v) { return true; }
 };
 template <> struct GCPolicy<uint32_t> : public IgnoreGCPolicy<uint32_t> {};
 template <> struct GCPolicy<uint64_t> : public IgnoreGCPolicy<uint64_t> {};
 
 template <typename T>
 struct GCPointerPolicy
 {
     static T initial() { return nullptr; }
@@ -120,25 +125,46 @@ struct GCPointerPolicy
         if (*vp)
             js::UnsafeTraceManuallyBarrieredEdge(trc, vp, name);
     }
     static bool needsSweep(T* vp) {
         if (*vp)
             return js::gc::IsAboutToBeFinalizedUnbarriered(vp);
         return false;
     }
+    static bool isValid(T v) {
+        return js::gc::IsCellPointerValid(v);
+    }
 };
 template <> struct GCPolicy<JS::Symbol*> : public GCPointerPolicy<JS::Symbol*> {};
 template <> struct GCPolicy<JSAtom*> : public GCPointerPolicy<JSAtom*> {};
 template <> struct GCPolicy<JSFunction*> : public GCPointerPolicy<JSFunction*> {};
 template <> struct GCPolicy<JSObject*> : public GCPointerPolicy<JSObject*> {};
 template <> struct GCPolicy<JSScript*> : public GCPointerPolicy<JSScript*> {};
 template <> struct GCPolicy<JSString*> : public GCPointerPolicy<JSString*> {};
 
 template <typename T>
+struct NonGCPointerPolicy
+{
+    static T initial() { return nullptr; }
+    static void trace(JSTracer* trc, T* vp, const char* name) {
+        if (*vp)
+            (*vp)->trace(trc);
+    }
+    static bool needsSweep(T* vp) {
+        if (*vp)
+            return (*vp)->needsSweep();
+        return false;
+    }
+    static bool isValid(T v) {
+        return true;
+    }
+};
+
+template <typename T>
 struct GCPolicy<JS::Heap<T>>
 {
     static void trace(JSTracer* trc, JS::Heap<T>* thingp, const char* name) {
         TraceEdge(trc, thingp, name);
     }
     static bool needsSweep(JS::Heap<T>* thingp) {
         return *thingp && js::gc::EdgeNeedsSweep(thingp);
     }
@@ -153,16 +179,21 @@ struct GCPolicy<mozilla::UniquePtr<T, D>
         if (tp->get())
             GCPolicy<T>::trace(trc, tp->get(), name);
     }
     static bool needsSweep(mozilla::UniquePtr<T,D>* tp) {
         if (tp->get())
             return GCPolicy<T>::needsSweep(tp->get());
         return false;
     }
+    static bool isValid(const mozilla::UniquePtr<T,D>& t) {
+        if (t.get())
+            return GCPolicy<T>::isValid(*t.get());
+        return true;
+    }
 };
 
 // GCPolicy<Maybe<T>> forwards tracing/sweeping to GCPolicy<T*> if
 // when the Maybe<T> is full.
 template <typename T>
 struct GCPolicy<mozilla::Maybe<T>>
 {
     static mozilla::Maybe<T> initial() { return mozilla::Maybe<T>(); }
@@ -170,15 +201,20 @@ struct GCPolicy<mozilla::Maybe<T>>
         if (tp->isSome())
             GCPolicy<T>::trace(trc, tp->ptr(), name);
     }
     static bool needsSweep(mozilla::Maybe<T>* tp) {
         if (tp->isSome())
             return GCPolicy<T>::needsSweep(tp->ptr());
         return false;
     }
+    static bool isValid(const mozilla::Maybe<T>& t) {
+        if (t.isSome())
+            return GCPolicy<T>::isValid(t.ref());
+        return true;
+    }
 };
 
 template <> struct GCPolicy<JS::Realm*>;  // see Realm.h
 
 } // namespace JS
 
 #endif // GCPolicyAPI_h
--- a/js/public/GCVariant.h
+++ b/js/public/GCVariant.h
@@ -113,16 +113,29 @@ struct GCPolicy<mozilla::Variant<Ts...>>
     using Impl = detail::GCVariantImplementation<Ts...>;
 
     // Variants do not provide initial(). They do not have a default initial
     // value and one must be provided.
 
     static void trace(JSTracer* trc, mozilla::Variant<Ts...>* v, const char* name) {
         Impl::trace(trc, v, name);
     }
+
+    static bool isValid(const mozilla::Variant<Ts...>& v) {
+        return v.match(IsValidMatcher());
+    }
+
+  private:
+    struct IsValidMatcher
+    {
+        template<typename T>
+        bool match(T& v) {
+            return GCPolicy<T>::isValid(v);
+        };
+    };
 };
 
 } // namespace JS
 
 namespace js {
 
 template <typename Wrapper, typename... Ts>
 class WrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper>
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -48,16 +48,17 @@ const size_t ChunkMarkBitmapOffset = 258
 const size_t ChunkMarkBitmapBits = 31744;
 #else
 const size_t ChunkMarkBitmapOffset = 1032352;
 const size_t ChunkMarkBitmapBits = 129024;
 #endif
 const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*);
 const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t);
 const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize;
+const size_t ChunkStoreBufferOffset = ChunkLocationOffset + sizeof(uint64_t);
 const size_t ArenaZoneOffset = sizeof(size_t);
 const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) +
                                sizeof(size_t) + sizeof(uintptr_t);
 
 /*
  * Live objects are marked black or gray. Everything reachable from a JS root is
  * marked black. Objects marked gray are eligible for cycle collection.
  *
@@ -367,31 +368,62 @@ CellIsMarkedGray(const Cell* cell)
 extern JS_PUBLIC_API(bool)
 CellIsMarkedGrayIfKnown(const Cell* cell);
 
 #ifdef DEBUG
 extern JS_PUBLIC_API(bool)
 CellIsNotGray(const Cell* cell);
 #endif
 
+MOZ_ALWAYS_INLINE ChunkLocation
+GetCellLocation(const void* cell)
+{
+    uintptr_t addr = uintptr_t(cell);
+    addr &= ~js::gc::ChunkMask;
+    addr |= js::gc::ChunkLocationOffset;
+    return *reinterpret_cast<ChunkLocation*>(addr);
+}
+
+MOZ_ALWAYS_INLINE bool
+NurseryCellHasStoreBuffer(const void* cell)
+{
+    uintptr_t addr = uintptr_t(cell);
+    addr &= ~js::gc::ChunkMask;
+    addr |= js::gc::ChunkStoreBufferOffset;
+    return *reinterpret_cast<void**>(addr) != nullptr;
+}
+
 } /* namespace detail */
 
 MOZ_ALWAYS_INLINE bool
 IsInsideNursery(const js::gc::Cell* cell)
 {
     if (!cell)
         return false;
-    uintptr_t addr = uintptr_t(cell);
-    addr &= ~js::gc::ChunkMask;
-    addr |= js::gc::ChunkLocationOffset;
-    auto location = *reinterpret_cast<ChunkLocation*>(addr);
+    auto location = detail::GetCellLocation(cell);
     MOZ_ASSERT(location == ChunkLocation::Nursery || location == ChunkLocation::TenuredHeap);
     return location == ChunkLocation::Nursery;
 }
 
+MOZ_ALWAYS_INLINE bool
+IsCellPointerValid(const void* cell)
+{
+    if (!cell)
+        return true;
+    auto addr = uintptr_t(cell);
+    if (addr < ChunkSize || addr % CellAlignBytes != 0)
+        return false;
+    auto location = detail::GetCellLocation(cell);
+    if (location == ChunkLocation::TenuredHeap)
+        return !!detail::GetGCThingZone(addr);
+    if (location == ChunkLocation::Nursery)
+        return detail::NurseryCellHasStoreBuffer(cell);
+    return false;
+}
+
 } /* namespace gc */
 } /* namespace js */
 
 namespace JS {
 
 static MOZ_ALWAYS_INLINE Zone*
 GetTenuredGCThingZone(GCCellPtr thing)
 {
--- a/js/public/Id.h
+++ b/js/public/Id.h
@@ -166,16 +166,19 @@ namespace JS {
 
 template <>
 struct GCPolicy<jsid>
 {
     static jsid initial() { return JSID_VOID; }
     static void trace(JSTracer* trc, jsid* idp, const char* name) {
         js::UnsafeTraceManuallyBarrieredEdge(trc, idp, name);
     }
+    static bool isValid(jsid id) {
+        return !JSID_IS_GCTHING(id) || js::gc::IsCellPointerValid(JSID_TO_GCTHING(id).asCell());
+    }
 };
 
 } // namespace JS
 
 namespace js {
 
 template <>
 struct BarrierMethods<jsid>
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -330,17 +330,17 @@ class JS_FRIEND_API(BaseProxyHandler)
                                  ESClass* cls) const;
     virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const;
     virtual const char* className(JSContext* cx, HandleObject proxy) const;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const;
     virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const;
     virtual void trace(JSTracer* trc, JSObject* proxy) const;
     virtual void finalize(JSFreeOp* fop, JSObject* proxy) const;
-    virtual void objectMoved(JSObject* proxy, const JSObject* old) const;
+    virtual size_t objectMoved(JSObject* proxy, JSObject* old) const;
 
     // Allow proxies, wrappers in particular, to specify callability at runtime.
     // Note: These do not take const JSObject*, but they do in spirit.
     //       We are not prepared to do this, as there's little const correctness
     //       in the external APIs that handle proxies.
     virtual bool isCallable(JSObject* obj) const;
     virtual bool isConstructor(JSObject* obj) const;
 
@@ -679,28 +679,16 @@ inline void assertEnteredPolicy(JSContex
 
 extern JS_FRIEND_API(JSObject*)
 InitProxyClass(JSContext* cx, JS::HandleObject obj);
 
 extern JS_FRIEND_DATA(const js::ClassOps) ProxyClassOps;
 extern JS_FRIEND_DATA(const js::ClassExtension) ProxyClassExtension;
 extern JS_FRIEND_DATA(const js::ObjectOps) ProxyObjectOps;
 
-/*
- * Helper Macros for creating JSClasses that function as proxies.
- *
- * NB: The macro invocation must be surrounded by braces, so as to
- *     allow for potential JSClass extensions.
- */
-#define PROXY_MAKE_EXT(objectMoved)                                     \
-    {                                                                   \
-        js::proxy_WeakmapKeyDelegate,                                   \
-        objectMoved                                                     \
-    }
-
 template <unsigned Flags>
 constexpr unsigned
 CheckProxyFlags()
 {
     // For now assert each Proxy Class has at least 1 reserved slot. This is
     // not a hard requirement, but helps catch Classes that need an explicit
     // JSCLASS_HAS_RESERVED_SLOTS since bug 1360523.
     static_assert(((Flags >> JSCLASS_RESERVED_SLOTS_SHIFT) & JSCLASS_RESERVED_SLOTS_MASK) > 0,
@@ -717,27 +705,24 @@ CheckProxyFlags()
     // always have finalizers, and whether they can be nursery allocated is
     // controlled by the canNurseryAllocate() method on the proxy handler.
     static_assert(!(Flags & JSCLASS_SKIP_NURSERY_FINALIZE),
                   "Proxies must not use JSCLASS_SKIP_NURSERY_FINALIZE; use "
                   "the canNurseryAllocate() proxy handler method instead.");
     return Flags;
 }
 
-#define PROXY_CLASS_WITH_EXT(name, flags, extPtr)                                       \
+#define PROXY_CLASS_DEF(name, flags)                                                    \
     {                                                                                   \
         name,                                                                           \
         js::Class::NON_NATIVE |                                                         \
             JSCLASS_IS_PROXY |                                                          \
             JSCLASS_DELAY_METADATA_BUILDER |                                            \
             js::CheckProxyFlags<flags>(),                                               \
         &js::ProxyClassOps,                                                             \
         JS_NULL_CLASS_SPEC,                                                             \
-        extPtr,                                                                         \
+        &js::ProxyClassExtension,                                                       \
         &js::ProxyObjectOps                                                             \
     }
 
-#define PROXY_CLASS_DEF(name, flags) \
-  PROXY_CLASS_WITH_EXT(name, flags, &js::ProxyClassExtension)
-
 } /* namespace js */
 
 #endif /* js_Proxy_h */
--- a/js/public/Realm.h
+++ b/js/public/Realm.h
@@ -23,19 +23,18 @@ JS_PUBLIC_API(void) TraceRealm(JSTracer*
 JS_PUBLIC_API(bool) RealmNeedsSweep(JS::Realm* realm);
 }
 }
 
 namespace JS {
 
 // Each Realm holds a strong reference to its GlobalObject, and vice versa.
 template <>
-struct GCPolicy<Realm*>
+struct GCPolicy<Realm*> : public NonGCPointerPolicy<Realm*>
 {
-    static Realm* initial() { return nullptr; }
     static void trace(JSTracer* trc, Realm** vp, const char* name) {
         if (*vp)
             ::js::gc::TraceRealm(trc, *vp, name);
     }
     static bool needsSweep(Realm** vp) {
         return *vp && ::js::gc::RealmNeedsSweep(*vp);
     }
 };
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -391,16 +391,17 @@ class TenuredHeap : public js::HeapBase<
         static_assert(sizeof(T) == sizeof(TenuredHeap<T>),
                       "TenuredHeap<T> must be binary compatible with T.");
     }
     explicit TenuredHeap(T p) : bits(0) { setPtr(p); }
     explicit TenuredHeap(const TenuredHeap<T>& p) : bits(0) { setPtr(p.getPtr()); }
 
     void setPtr(T newPtr) {
         MOZ_ASSERT((reinterpret_cast<uintptr_t>(newPtr) & flagsMask) == 0);
+        MOZ_ASSERT(js::gc::IsCellPointerValid(newPtr));
         if (newPtr)
             AssertGCThingMustBeTenured(newPtr);
         bits = (bits & flagsMask) | reinterpret_cast<uintptr_t>(newPtr);
     }
 
     void setFlags(uintptr_t flagsToSet) {
         MOZ_ASSERT((flagsToSet & ~flagsMask) == 0);
         bits |= flagsToSet;
@@ -566,19 +567,21 @@ class MOZ_STACK_CLASS MutableHandle : pu
 
   private:
     // Disallow nullptr for overloading purposes.
     MutableHandle(decltype(nullptr)) = delete;
 
   public:
     void set(const T& v) {
         *ptr = v;
+        MOZ_ASSERT(GCPolicy<T>::isValid(*ptr));
     }
     void set(T&& v) {
         *ptr = mozilla::Move(v);
+        MOZ_ASSERT(GCPolicy<T>::isValid(*ptr));
     }
 
     /*
      * This may be called only if the location of the T is guaranteed
      * to be marked (for some reason other than being a Rooted),
      * e.g., if it is guaranteed to be reachable from an implicit root.
      *
      * Create a MutableHandle from a raw location of a T.
@@ -813,35 +816,38 @@ class MOZ_RAII Rooted : public js::Roote
     {
         registerWithRootLists(rootLists(cx));
     }
 
     template <typename RootingContext, typename S>
     Rooted(const RootingContext& cx, S&& initial)
       : ptr(mozilla::Forward<S>(initial))
     {
+        MOZ_ASSERT(GCPolicy<T>::isValid(ptr));
         registerWithRootLists(rootLists(cx));
     }
 
     ~Rooted() {
         MOZ_ASSERT(*stack == reinterpret_cast<Rooted<void*>*>(this));
         *stack = prev;
     }
 
     Rooted<T>* previous() { return reinterpret_cast<Rooted<T>*>(prev); }
 
     /*
      * This method is public for Rooted so that Codegen.py can use a Rooted
      * interchangeably with a MutableHandleValue.
      */
     void set(const T& value) {
         ptr = value;
+        MOZ_ASSERT(GCPolicy<T>::isValid(ptr));
     }
     void set(T&& value) {
         ptr = mozilla::Move(value);
+        MOZ_ASSERT(GCPolicy<T>::isValid(ptr));
     }
 
     DECLARE_POINTER_CONSTREF_OPS(T);
     DECLARE_POINTER_ASSIGN_OPS(Rooted, T);
     DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
     DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr);
 
   private:
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -349,27 +349,27 @@ class MOZ_NON_PARAM alignas(8) Value
     }
 
     double& getDoubleRef() {
         MOZ_ASSERT(isDouble());
         return data.asDouble;
     }
 
     void setString(JSString* str) {
-        MOZ_ASSERT(uintptr_t(str) > 0x1000);
+        MOZ_ASSERT(js::gc::IsCellPointerValid(str));
         data.asBits = bitsFromTagAndPayload(JSVAL_TAG_STRING, PayloadType(str));
     }
 
     void setSymbol(JS::Symbol* sym) {
-        MOZ_ASSERT(uintptr_t(sym) > 0x1000);
+        MOZ_ASSERT(js::gc::IsCellPointerValid(sym));
         data.asBits = bitsFromTagAndPayload(JSVAL_TAG_SYMBOL, PayloadType(sym));
     }
 
     void setObject(JSObject& obj) {
-        MOZ_ASSERT(uintptr_t(&obj) >= 0x1000);
+        MOZ_ASSERT(js::gc::IsCellPointerValid(&obj));
 #if defined(JS_PUNBOX64)
         // VisualStudio cannot contain parenthesized C++ style cast and shift
         // inside decltype in template parameter:
         //   AssertionConditionType<decltype((uintptr_t(x) >> 1))>
         // It throws syntax error.
         MOZ_ASSERT((((uintptr_t)&obj) >> JSVAL_TAG_SHIFT) == 0);
 #endif
         setObjectNoCheck(&obj);
@@ -751,17 +751,17 @@ class MOZ_NON_PARAM alignas(8) Value
     void setPrivateGCThing(js::gc::Cell* cell) {
         MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String,
                    "Private GC thing Values must not be strings. Make a StringValue instead.");
         MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol,
                    "Private GC thing Values must not be symbols. Make a SymbolValue instead.");
         MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object,
                    "Private GC thing Values must not be objects. Make an ObjectValue instead.");
 
-        MOZ_ASSERT(uintptr_t(cell) > 0x1000);
+        MOZ_ASSERT(js::gc::IsCellPointerValid(cell));
 #if defined(JS_PUNBOX64)
         // VisualStudio cannot contain parenthesized C++ style cast and shift
         // inside decltype in template parameter:
         //   AssertionConditionType<decltype((uintptr_t(x) >> 1))>
         // It throws syntax error.
         MOZ_ASSERT((((uintptr_t)cell) >> JSVAL_TAG_SHIFT) == 0);
 #endif
         data.asBits = bitsFromTagAndPayload(JSVAL_TAG_PRIVATE_GCTHING, PayloadType(cell));
@@ -1254,16 +1254,19 @@ struct GCPolicy<JS::Value>
 {
     static Value initial() { return UndefinedValue(); }
     static void trace(JSTracer* trc, Value* v, const char* name) {
         js::UnsafeTraceManuallyBarrieredEdge(trc, v, name);
     }
     static bool isTenured(const Value& thing) {
         return !thing.isGCThing() || !IsInsideNursery(thing.toGCThing());
     }
+    static bool isValid(const Value& value) {
+        return !value.isGCThing() || js::gc::IsCellPointerValid(value.toGCThing());
+    }
 };
 
 } // namespace JS
 
 namespace js {
 
 template <>
 struct BarrierMethods<JS::Value>
--- a/js/rust/src/glue.rs
+++ b/js/rust/src/glue.rs
@@ -128,17 +128,18 @@ pub struct ProxyTraps {
                                                                  hint: JSType,
                                                                  vp: JS::MutableHandleValue)
                                                                  -> bool>,
     pub trace:
         ::std::option::Option<unsafe extern "C" fn(trc: *mut JSTracer, proxy: *mut JSObject)>,
     pub finalize:
         ::std::option::Option<unsafe extern "C" fn(fop: *mut JSFreeOp, proxy: *mut JSObject)>,
     pub objectMoved:
-        ::std::option::Option<unsafe extern "C" fn(proxy: *mut JSObject, old: *const JSObject)>,
+        ::std::option::Option<unsafe extern "C" fn(proxy: *mut JSObject,
+                                                   old: *mut JSObject) -> usize>,
     pub isCallable: ::std::option::Option<unsafe extern "C" fn(obj: *mut JSObject) -> bool>,
     pub isConstructor: ::std::option::Option<unsafe extern "C" fn(obj: *mut JSObject) -> bool>,
 }
 impl ::std::default::Default for ProxyTraps {
     fn default() -> ProxyTraps {
         unsafe { ::std::mem::zeroed() }
     }
 }
--- a/js/rust/src/jsglue.cpp
+++ b/js/rust/src/jsglue.cpp
@@ -80,17 +80,17 @@ struct ProxyTraps {
     JSString* (*fun_toString)(JSContext *cx, JS::HandleObject proxy,
                               bool isToString);
     //bool (*regexp_toShared)(JSContext *cx, JS::HandleObject proxy, RegExpGuard *g);
     bool (*boxedValue_unbox)(JSContext *cx, JS::HandleObject proxy,
                              JS::MutableHandleValue vp);
     bool (*defaultValue)(JSContext *cx, JS::HandleObject obj, JSType hint, JS::MutableHandleValue vp);
     void (*trace)(JSTracer *trc, JSObject *proxy);
     void (*finalize)(JSFreeOp *fop, JSObject *proxy);
-    void (*objectMoved)(JSObject *proxy, const JSObject *old);
+    size_t (*objectMoved)(JSObject *proxy, JSObject *old);
 
     bool (*isCallable)(JSObject *obj);
     bool (*isConstructor)(JSObject *obj);
 
     // watch
     // unwatch
     // getElements
 
@@ -221,22 +221,21 @@ static int HandlerFamily;
                                                                                 \
     virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override        \
     {                                                                           \
         mTraps.finalize                                                         \
         ? mTraps.finalize(fop, proxy)                                           \
         : _base::finalize(fop, proxy);                                          \
     }                                                                           \
                                                                                 \
-    virtual void objectMoved(JSObject* proxy,                                   \
-                             const JSObject *old) const override                \
+    virtual size_t objectMoved(JSObject* proxy, JSObject *old) const override   \
     {                                                                           \
-        mTraps.objectMoved                                                      \
-        ? mTraps.objectMoved(proxy, old)                                        \
-        : _base::objectMoved(proxy, old);                                       \
+        return mTraps.objectMoved                                               \
+               ? mTraps.objectMoved(proxy, old)                                 \
+               : _base::objectMoved(proxy, old);                                \
     }                                                                           \
                                                                                 \
     virtual bool isCallable(JSObject* obj) const override                       \
     {                                                                           \
         return mTraps.isCallable                                                \
                ? mTraps.isCallable(obj)                                         \
                : _base::isCallable(obj);                                        \
     }                                                                           \
@@ -563,29 +562,19 @@ WrapperNew(JSContext* aCx, JS::HandleObj
     js::WrapperOptions options;
     if (aClass) {
         options.setClass(js::Valueify(aClass));
     }
     options.setSingleton(aSingleton);
     return js::Wrapper::New(aCx, aObj, (const js::Wrapper*)aHandler, options);
 }
 
-void WindowProxyObjectMoved(JSObject*, const JSObject*)
-{
-    abort();
-}
-
-static const js::ClassExtension WindowProxyClassExtension = PROXY_MAKE_EXT(
-    WindowProxyObjectMoved
-);
-
-const js::Class WindowProxyClass = PROXY_CLASS_WITH_EXT(
+const js::Class WindowProxyClass = PROXY_CLASS_DEF(
     "Proxy",
-    JSCLASS_HAS_RESERVED_SLOTS(1), /* additional class flags */
-    &WindowProxyClassExtension);
+    JSCLASS_HAS_RESERVED_SLOTS(1)); /* additional class flags */
 
 const js::Class*
 GetWindowProxyClass()
 {
     return &WindowProxyClass;
 }
 
 JS::Value
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -270,41 +270,42 @@ MapIteratorObject::finalize(FreeOp* fop,
     MOZ_ASSERT(!IsInsideNursery(obj));
 
     auto range = MapIteratorObjectRange(&obj->as<NativeObject>());
     MOZ_ASSERT(!obj->zone()->group()->nursery().isInside(range));
 
     fop->delete_(range);
 }
 
-void
-MapIteratorObject::objectMoved(JSObject* obj, const JSObject* old)
+size_t
+MapIteratorObject::objectMoved(JSObject* obj, JSObject* old)
 {
     if (!IsInsideNursery(old))
-        return;
+        return 0;
 
     MapIteratorObject* iter = &obj->as<MapIteratorObject>();
     ValueMap::Range* range = MapIteratorObjectRange(iter);
     if (!range)
-        return;
+        return 0;
 
     Nursery& nursery = iter->zone()->group()->nursery();
     if (!nursery.isInside(range)) {
         nursery.removeMallocedBuffer(range);
-        return;
+        return 0;
     }
 
     AutoEnterOOMUnsafeRegion oomUnsafe;
     auto newRange = iter->zone()->pod_malloc<ValueMap::Range>();
     if (!newRange)
         oomUnsafe.crash("MapIteratorObject failed to allocate Range data while tenuring.");
 
     new (newRange) ValueMap::Range(*range);
     range->~Range();
     iter->setReservedSlot(MapIteratorObject::RangeSlot, PrivateValue(newRange));
+    return sizeof(ValueMap::Range);
 }
 
 template <typename Range>
 static void
 DestroyRange(JSObject* iterator, Range* range)
 {
     range->~Range();
     if (!IsInsideNursery(iterator))
@@ -1119,41 +1120,42 @@ SetIteratorObject::finalize(FreeOp* fop,
     MOZ_ASSERT(!IsInsideNursery(obj));
 
     auto range = SetIteratorObjectRange(&obj->as<NativeObject>());
     MOZ_ASSERT(!obj->zone()->group()->nursery().isInside(range));
 
     fop->delete_(range);
 }
 
-void
-SetIteratorObject::objectMoved(JSObject* obj, const JSObject* old)
+size_t
+SetIteratorObject::objectMoved(JSObject* obj, JSObject* old)
 {
     if (!IsInsideNursery(old))
-        return;
+        return 0;
 
     SetIteratorObject* iter = &obj->as<SetIteratorObject>();
     ValueSet::Range* range = SetIteratorObjectRange(iter);
     if (!range)
-        return;
+        return 0;
 
     Nursery& nursery = iter->zone()->group()->nursery();
     if (!nursery.isInside(range)) {
         nursery.removeMallocedBuffer(range);
-        return;
+        return 0;
     }
 
     AutoEnterOOMUnsafeRegion oomUnsafe;
     auto newRange = iter->zone()->pod_malloc<ValueSet::Range>();
     if (!newRange)
         oomUnsafe.crash("SetIteratorObject failed to allocate Range data while tenuring.");
 
     new (newRange) ValueSet::Range(*range);
     range->~Range();
     iter->setReservedSlot(SetIteratorObject::RangeSlot, PrivateValue(newRange));
+    return sizeof(ValueSet::Range);
 }
 
 bool
 SetIteratorObject::next(Handle<SetIteratorObject*> setIterator, HandleArrayObject resultObj,
                         JSContext* cx)
 {
     // Check invariants for inlined _GetNextSetEntryForIterator.
 
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -185,17 +185,17 @@ class MapIteratorObject : public NativeO
                   "RangeSlot must match self-hosting define for range or index slot.");
     static_assert(KindSlot == ITERATOR_SLOT_ITEM_KIND,
                   "KindSlot must match self-hosting define for item kind slot.");
 
     static const JSFunctionSpec methods[];
     static MapIteratorObject* create(JSContext* cx, HandleObject mapobj, ValueMap* data,
                                      MapObject::IteratorKind kind);
     static void finalize(FreeOp* fop, JSObject* obj);
-    static void objectMoved(JSObject* obj, const JSObject* old);
+    static size_t objectMoved(JSObject* obj, JSObject* old);
 
     static MOZ_MUST_USE bool next(Handle<MapIteratorObject*> mapIterator,
                                   HandleArrayObject resultPairObj, JSContext* cx);
 
     static JSObject* createResultPair(JSContext* cx);
 
   private:
     inline MapObject::IteratorKind kind() const;
@@ -288,17 +288,17 @@ class SetIteratorObject : public NativeO
                   "RangeSlot must match self-hosting define for range or index slot.");
     static_assert(KindSlot == ITERATOR_SLOT_ITEM_KIND,
                   "KindSlot must match self-hosting define for item kind slot.");
 
     static const JSFunctionSpec methods[];
     static SetIteratorObject* create(JSContext* cx, HandleObject setobj, ValueSet* data,
                                      SetObject::IteratorKind kind);
     static void finalize(FreeOp* fop, JSObject* obj);
-    static void objectMoved(JSObject* obj, const JSObject* old);
+    static size_t objectMoved(JSObject* obj, JSObject* old);
 
     static MOZ_MUST_USE bool next(Handle<SetIteratorObject*> setIterator,
                                   HandleArrayObject resultObj, JSContext* cx);
 
     static JSObject* createResult(JSContext* cx);
 
   private:
     inline SetObject::IteratorKind kind() const;
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -1568,17 +1568,17 @@ js::RegExpPrototypeOptimizable(JSContext
 
     args.rval().setBoolean(RegExpPrototypeOptimizableRaw(cx, &args[0].toObject()));
     return true;
 }
 
 bool
 js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto)
 {
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
     AutoAssertNoPendingException aanpe(cx);
     if (!proto->isNative())
         return false;
 
     NativeObject* nproto = static_cast<NativeObject*>(proto);
 
     Shape* shape = cx->compartment()->regExps.getOptimizableRegExpPrototypeShape();
     if (shape == nproto->lastProperty())
@@ -1661,17 +1661,17 @@ js::RegExpInstanceOptimizable(JSContext*
     args.rval().setBoolean(RegExpInstanceOptimizableRaw(cx, &args[0].toObject(),
                                                         &args[1].toObject()));
     return true;
 }
 
 bool
 js::RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* obj, JSObject* proto)
 {
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
     AutoAssertNoPendingException aanpe(cx);
 
     RegExpObject* rx = &obj->as<RegExpObject>();
 
     Shape* shape = cx->compartment()->regExps.getOptimizableRegExpInstanceShape();
     if (shape == rx->lastProperty())
         return true;
 
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2353,17 +2353,17 @@ SetIonCheckGraphCoherency(JSContext* cx,
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     jit::JitOptions.checkGraphConsistency = ToBoolean(args.get(0));
     args.rval().setUndefined();
     return true;
 }
 
 class CloneBufferObject : public NativeObject {
-    static const JSPropertySpec props_[2];
+    static const JSPropertySpec props_[3];
     static const size_t DATA_SLOT   = 0;
     static const size_t LENGTH_SLOT = 1;
     static const size_t NUM_SLOTS   = 2;
 
   public:
     static const Class class_;
 
     static CloneBufferObject* Create(JSContext* cx) {
@@ -2408,46 +2408,54 @@ class CloneBufferObject : public NativeO
             JSAutoStructuredCloneBuffer clonebuf(JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr);
             clonebuf.adopt(Move(*data()));
         }
         setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
     }
 
     static bool
     setCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
-        if (args.length() != 1) {
-            JS_ReportErrorASCII(cx, "clonebuffer setter requires a single string argument");
-            return false;
-        }
-        if (!args[0].isString()) {
-            JS_ReportErrorASCII(cx, "clonebuffer value must be a string");
-            return false;
-        }
-
         if (fuzzingSafe) {
             // A manually-created clonebuffer could easily trigger a crash
             args.rval().setUndefined();
             return true;
         }
 
         Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
-        obj->discard();
-
-        char* str = JS_EncodeString(cx, args[0].toString());
-        if (!str)
-            return false;
-        size_t nbytes = JS_GetStringLength(args[0].toString());
-        MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0);
-        auto buf = js::MakeUnique<JSStructuredCloneData>(0, 0, nbytes);
-        if (!buf->Init(nbytes, nbytes)) {
-            JS_free(cx, str);
+
+        uint8_t* data = nullptr;
+        UniquePtr<uint8_t[], JS::FreePolicy> dataOwner;
+        uint32_t nbytes;
+
+        if (args.get(0).isObject() && args[0].toObject().is<ArrayBufferObject>()) {
+            ArrayBufferObject* buffer = &args[0].toObject().as<ArrayBufferObject>();
+            bool isSharedMemory;
+            js::GetArrayBufferLengthAndData(buffer, &nbytes, &isSharedMemory, &data);
+            MOZ_ASSERT(!isSharedMemory);
+        } else {
+            JSString* str = JS::ToString(cx, args.get(0));
+            if (!str)
+                return false;
+            data = reinterpret_cast<uint8_t*>(JS_EncodeString(cx, str));
+            if (!data)
+                return false;
+            dataOwner.reset(data);
+            nbytes = JS_GetStringLength(str);
+        }
+
+        if (nbytes % sizeof(uint64_t) != 0) {
+            JS_ReportErrorASCII(cx, "Invalid length for clonebuffer data");
             return false;
         }
-        js_memcpy(buf->Start(), str, nbytes);
-        JS_free(cx, str);
+
+        auto buf = js::MakeUnique<JSStructuredCloneData>(0, 0, nbytes);
+        if (!buf->Init(nbytes, nbytes))
+            return false;
+        js_memcpy(buf->Start(), data, nbytes);
+        obj->discard();
         obj->setData(buf.release());
 
         args.rval().setUndefined();
         return true;
     }
 
     static bool
     is(HandleValue v) {
@@ -2456,55 +2464,95 @@ class CloneBufferObject : public NativeO
 
     static bool
     setCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
         CallArgs args = CallArgsFromVp(argc, vp);
         return CallNonGenericMethod<is, setCloneBuffer_impl>(cx, args);
     }
 
     static bool
-    getCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
-        Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
-        MOZ_ASSERT(args.length() == 0);
-
+    getData(JSContext* cx, Handle<CloneBufferObject*> obj, JSStructuredCloneData** data) {
         if (!obj->data()) {
-            args.rval().setUndefined();
+            *data = nullptr;
             return true;
         }
 
         bool hasTransferable;
         if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable))
             return false;
 
         if (hasTransferable) {
             JS_ReportErrorASCII(cx, "cannot retrieve structured clone buffer with transferables");
             return false;
         }
 
-        size_t size = obj->data()->Size();
+        *data = obj->data();
+        return true;
+    }
+
+    static bool
+    getCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
+        Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
+        MOZ_ASSERT(args.length() == 0);
+
+        JSStructuredCloneData* data;
+        if (!getData(cx, obj, &data))
+            return false;
+
+        size_t size = data->Size();
         UniqueChars buffer(static_cast<char*>(js_malloc(size)));
         if (!buffer) {
             ReportOutOfMemory(cx);
             return false;
         }
-        auto iter = obj->data()->Iter();
-        obj->data()->ReadBytes(iter, buffer.get(), size);
+        auto iter = data->Iter();
+        data->ReadBytes(iter, buffer.get(), size);
         JSString* str = JS_NewStringCopyN(cx, buffer.get(), size);
         if (!str)
             return false;
         args.rval().setString(str);
         return true;
     }
 
     static bool
     getCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
         CallArgs args = CallArgsFromVp(argc, vp);
         return CallNonGenericMethod<is, getCloneBuffer_impl>(cx, args);
     }
 
+    static bool
+    getCloneBufferAsArrayBuffer_impl(JSContext* cx, const CallArgs& args) {
+        Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
+        MOZ_ASSERT(args.length() == 0);
+
+        JSStructuredCloneData* data;
+        if (!getData(cx, obj, &data))
+            return false;
+
+        size_t size = data->Size();
+        UniqueChars buffer(static_cast<char*>(js_malloc(size)));
+        if (!buffer) {
+            ReportOutOfMemory(cx);
+            return false;
+        }
+        auto iter = data->Iter();
+        data->ReadBytes(iter, buffer.get(), size);
+        JSObject* arrayBuffer = JS_NewArrayBufferWithContents(cx, size, buffer.release());
+        if (!arrayBuffer)
+            return false;
+        args.rval().setObject(*arrayBuffer);
+        return true;
+    }
+
+    static bool
+    getCloneBufferAsArrayBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
+        CallArgs args = CallArgsFromVp(argc, vp);
+        return CallNonGenericMethod<is, getCloneBufferAsArrayBuffer_impl>(cx, args);
+    }
+
     static void Finalize(FreeOp* fop, JSObject* obj) {
         obj->as<CloneBufferObject>().discard();
     }
 };
 
 static const ClassOps CloneBufferObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
@@ -2519,16 +2567,17 @@ const Class CloneBufferObject::class_ = 
     "CloneBuffer",
     JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS) |
     JSCLASS_FOREGROUND_FINALIZE,
     &CloneBufferObjectClassOps
 };
 
 const JSPropertySpec CloneBufferObject::props_[] = {
     JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
+    JS_PSG("arraybuffer", getCloneBufferAsArrayBuffer, 0),
     JS_PS_END
 };
 
 static mozilla::Maybe<JS::StructuredCloneScope>
 ParseCloneScope(JSContext* cx, HandleString str)
 {
     mozilla::Maybe<JS::StructuredCloneScope> scope;
 
@@ -4540,17 +4589,17 @@ static const JSFunctionSpecWithHelp Test
 "representativeStringArray()",
 "  Returns an array of strings that represent the various internal string\n"
 "  types and character encodings."),
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     JS_FN_HELP("oomThreadTypes", OOMThreadTypes, 0, 0,
 "oomThreadTypes()",
 "  Get the number of thread types that can be used as an argument for\n"
-"oomAfterAllocations() and oomAtAllocation()."),
+"  oomAfterAllocations() and oomAtAllocation()."),
 
     JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 2, 0,
 "oomAfterAllocations(count [,threadType])",
 "  After 'count' js_malloc memory allocations, fail every following allocation\n"
 "  (return nullptr). The optional thread type limits the effect to the\n"
 "  specified type of helper thread."),
 
     JS_FN_HELP("oomAtAllocation", OOMAtAllocation, 2, 0,
@@ -4694,18 +4743,18 @@ gc::ZealModeHelpText),
 "  memory or been terminated by the slow script dialog."),
 
     JS_FN_HELP("readGeckoProfilingStack", ReadGeckoProfilingStack, 0, 0,
 "readGeckoProfilingStack()",
 "  Reads the jit stack using ProfilingFrameIterator."),
 
     JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks, 0, 0,
 "enableOsiPointRegisterChecks()",
-"Emit extra code to verify live regs at the start of a VM call are not\n"
-"modified before its OsiPoint."),
+"  Emit extra code to verify live regs at the start of a VM call are not\n"
+"  modified before its OsiPoint."),
 
     JS_FN_HELP("displayName", DisplayName, 1, 0,
 "displayName(fn)",
 "  Gets the display name for a function, which can possibly be a guessed or\n"
 "  inferred name based on where the function was defined. This can be\n"
 "  different from the 'name' property on the function."),
 
     JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable, 0, 0,
@@ -4714,17 +4763,17 @@ gc::ZealModeHelpText),
 "  (e.g., by the debugger)."),
 
     JS_FN_HELP("isSimdAvailable", IsSimdAvailable, 0, 0,
 "isSimdAvailable",
 "  Returns true if SIMD extensions are supported on this platform."),
 
     JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions, 0, 0,
 "getCompilerOptions()",
-"Return an object describing some of the JIT compiler options.\n"),
+"  Return an object describing some of the JIT compiler options.\n"),
 
     JS_FN_HELP("isAsmJSModule", IsAsmJSModule, 1, 0,
 "isAsmJSModule(fn)",
 "  Returns whether the given value is a function containing \"use asm\" that has been\n"
 "  validated according to the asm.js spec."),
 
     JS_FN_HELP("isAsmJSModuleLoadedFromCache", IsAsmJSModuleLoadedFromCache, 1, 0,
 "isAsmJSModuleLoadedFromCache(fn)",
@@ -4767,17 +4816,17 @@ gc::ZealModeHelpText),
 "  until background compilation is complete."),
 
     JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0,
 "isLazyFunction(fun)",
 "  True if fun is a lazy JSFunction."),
 
     JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction, 1, 0,
 "isRelazifiableFunction(fun)",
-"  Ture if fun is a JSFunction with a relazifiable JSScript."),
+"  True if fun is a JSFunction with a relazifiable JSScript."),
 
     JS_FN_HELP("enableShellAllocationMetadataBuilder", EnableShellAllocationMetadataBuilder, 0, 0,
 "enableShellAllocationMetadataBuilder()",
 "  Use ShellAllocationMetadataBuilder to supply metadata for all newly created objects."),
 
     JS_FN_HELP("getAllocationMetadata", GetAllocationMetadata, 1, 0,
 "getAllocationMetadata(obj)",
 "  Get the metadata for an object."),
@@ -4820,21 +4869,20 @@ gc::ZealModeHelpText),
 "  are valuable and should be generally enabled, however they can be very expensive for large\n"
 "  (wasm) programs."),
 
     JS_FN_HELP("serialize", Serialize, 1, 0,
 "serialize(data, [transferables, [policy]])",
 "  Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
 "  clone buffer object. 'policy' may be an options hash. Valid keys:\n"
 "    'SharedArrayBuffer' - either 'allow' (the default) or 'deny'\n"
-"    to specify whether SharedArrayBuffers may be serialized.\n"
-"\n"
+"      to specify whether SharedArrayBuffers may be serialized.\n"
 "    'scope' - SameProcessSameThread, SameProcessDifferentThread, or\n"
-"    DifferentProcess. Determines how some values will be serialized.\n"
-"    Clone buffers may only be deserialized with a compatible scope."),
+"      DifferentProcess. Determines how some values will be serialized.\n"
+"      Clone buffers may only be deserialized with a compatible scope."),
 
     JS_FN_HELP("deserialize", Deserialize, 1, 0,
 "deserialize(clonebuffer[, opts])",
 "  Deserialize data generated by serialize. 'opts' is an options hash with one\n"
 "  recognized key 'scope', which limits the clone buffers that are considered\n"
 "  valid. Allowed values: 'SameProcessSameThread', 'SameProcessDifferentThread',\n"
 "  and 'DifferentProcess'. So for example, a DifferentProcess clone buffer\n"
 "  may be deserialized in any scope, but a SameProcessSameThread clone buffer\n"
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -2121,33 +2121,39 @@ InlineTypedObject::obj_trace(JSTracer* t
     // tracing. If there is an entry in the compartment's LazyArrayBufferTable,
     // tracing that reference will be taken care of by the table itself.
     if (typedObj.is<InlineTransparentTypedObject>())
         return;
 
     typedObj.typeDescr().traceInstances(trc, typedObj.inlineTypedMem(), 1);
 }
 
-/* static */ void
-InlineTypedObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src)
+/* static */ size_t
+InlineTypedObject::obj_moved(JSObject* dst, JSObject* src)
 {
+    if (!IsInsideNursery(src))
+        return 0;
+
     // Inline typed object element arrays can be preserved on the stack by Ion
     // and need forwarding pointers created during a minor GC. We can't do this
     // in the trace hook because we don't have any stale data to determine
     // whether this object moved and where it was moved from.
     TypeDescr& descr = dst->as<InlineTypedObject>().typeDescr();
     if (descr.kind() == type::Array) {
         // The forwarding pointer can be direct as long as there is enough
         // space for it. Other objects might point into the object's buffer,
         // but they will not set any direct forwarding pointers.
         uint8_t* oldData = reinterpret_cast<uint8_t*>(src) + offsetOfDataStart();
         uint8_t* newData = dst->as<InlineTypedObject>().inlineTypedMem();
-        dst->zone()->group()->nursery().maybeSetForwardingPointer(trc, oldData, newData,
-                                                                  descr.size() >= sizeof(uintptr_t));
+        auto& nursery = dst->zone()->group()->nursery();
+        bool direct = descr.size() >= sizeof(uintptr_t);
+        nursery.setForwardingPointerWhileTenuring(oldData, newData, direct);
     }
+
+    return 0;
 }
 
 ArrayBufferObject*
 InlineTransparentTypedObject::getOrCreateBuffer(JSContext* cx)
 {
     ObjectWeakMap*& table = cx->compartment()->lazyArrayBuffers;
     if (!table) {
         table = cx->new_<ObjectWeakMap>(cx);
@@ -2214,44 +2220,58 @@ const ObjectOps TypedObject::objectOps_ 
     TypedObject::obj_setProperty,
     TypedObject::obj_getOwnPropertyDescriptor,
     TypedObject::obj_deleteProperty,
     nullptr, nullptr, /* watch/unwatch */
     nullptr,   /* getElements */
     nullptr, /* thisValue */
 };
 
-#define DEFINE_TYPEDOBJ_CLASS(Name, Trace, flag)         \
+#define DEFINE_TYPEDOBJ_CLASS(Name, Trace, Moved, flag)   \
     static const ClassOps Name##ClassOps = {             \
         nullptr,        /* addProperty */                \
         nullptr,        /* delProperty */                \
         nullptr,        /* enumerate   */                \
         TypedObject::obj_newEnumerate,                   \
         nullptr,        /* resolve     */                \
         nullptr,        /* mayResolve  */                \
         nullptr,        /* finalize    */                \
         nullptr,        /* call        */                \
         nullptr,        /* hasInstance */                \
         nullptr,        /* construct   */                \
         Trace,                                           \
     };                                                   \
+    static const ClassExtension Name##ClassExt = {       \
+        nullptr,        /* weakmapKeyDelegateOp */       \
+        Moved           /* objectMovedOp */              \
+    };                                                   \
     const Class Name::class_ = {                         \
         # Name,                                          \
         Class::NON_NATIVE | flag,                        \
         &Name##ClassOps,                                 \
         JS_NULL_CLASS_SPEC,                              \
-        JS_NULL_CLASS_EXT,                               \
+        &Name##ClassExt,                                 \
         &TypedObject::objectOps_                         \
     }
 
-DEFINE_TYPEDOBJ_CLASS(OutlineTransparentTypedObject, OutlineTypedObject::obj_trace, 0);
-DEFINE_TYPEDOBJ_CLASS(OutlineOpaqueTypedObject,      OutlineTypedObject::obj_trace, 0);
-DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject,  InlineTypedObject::obj_trace,
+DEFINE_TYPEDOBJ_CLASS(OutlineTransparentTypedObject,
+                      OutlineTypedObject::obj_trace,
+                      nullptr,
+                      0);
+DEFINE_TYPEDOBJ_CLASS(OutlineOpaqueTypedObject,
+                      OutlineTypedObject::obj_trace,
+                      nullptr,
+                      0);
+DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject,
+                      InlineTypedObject::obj_trace,
+                      InlineTypedObject::obj_moved,
                       JSCLASS_DELAY_METADATA_BUILDER);
-DEFINE_TYPEDOBJ_CLASS(InlineOpaqueTypedObject,       InlineTypedObject::obj_trace,
+DEFINE_TYPEDOBJ_CLASS(InlineOpaqueTypedObject,
+                      InlineTypedObject::obj_trace,
+                      InlineTypedObject::obj_moved,
                       JSCLASS_DELAY_METADATA_BUILDER);
 
 static int32_t
 LengthForType(TypeDescr& descr)
 {
     switch (descr.kind()) {
       case type::Scalar:
       case type::Reference:
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -718,17 +718,17 @@ class InlineTypedObject : public TypedOb
         return inlineTypedMem();
     }
 
     uint8_t* inlineTypedMemForGC() const {
         return inlineTypedMem();
     }
 
     static void obj_trace(JSTracer* trace, JSObject* object);
-    static void objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src);
+    static size_t obj_moved(JSObject* dst, JSObject* src);
 
     static size_t offsetOfDataStart() {
         return offsetof(InlineTypedObject, data_);
     }
 
     static InlineTypedObject* create(JSContext* cx, HandleTypeDescr descr,
                                      gc::InitialHeap heap = gc::DefaultHeap);
     static InlineTypedObject* createCopy(JSContext* cx, Handle<InlineTypedObject*> templateObject,
--- a/js/src/devtools/rootAnalysis/analyze.py
+++ b/js/src/devtools/rootAnalysis/analyze.py
@@ -226,20 +226,23 @@ parser.add_argument('--jobs', '-j', defa
 parser.add_argument('--list', const=True, nargs='?', type=bool,
                     help='display available steps')
 parser.add_argument('--buildcommand', '--build', '-b', type=str, nargs='?',
                     help='command to build the tree being analyzed')
 parser.add_argument('--tag', '-t', type=str, nargs='?',
                     help='name of job, also sets build command to "build.<tag>"')
 parser.add_argument('--expect-file', type=str, nargs='?',
                     help='deprecated option, temporarily still present for backwards compatibility')
-parser.add_argument('--verbose', '-v', action='store_true',
+parser.add_argument('--verbose', '-v', action='count', default=1,
                     help='Display cut & paste commands to run individual steps')
+parser.add_argument('--quiet', '-q', action='count', default=0,
+                    help='Suppress output')
 
 args = parser.parse_args()
+args.verbose = max(0, args.verbose - args.quiet)
 
 for default in defaults:
     try:
         execfile(default, config)
         if args.verbose:
             print("Loaded %s" % default)
     except:
         pass
@@ -251,17 +254,17 @@ for k,v in vars(args).items():
         data[k] = v
 
 if args.tag and not args.buildcommand:
     args.buildcommand="build.%s" % args.tag
 
 if args.jobs is not None:
     data['jobs'] = args.jobs
 if not data.get('jobs'):
-    data['jobs'] = subprocess.check_output(['nproc', '--ignore=1']).strip()
+    data['jobs'] = int(subprocess.check_output(['nproc', '--ignore=1']).strip())
 
 if args.buildcommand:
     data['buildcommand'] = args.buildcommand
 elif 'BUILD' in os.environ:
     data['buildcommand'] = os.environ['BUILD']
 else:
     data['buildcommand'] = 'make -j4 -s'
 
--- a/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
+++ b/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
@@ -1,15 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
 
 "use strict";
 
 loadRelativeToScript('utility.js');
 loadRelativeToScript('annotations.js');
 loadRelativeToScript('callgraph.js');
+loadRelativeToScript('dumpCFG.js');
 
 ///////////////////////////////////////////////////////////////////////////////
 // Annotations
 ///////////////////////////////////////////////////////////////////////////////
 
 function checkExternalFunction(entry)
 {
     var whitelist = [
@@ -19,19 +20,22 @@ function checkExternalFunction(entry)
         "ceilf",
         "floorf",
         /^rusturl/,
         "memcmp",
         "strcmp",
         "fmod",
         "floor",
         "ceil",
+        "atof",
         /memchr/,
         "strlen",
+        "Servo_ComputedValues_EqualCustomProperties",
         /Servo_DeclarationBlock_GetCssText/,
+        "Servo_GetArcStringData",
         /nsIFrame::AppendOwnedAnonBoxes/,
         // Assume that atomic accesses are threadsafe.
         /^__atomic_fetch_/,
         /^__atomic_load_/,
         /^__atomic_thread_fence/,
     ];
     if (entry.matches(whitelist))
         return;
@@ -216,55 +220,101 @@ function treatAsSafeArgument(entry, varN
         ["Gecko_StyleShapeSource_SetURLValue", "aShape", null],
         ["Gecko_nsFont_InitSystem", "aDest", null],
         ["Gecko_nsFont_SetFontFeatureValuesLookup", "aFont", null],
         ["Gecko_nsFont_ResetFontFeatureValuesLookup", "aFont", null],
         ["Gecko_nsStyleFont_FixupNoneGeneric", "aFont", null],
         ["Gecko_StyleTransition_SetUnsupportedProperty", "aTransition", null],
         ["Gecko_AddPropertyToSet", "aPropertySet", null],
         ["Gecko_CalcStyleDifference", "aAnyStyleChanged", null],
+        ["Gecko_CalcStyleDifference", "aOnlyResetStructsChanged", null],
         ["Gecko_nsStyleSVG_CopyContextProperties", "aDst", null],
         ["Gecko_nsStyleFont_PrefillDefaultForGeneric", "aFont", null],
         ["Gecko_nsStyleSVG_SetContextPropertiesLength", "aSvg", null],
         ["Gecko_ClearAlternateValues", "aFont", null],
         ["Gecko_AppendAlternateValues", "aFont", null],
         ["Gecko_CopyAlternateValuesFrom", "aDest", null],
         ["Gecko_CounterStyle_GetName", "aResult", null],
         ["Gecko_CounterStyle_GetSingleString", "aResult", null],
+        ["Gecko_EnsureMozBorderColors", "aBorder", null],
+        ["Gecko_ClearMozBorderColors", "aBorder", null],
+        ["Gecko_AppendMozBorderColors", "aBorder", null],
+        ["Gecko_CopyMozBorderColors", "aDest", null],
     ];
     for (var [entryMatch, varMatch, csuMatch] of whitelist) {
         assert(entryMatch || varMatch || csuMatch);
         if (entryMatch && !nameMatches(entry.name, entryMatch))
             continue;
         if (varMatch && !nameMatches(varName, varMatch))
             continue;
         if (csuMatch && (!csuName || !nameMatches(csuName, csuMatch)))
             continue;
         return true;
     }
     return false;
 }
 
+function isSafeAssignment(entry, edge, variable)
+{
+    if (edge.Kind != 'Assign')
+        return false;
+
+    var [mangled, unmangled] = splitFunction(entry.name);
+
+    // The assignment
+    //
+    //   nsFont* font = fontTypes[eType];
+    //
+    // ends up with 'font' pointing to a member of 'this', so it should inherit
+    // the safety of 'this'.
+    if (unmangled.includes("mozilla::LangGroupFontPrefs::Initialize") &&
+        variable == 'font')
+    {
+        const [lhs, rhs] = edge.Exp;
+        const {Kind, Exp: [{Kind: indexKind, Exp: [collection, index]}]} = rhs;
+        if (Kind == 'Drf' &&
+            indexKind == 'Index' &&
+            collection.Kind == 'Var' &&
+            collection.Variable.Name[0] == 'fontTypes')
+        {
+            return entry.isSafeArgument(0); // 'this'
+        }
+    }
+
+    return false;
+}
+
 function checkFieldWrite(entry, location, fields)
 {
     var name = entry.name;
     for (var field of fields) {
         // The analysis is having some trouble keeping track of whether
         // already_AddRefed and nsCOMPtr structures are safe to access.
         // Hopefully these will be thread local, but it would be better to
         // improve the analysis to handle these.
         if (/already_AddRefed.*?.mRawPtr/.test(field))
             return;
         if (/nsCOMPtr<.*?>.mRawPtr/.test(field))
             return;
+
+        if (/\bThreadLocal<\b/.test(field))
+            return;
     }
 
     var str = "";
     for (var field of fields)
         str += " " + field;
+
+    // Bug 1400435
+    if (entry.stack[entry.stack.length - 1].callee.match(/^Gecko_CSSValue_Set/) &&
+        str == " nsAutoRefCnt.mValue")
+    {
+        return;
+    }
+
     dumpError(entry, location, "Field write" + str);
 }
 
 function checkDereferenceWrite(entry, location, variable)
 {
     var name = entry.name;
 
     // Maybe<T> uses placement new on local storage in a way we don't understand.
@@ -276,16 +326,21 @@ function checkDereferenceWrite(entry, lo
     // Allow this if the UniquePtr<> is threadsafe.
     if (/UniquePtr.*?::reset/.test(name) && entry.isSafeArgument(0))
         return;
 
     // Operations on nsISupports reference counts.
     if (hasThreadsafeReferenceCounts(entry, /nsCOMPtr<T>::swap\(.*?\[with T = (.*?)\]/))
         return;
 
+    // ConvertToLowerCase::write writes through a local pointer into the first
+    // argument.
+    if (/ConvertToLowerCase::write/.test(name) && entry.isSafeArgument(0))
+        return;
+
     dumpError(entry, location, "Dereference write " + (variable ? variable : "<unknown>"));
 }
 
 function ignoreCallEdge(entry, callee)
 {
     var name = entry.name;
 
     // nsPropertyTable::GetPropertyInternal has the option of removing data
@@ -362,103 +417,104 @@ function ignoreCallEdge(entry, callee)
 
 function ignoreContents(entry)
 {
     var whitelist = [
         // We don't care what happens when we're about to crash.
         "abort",
         /MOZ_ReportAssertionFailure/,
         /MOZ_ReportCrash/,
+        /MOZ_CrashPrintf/,
+        /MOZ_CrashOOL/,
         /AnnotateMozCrashReason/,
         /InvalidArrayIndex_CRASH/,
         /NS_ABORT_OOM/,
 
         // These ought to be threadsafe.
         "NS_DebugBreak",
         /mozalloc_handle_oom/,
         /^NS_Log/, /log_print/, /LazyLogModule::operator/,
         /SprintfLiteral/, "PR_smprintf", "PR_smprintf_free",
         /NS_DispatchToMainThread/, /NS_ReleaseOnMainThreadSystemGroup/,
         /NS_NewRunnableFunction/, /NS_Atomize/,
         /nsCSSValue::BufferFromString/,
         /NS_strdup/,
         /Assert_NoQueryNeeded/,
+        /AssertCurrentThreadOwnsMe/,
+        /PlatformThread::CurrentId/,
         /imgRequestProxy::GetProgressTracker/, // Uses an AutoLock
         /Smprintf/,
         "malloc",
         "free",
         "realloc",
         "jemalloc_thread_local_arena",
-        /profiler_register_thread/,
-        /profiler_unregister_thread/,
 
         // These all create static strings in local storage, which is threadsafe
         // to do but not understood by the analysis yet.
         / EmptyString\(\)/,
         /nsCSSProps::LookupPropertyValue/,
         /nsCSSProps::ValueToKeyword/,
         /nsCSSKeywords::GetStringValue/,
 
-        // The analysis can't cope with the indirection used for the objects
-        // being initialized here.
-        "Gecko_GetOrCreateKeyframeAtStart",
-        "Gecko_GetOrCreateInitialKeyframe",
-        "Gecko_GetOrCreateFinalKeyframe",
-        "Gecko_NewStyleQuoteValues",
-        "Gecko_NewCSSValueSharedList",
-        "Gecko_NewNoneTransform",
-        "Gecko_NewGridTemplateAreasValue",
-        /nsCSSValue::SetCalcValue/,
-        /CSSValueSerializeCalcOps::Append/,
-        "Gecko_CSSValue_SetFunction",
-        "Gecko_CSSValue_SetArray",
-        "Gecko_CSSValue_InitSharedList",
-        "Gecko_EnsureMozBorderColors",
-        "Gecko_ClearMozBorderColors",
-        "Gecko_AppendMozBorderColors",
-        "Gecko_CopyMozBorderColors",
-        "Gecko_SetNullImageValue",
+        // These could probably be handled by treating the scope of PSAutoLock
+        // aka BaseAutoLock<PSMutex> as threadsafe.
+        /profiler_register_thread/,
+        /profiler_unregister_thread/,
 
         // The analysis thinks we'll write to mBits in the DoGetStyleFoo<false>
         // call.  Maybe the template parameter confuses it?
         /nsStyleContext::PeekStyle/,
 
-        // Needs main thread assertions or other fixes.
-        /UndisplayedMap::GetEntryFor/,
+        // The analysis can't cope with the indirection used for the objects
+        // being initialized here, from nsCSSValue::Array::Create to the return
+        // value of the Item(i) getter.
+        /nsCSSValue::SetCalcValue/,
+
+        // Unable to analyze safety of linked list initialization.
+        "Gecko_NewCSSValueSharedList",
+        "Gecko_CSSValue_InitSharedList",
+
+        // Unable to trace through dataflow, but straightforward if inspected.
+        "Gecko_NewNoneTransform",
+
+        // Bug 1368922
+        "Gecko_UnsetDirtyStyleAttr",
+
+        // Bug 1400438
+        "Gecko_AppendMozBorderColors",
+
+        // Need main thread assertions or other fixes.
         /EffectCompositor::GetServoAnimationRule/,
         /LookAndFeel::GetColor/,
-        "Gecko_CopyStyleContentsFrom",
-        "Gecko_CSSValue_SetPixelValue",
-        "Gecko_UnsetDirtyStyleAttr",
-        /nsCSSPropertyIDSet::AddProperty/,
     ];
     if (entry.matches(whitelist))
         return true;
 
     if (entry.isSafeArgument(0)) {
         var heapWhitelist = [
             // Operations on heap structures pointed to by arrays and strings are
             // threadsafe as long as the array/string itself is threadsafe.
             /nsTArray_Impl.*?::AppendElement/,
             /nsTArray_Impl.*?::RemoveElementsAt/,
             /nsTArray_Impl.*?::ReplaceElementsAt/,
-            /nsTArray_Impl.*?::InsertElementsAt/,
+            /nsTArray_Impl.*?::InsertElementAt/,
             /nsTArray_Impl.*?::SetCapacity/,
+            /nsTArray_Impl.*?::SetLength/,
             /nsTArray_base.*?::EnsureCapacity/,
             /nsTArray_base.*?::ShiftData/,
             /AutoTArray.*?::Init/,
-            /nsTSubstring<T>::SetCapacity/,
-            /nsTSubstring<T>::SetLength/,
-            /nsTSubstring<T>::Assign/,
-            /nsTSubstring<T>::Append/,
-            /nsTSubstring<T>::Replace/,
-            /nsTSubstring<T>::Trim/,
-            /nsTSubstring<T>::Truncate/,
-            /nsTSubstring<T>::StripTaggedASCII/,
-            /nsTSubstring<T>::operator=/,
+            /(nsTSubstring<T>|nsAC?String)::SetCapacity/,
+            /(nsTSubstring<T>|nsAC?String)::SetLength/,
+            /(nsTSubstring<T>|nsAC?String)::Assign/,
+            /(nsTSubstring<T>|nsAC?String)::Append/,
+            /(nsTSubstring<T>|nsAC?String)::Replace/,
+            /(nsTSubstring<T>|nsAC?String)::Trim/,
+            /(nsTSubstring<T>|nsAC?String)::Truncate/,
+            /(nsTSubstring<T>|nsAC?String)::StripTaggedASCII/,
+            /(nsTSubstring<T>|nsAC?String)::operator=/,
             /nsTAutoStringN<T, N>::nsTAutoStringN/,
             /nsTFixedString<T>::nsTFixedString/,
 
             // Similar for some other data structures
             /nsCOMArray_base::SetCapacity/,
             /nsCOMArray_base::Clear/,
             /nsCOMArray_base::AppendElement/,
 
@@ -486,18 +542,18 @@ function ignoreContents(entry)
         ];
         if (entry.matches(firstArgWhitelist))
             return true;
     }
 
     if (entry.isSafeArgument(2)) {
         var secondArgWhitelist = [
             /nsStringBuffer::ToString/,
-            /AppendUTF8toUTF16/,
-            /AppendASCIItoUTF16/,
+            /AppendUTF\d+toUTF\d+/,
+            /AppendASCIItoUTF\d+/,
         ];
         if (entry.matches(secondArgWhitelist))
             return true;
     }
 
     return false;
 }
 
@@ -566,22 +622,22 @@ function isZero(exp)
 // pointer fields etc.) without concerns about thread safety. This includes
 // pointers to stack data, null pointers, and other data we know is thread
 // local, such as certain arguments to the root functions.
 //
 // Entries in the worklist keep track of the pointer arguments to the function
 // which are safe using a sorted array, so that this can be propagated down the
 // stack. Zero is |this|, and arguments are indexed starting at one.
 
-function WorklistEntry(name, safeArguments, stack)
+function WorklistEntry(name, safeArguments, stack, parameterNames)
 {
     this.name = name;
     this.safeArguments = safeArguments;
     this.stack = stack;
-    this.parameterNames = {};
+    this.parameterNames = parameterNames;
 }
 
 WorklistEntry.prototype.readable = function()
 {
     const [ mangled, readable ] = splitFunction(this.name);
     return readable;
 }
 
@@ -679,16 +735,22 @@ CallSite.prototype.safeString = function
 
 ///////////////////////////////////////////////////////////////////////////////
 // Analysis Core
 ///////////////////////////////////////////////////////////////////////////////
 
 var errorCount = 0;
 var errorLimit = 100;
 
+// We want to suppress output for functions that ended up not having any
+// hazards, for brevity of the final output. So each new toplevel function will
+// initialize this to a string, which should be printed only if an error is
+// seen.
+var errorHeader;
+
 var startTime = new Date;
 function elapsedTime()
 {
     var seconds = (new Date - startTime) / 1000;
     return "[" + seconds.toFixed(2) + "s] ";
 }
 
 var options = parse_options([
@@ -718,44 +780,50 @@ var removePrefix = add_trailing_slash(op
 var addPrefix = add_trailing_slash(options.add_prefix);
 
 if (options.verbose) {
     printErr(`Removing prefix ${removePrefix} from paths`);
     printErr(`Prepending ${addPrefix} to paths`);
 }
 
 print(elapsedTime() + "Loading types...");
-loadTypes('src_comp.xdb');
+if (os.getenv("TYPECACHE"))
+    loadTypesWithCache('src_comp.xdb', os.getenv("TYPECACHE"));
+else
+    loadTypes('src_comp.xdb');
 print(elapsedTime() + "Starting analysis...");
 
-var reachable = {};
-
 var xdb = xdbLibrary();
 xdb.open("src_body.xdb");
 
 var minStream = xdb.min_data_stream();
 var maxStream = xdb.max_data_stream();
+var roots = [];
 
-var roots = [];
-for (var bodyIndex = minStream; bodyIndex <= maxStream; bodyIndex++) {
-    var key = xdb.read_key(bodyIndex);
-    var name = key.readString();
-    if (/^Gecko_/.test(name)) {
-        var data = xdb.read_entry(key);
-        if (/ServoBindings.cpp/.test(data.readString()))
-            roots.push(name);
-        xdb.free_string(data);
+var [flag, arg] = scriptArgs;
+if (flag && (flag == '-f' || flag == '--function')) {
+    roots = [arg];
+} else {
+    for (var bodyIndex = minStream; bodyIndex <= maxStream; bodyIndex++) {
+        var key = xdb.read_key(bodyIndex);
+        var name = key.readString();
+        if (/^Gecko_/.test(name)) {
+            var data = xdb.read_entry(key);
+            if (/ServoBindings.cpp/.test(data.readString()))
+                roots.push(name);
+            xdb.free_string(data);
+        }
+        xdb.free_string(key);
     }
-    xdb.free_string(key);
 }
 
 print(elapsedTime() + "Found " + roots.length + " roots.");
 for (var i = 0; i < roots.length; i++) {
     var root = roots[i];
-    print(elapsedTime() + "#" + (i + 1) + " Analyzing " + root + " ...");
+    errorHeader = elapsedTime() + "#" + (i + 1) + " Analyzing " + root + " ...";
     try {
         processRoot(root);
     } catch (e) {
         if (e != "Error!")
             throw e;
     }
 }
 
@@ -766,18 +834,26 @@ var currentBody;
 // All local variable assignments we have seen in either the outer or inner
 // function. This crosses loop boundaries, and currently has an unsoundness
 // where later assignments in a loop are not taken into account.
 var assignments;
 
 // All loops in the current function which are reachable off main thread.
 var reachableLoops;
 
+// Functions that are reachable from the current root.
+var reachable = {};
+
 function dumpError(entry, location, text)
 {
+    if (errorHeader) {
+        print(errorHeader);
+        errorHeader = undefined;
+    }
+
     var stack = entry.stack;
     print("Error: " + text);
     print("Location: " + entry.name + (location ? " @ " + location : "") + stack[0].safeString());
     print("Stack Trace:");
     // Include the callers in the stack trace instead of the callees. Make sure
     // the dummy stack entry we added for the original roots is in place.
     assert(stack[stack.length - 1].location == null);
     for (var i = 0; i < stack.length - 1; i++)
@@ -798,51 +874,69 @@ function variableAssignRhs(edge)
     if (edge.Kind == "Assign" && edge.Exp[1].Kind == "Drf" && edge.Exp[1].Exp[0].Kind == "Var") {
         var variable = edge.Exp[1].Exp[0].Variable;
         if (isLocalVariable(variable))
             return variable;
     }
     return null;
 }
 
-function processAssign(entry, location, lhs, edge)
+function processAssign(body, entry, location, lhs, edge)
 {
     var fields;
     [lhs, fields] = stripFields(lhs);
 
     switch (lhs.Kind) {
       case "Var":
         var name = variableName(lhs.Variable);
         if (isLocalVariable(lhs.Variable)) {
             // Remember any assignments to local variables in this function.
             // Note that we ignore any points where the variable's address is
             // taken and indirect assignments might occur. This is an
             // unsoundness in the analysis.
 
+            let assign = [body, edge];
+
             // Chain assignments if the RHS has only been assigned once.
             var rhsVariable = variableAssignRhs(edge);
             if (rhsVariable) {
-                var rhsEdge = singleAssignment(variableName(rhsVariable));
-                if (rhsEdge)
-                    edge = rhsEdge;
+                var rhsAssign = singleAssignment(variableName(rhsVariable));
+                if (rhsAssign)
+                    assign = rhsAssign;
             }
 
             if (!(name in assignments))
                 assignments[name] = [];
-            assignments[name].push(edge);
+            assignments[name].push(assign);
         } else {
             checkVariableAssignment(entry, location, name);
         }
         return;
       case "Drf":
         var variable = null;
         if (lhs.Exp[0].Kind == "Var") {
             variable = lhs.Exp[0].Variable;
             if (isSafeVariable(entry, variable))
                 return;
+        } else if (lhs.Exp[0].Kind == "Fld") {
+            const {
+                Type: {Kind, Type: fieldType},
+                FieldCSU: {Type: {Kind: containerTypeKind,
+                                  Name: containerTypeName}}
+            } = lhs.Exp[0].Field;
+            const [containerExpr] = lhs.Exp[0].Exp;
+
+            if (containerTypeKind == 'CSU' &&
+                Kind == 'Pointer' &&
+                isEdgeSafeArgument(entry, containerExpr) &&
+                isSafeMemberPointer(containerTypeName, fieldType))
+            {
+                return;
+            }
+
         }
         if (fields.length)
             checkFieldWrite(entry, location, fields);
         else
             checkDereferenceWrite(entry, location, variableName(variable));
         return;
       case "Int":
         if (isZero(lhs)) {
@@ -892,17 +986,17 @@ function process(entry, body, addCallee)
 
         var location = get_location(body.PPoint[edge.Index[0] - 1].Location);
 
         var callees = getCallees(edge);
         for (var callee of callees) {
             switch (callee.kind) {
             case "direct":
                 var safeArguments = getEdgeSafeArguments(entry, edge, callee.name);
-                addCallee(new CallSite(callee.name, safeArguments, location, entry.parameterNames));
+                addCallee(new CallSite(callee.name, safeArguments, location, {}));
                 break;
               case "resolved-field":
                 break;
               case "field":
                 var field = callee.csu + "." + callee.field;
                 if (callee.isVirtual)
                     checkOverridableVirtualCall(entry, location, field);
                 else
@@ -916,21 +1010,21 @@ function process(entry, body, addCallee)
                 break;
             }
         }
 
         var fallthrough = true;
 
         if (edge.Kind == "Assign") {
             assert(edge.Exp.length == 2);
-            processAssign(entry, location, edge.Exp[0], edge);
+            processAssign(body, entry, location, edge.Exp[0], edge);
         } else if (edge.Kind == "Call") {
             assert(edge.Exp.length <= 2);
             if (edge.Exp.length == 2)
-                processAssign(entry, location, edge.Exp[1], edge);
+                processAssign(body, entry, location, edge.Exp[1], edge);
 
             // Treat assertion failures as if they don't return, so that
             // asserting NS_IsMainThread() is sufficient to prevent the
             // analysis from considering a block of code.
             if (isDirectCall(edge, /MOZ_ReportAssertionFailure/))
                 fallthrough = false;
         } else if (edge.Kind == "Loop") {
             reachableLoops[edge.BlockId.Loop] = true;
@@ -988,26 +1082,34 @@ function maybeProcessMissingFunction(ent
     }
 
     return false;
 }
 
 function processRoot(name)
 {
     var safeArguments = [];
-    var worklist = [new WorklistEntry(name, safeArguments, [new CallSite(name, safeArguments, null, {})])];
+    var parameterNames = {};
+    var worklist = [new WorklistEntry(name, safeArguments, [new CallSite(name, safeArguments, null, parameterNames)], parameterNames)];
+
+    reachable = {};
+
+    var armed = false;
+    if (name.includes("Gecko_CSSValue_Set"))
+        armed = true;
 
     while (worklist.length > 0) {
         var entry = worklist.pop();
 
         // In principle we would be better off doing a meet-over-paths here to get
         // the common subset of arguments which are safe to write through. However,
         // analyzing functions separately for each subset if simpler, ensures that
         // the stack traces we produce accurately characterize the stack arguments,
         // and should be fast enough for now.
+
         if (entry.mangledName() in reachable)
             continue;
         reachable[entry.mangledName()] = true;
 
         if (ignoreContents(entry))
             continue;
 
         var data = xdb.read_entry(entry.name);
@@ -1029,17 +1131,17 @@ function processRoot(name)
             if (!maybeProcessMissingFunction(entry, Array.prototype.push.bind(callees)))
                 checkExternalFunction(entry);
         }
         xdb.free_string(data);
 
         for (var callee of callees) {
             if (!ignoreCallEdge(entry, callee.callee)) {
                 var nstack = [callee, ...entry.stack];
-                worklist.push(new WorklistEntry(callee.callee, callee.safeArguments, nstack));
+                worklist.push(new WorklistEntry(callee.callee, callee.safeArguments, nstack, callee.parameterNames));
             }
         }
     }
 }
 
 function isEdgeSafeArgument(entry, exp)
 {
     var fields;
@@ -1080,35 +1182,53 @@ function singleAssignment(name)
         var edges = assignments[name];
         if (edges.length == 1)
             return edges[0];
     }
     return null;
 }
 
 function expressionValueEdge(exp) {
-    if (exp.Kind == "Var" && exp.Variable.Kind == "Temp")
-        return singleAssignment(variableName(exp.Variable));
-    return null;
+    if (!(exp.Kind == "Var" && exp.Variable.Kind == "Temp"))
+        return null;
+    const assign = singleAssignment(variableName(exp.Variable));
+    if (!assign)
+        return null;
+    const [body, edge] = assign;
+    return edge;
 }
 
 function isSafeVariable(entry, variable)
 {
     var index = safeArgumentIndex(variable);
     if (index >= 0)
         return entry.isSafeArgument(index);
 
     if (variable.Kind != "Temp" && variable.Kind != "Local")
         return false;
     var name = variableName(variable);
 
+    if (!entry.safeLocals)
+        entry.safeLocals = new Map;
+    if (entry.safeLocals.has(name))
+        return entry.safeLocals.get(name);
+
+    const safe = isSafeLocalVariable(entry, name);
+    entry.safeLocals.set(name, safe);
+    return safe;
+}
+
+function isSafeLocalVariable(entry, name)
+{
     // If there is a single place where this variable has been assigned on
     // edges we are considering, look at that edge.
-    var edge = singleAssignment(name);
-    if (edge) {
+    var assign = singleAssignment(name);
+    if (assign) {
+        const [body, edge] = assign;
+
         // Treat temporary pointers to DebugOnly contents as thread local.
         if (isDirectCall(edge, /DebugOnly.*?::operator/))
             return true;
 
         // Treat heap allocated pointers as thread local during construction.
         // Hopefully the construction code doesn't leak pointers to the object
         // to places where other threads might access it.
         if (isDirectCall(edge, /operator new/) ||
@@ -1116,33 +1236,42 @@ function isSafeVariable(entry, variable)
         {
             return true;
         }
 
         if ("PEdgeCallInstance" in edge) {
             // References to the contents of an array are threadsafe if the array
             // itself is threadsafe.
             if ((isDirectCall(edge, /operator\[\]/) ||
+                 isDirectCall(edge, /nsTArray.*?::InsertElementAt\b/) ||
                  isDirectCall(edge, /nsStyleContent::ContentAt/)) &&
                 isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp))
             {
                 return true;
             }
 
-            // Watch for the coerced result of a getter_AddRefs call.
+            // Watch for the coerced result of a getter_AddRefs or getter_Copies call.
             if (isDirectCall(edge, /operator /)) {
                 var otherEdge = expressionValueEdge(edge.PEdgeCallInstance.Exp);
                 if (otherEdge &&
-                    isDirectCall(otherEdge, /getter_AddRefs/) &&
+                    isDirectCall(otherEdge, /getter_(?:AddRefs|Copies)/) &&
                     isEdgeSafeArgument(entry, otherEdge.PEdgeCallArguments.Exp[0]))
                 {
                     return true;
                 }
             }
 
+            // RefPtr::operator->() and operator* transmit the safety of the
+            // RefPtr to the return value.
+            if (isDirectCall(edge, /RefPtr<.*?>::operator(->|\*)\(\)/) &&
+                isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp))
+            {
+                return true;
+            }
+
             // Placement-new returns a pointer that is as safe as the pointer
             // passed to it. Exp[0] is the size, Exp[1] is the pointer/address.
             // Note that the invocation of the constructor is a separate call,
             // and so need not be considered here.
             if (isDirectCall(edge, /operator new/) &&
                 edge.PEdgeCallInstance.Exp.length == 2 &&
                 isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp[1]))
             {
@@ -1150,18 +1279,41 @@ function isSafeVariable(entry, variable)
             }
 
             // Coercion via AsAString preserves safety.
             if (isDirectCall(edge, /AsAString/) &&
                 isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp))
             {
                 return true;
             }
+
+            // Special case:
+            //
+            //   keyframe->mTimingFunction.emplace()
+            //   keyframe->mTimingFunction->Init()
+            //
+            // The object calling Init should be considered safe here because
+            // we just emplaced it, though in general keyframe::operator->
+            // could do something crazy.
+            if (isDirectCall(edge, /operator->/)) do {
+                const predges = getPredecessors(body)[edge.Index[0]];
+                if (!predges || predges.length != 1)
+                    break;
+                const predge = predges[0];
+                if (!isDirectCall(predge, /\bemplace\b/))
+                    break;
+                const instance = predge.PEdgeCallInstance;
+                if (JSON.stringify(instance) == JSON.stringify(edge.PEdgeCallInstance))
+                    return true;
+            } while (false);
         }
 
+        if (isSafeAssignment(entry, edge, name))
+            return true;
+
         // Watch out for variables which were assigned arguments.
         var rhsVariable = variableAssignRhs(edge);
         if (rhsVariable)
             return isSafeVariable(entry, rhsVariable);
     }
 
     // When temporary stack structures are created (either to return or to call
     // methods on without assigning them a name), the generated sixgill JSON is
@@ -1184,16 +1336,36 @@ function isSafeVariable(entry, variable)
     // If it is initialized at this point we should have seen *some* write
     // already, since the CFG edges are visited in reverse post order.
     if (name in assignments)
         return false;
 
     return true;
 }
 
+function isSafeMemberPointer(containerType, memberType)
+{
+    if (memberType.Kind != 'Pointer')
+        return false;
+
+    const {Type: {Kind: pointeeKind, Name: pointeeTypeName}} = memberType;
+
+    // nsStyleBorder has a member mBorderColors of type nsBorderColors**. It is
+    // lazily initialized to an array of 4 nsBorderColors, and should inherit
+    // the safety of its container.
+    if (containerType == 'nsStyleBorder' &&
+        pointeeKind == 'CSU' &&
+        pointeeTypeName == 'nsBorderColors')
+    {
+        return true;
+    }
+
+    return false;
+}
+
 // Return whether 'exp == value' holds only when execution is on the main thread.
 function testFailsOffMainThread(exp, value) {
     switch (exp.Kind) {
       case "Drf":
         var edge = expressionValueEdge(exp.Exp[0]);
         if (edge) {
             if (isDirectCall(edge, /NS_IsMainThread/) && value)
                 return true;
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -62,16 +62,18 @@ var ignoreClasses = {
     "_MD_IOVector" : true,
     "malloc_table_t": true, // replace_malloc
     "malloc_hook_table_t": true, // replace_malloc
 };
 
 // Ignore calls through TYPE.FIELD, where TYPE is the class or struct name containing
 // a function pointer field named FIELD.
 var ignoreCallees = {
+    "js::Class.trace" : true,
+    "js::Class.finalize" : true,
     "js::ClassOps.trace" : true,
     "js::ClassOps.finalize" : true,
     "JSRuntime.destroyPrincipals" : true,
     "icu_50::UObject.__deleting_dtor" : true, // destructors in ICU code can't cause GC
     "mozilla::CycleCollectedJSRuntime.DescribeCustomObjects" : true, // During tracing, cannot GC.
     "mozilla::CycleCollectedJSRuntime.NoteCustomGCThingXPCOMChildren" : true, // During tracing, cannot GC.
     "PLDHashTableOps.hashKey" : true,
     "z_stream_s.zfree" : true,
--- a/js/src/devtools/rootAnalysis/callgraph.js
+++ b/js/src/devtools/rootAnalysis/callgraph.js
@@ -211,8 +211,25 @@ function loadTypes(type_xdb_filename) {
         const data = xdb.read_entry(csu);
         const json = JSON.parse(data.readString());
         processCSU(csu.readString(), json[0]);
 
         xdb.free_string(csu);
         xdb.free_string(data);
     }
 }
+
+function loadTypesWithCache(type_xdb_filename, cache_filename) {
+    try {
+        const cacheAB = os.file.readFile(cache_filename, "binary");
+        const cb = serialize();
+        cb.clonebuffer = cacheAB.buffer;
+        const cacheData = deserialize(cb);
+        subclasses = cacheData.subclasses;
+        superclasses = cacheData.superclasses;
+        classFunctions = cacheData.classFunctions;
+    } catch (e) {
+        loadTypes(type_xdb_filename);
+        const cb = serialize({subclasses, superclasses, classFunctions});
+        os.file.writeTypedArrayToFile(cache_filename,
+                                      new Uint8Array(cb.arraybuffer));
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/src/devtools/rootAnalysis/dumpCFG.js
@@ -0,0 +1,259 @@
+// const cfg = loadCFG(scriptArgs[0]);
+// dump_CFG(cfg);
+
+function loadCFG(filename) {
+  const data = os.file.readFile(filename);
+  return JSON.parse(data);
+}
+
+function dump_CFG(cfg) {
+  for (const body of cfg)
+    dump_body(body);
+}
+
+function dump_body(body, src, dst) {
+  const {BlockId,Command,DefineVariable,Index,Location,PEdge,PPoint,Version} = body;
+
+  const [mangled, unmangled] = splitFunction(BlockId.Variable.Name[0]);
+  print(`${unmangled} at ${Location[0].CacheString}:${Location[0].Line}`);
+
+  if (src === undefined) {
+    for (const def of DefineVariable)
+      print(str_definition(def));
+    print("");
+  }
+
+  for (const edge of PEdge) {
+    if (src === undefined || edge.Index[0] == src) {
+      if (dst == undefined || edge.Index[1] == dst)
+        print(str_edge(edge, body));
+    }
+  }
+}
+
+function str_definition(def) {
+  const {Type, Variable} = def;
+  return `define ${str_Variable(Variable)} : ${str_Type(Type)}`;
+}
+
+function badFormat(what, val) {
+  printErr("Bad format of " + what + ": " + JSON.stringify(val, null, 4));
+  printErr((new Error).stack);
+}
+
+function str_Variable(variable) {
+  if (variable.Kind == 'Return')
+    return '<returnval>';
+  else if (variable.Kind == 'This')
+    return 'this';
+
+  try {
+    return variable.Name[1];
+  } catch(e) {
+    badFormat("variable", variable);
+  }
+}
+
+function str_Type(type) {
+  try {
+    const {Kind, Type, Name, TypeFunctionArguments} = type;
+    if (Kind == 'Pointer')
+      return str_Type(Type) + "*";
+    else if (Kind == 'CSU') {
+      return Name;
+    }
+
+    return Kind;
+  } catch(e) {
+    badFormat("type", type);
+  }
+}
+
+var OpCodeNames = {
+  'LessEqual': ['<=', '>'],
+  'LessThan': ['<', '>='],
+  'GreaterEqual': ['>=', '<'],
+  'Greater': ['>', '<='],
+  'Plus': '+',
+  'Minus': '-',
+};
+
+function opcode_name(opcode, invert) {
+  if (opcode in OpCodeNames) {
+    const name = OpCodeNames[opcode];
+    if (invert === undefined)
+      return name;
+    return name[invert ? 1 : 0];
+  } else {
+    if (invert === undefined)
+      return opcode;
+    return (invert ? '!' : '') + opcode;
+  }
+}
+
+function str_value(val, env, options) {
+  const {Kind, Variable, String, Exp} = val;
+  if (Kind == 'Var')
+    return str_Variable(Variable);
+  else if (Kind == 'Drf') {
+    // Suppress the vtable lookup dereference
+    if (Exp[0].Kind == 'Fld' && "FieldInstanceFunction" in Exp[0].Field)
+      return str_value(Exp[0], env);
+    const exp = str_value(Exp[0], env);
+    if (options && options.noderef)
+      return exp;
+    return "*" + exp;
+  } else if (Kind == 'Fld') {
+    const {Exp, Field} = val;
+    const name = Field.Name[0];
+    if ("FieldInstanceFunction" in Field) {
+      return Field.FieldCSU.Type.Name + "::" + name;
+    }
+    const container = str_value(Exp[0]);
+    if (container.startsWith("*"))
+      return container.substring(1) + "->" + name;
+    return container + "." + name;
+  } else if (Kind == 'Empty') {
+    return '<unknown>';
+  } else if (Kind == 'Binop') {
+    const {OpCode} = val;
+    const op = opcode_name(OpCode);
+    return `${str_value(Exp[0], env)} ${op} ${str_value(Exp[1], env)}`;
+  } else if (Kind == 'Unop') {
+    const exp = str_value(Exp[0], env);
+    const {OpCode} = val;
+    if (OpCode == 'LogicalNot')
+      return `not ${exp}`;
+    return `${OpCode}(${exp})`;
+  } else if (Kind == 'Index') {
+    const index = str_value(Exp[1], env);
+    if (Exp[0].Kind == 'Drf')
+      return `${str_value(Exp[0], env, {noderef:true})}[${index}]`;
+    else
+      return `&${str_value(Exp[0], env)}[${index}]`;
+  } else if (Kind == 'NullTest') {
+    return `nullptr == ${str_value(Exp[0], env)}`;
+  } else if (Kind == "String") {
+    return '"' + String + '"';
+  } else if (String !== undefined) {
+    return String;
+  }
+  badFormat("value", val);
+}
+
+function str_thiscall_Exp(exp) {
+  return exp.Kind == 'Drf' ? str_value(exp.Exp[0]) + "->" : str_value(exp) + ".";
+}
+
+function stripcsu(s) {
+    return s.replace("class ", "").replace("struct ", "").replace("union ");
+}
+
+function str_call(prefix, edge, env) {
+  const {Exp, Type, PEdgeCallArguments, PEdgeCallInstance} = edge;
+  const {Kind, Type:cType, TypeFunctionArguments, TypeFunctionCSU} = Type;
+
+  if (Kind == 'Function') {
+    const params = PEdgeCallArguments ? PEdgeCallArguments.Exp : [];
+    const strParams = params.map(str_value);
+
+    let func;
+    let comment = "";
+    let assign_exp;
+    if (PEdgeCallInstance) {
+      const csu = TypeFunctionCSU.Type.Name;
+      const method = str_value(Exp[0], env);
+
+      // Heuristic to only display the csu for constructors
+      if (csu.includes(method)) {
+        func = stripcsu(csu) + "::" + method;
+      } else {
+        func = method;
+        comment = "# " + csu + "::" + method + "\n";
+      }
+
+      const {Exp: thisExp} = PEdgeCallInstance;
+      func = str_thiscall_Exp(thisExp) + func;
+    } else {
+      func = str_value(Exp[0]);
+    }
+    assign_exp = Exp[1];
+
+    let assign = "";
+    if (assign_exp) {
+      assign = str_value(assign_exp) + " := ";
+    }
+    return `${comment}${prefix} Call ${assign}${func}(${strParams.join(", ")})`;
+  }
+
+  print(JSON.stringify(edge, null, 4));
+  throw "unhandled format error";
+}
+
+function str_assign(prefix, edge) {
+  const {Exp} = edge;
+  const [lhs, rhs] = Exp;
+  return `${prefix} Assign ${str_value(lhs)} := ${str_value(rhs)}`;
+}
+
+function str_loop(prefix, edge) {
+  const {BlockId: {Loop}} = edge;
+  return `${prefix} Loop ${Loop}`;
+}
+
+function str_assume(prefix, edge) {
+  const {Exp, PEdgeAssumeNonZero} = edge;
+  const cmp = PEdgeAssumeNonZero ? "" : "!";
+
+  const {Exp: aExp, Kind, OpCode} = Exp[0];
+  if (Kind == 'Binop') {
+    const [lhs, rhs] = aExp;
+    const op = opcode_name(OpCode, !PEdgeAssumeNonZero);
+    return `${prefix} Assume ${str_value(lhs)} ${op} ${str_value(rhs)}`;
+  } else if (Kind == 'Unop') {
+    return `${prefix} Assume ${cmp}${OpCode} ${str_value(aExp[0])}`;
+  } else if (Kind == 'NullTest') {
+    return `${prefix} Assume nullptr ${cmp}== ${str_value(aExp[0])}`;
+  } else if (Kind == 'Drf') {
+    return `${prefix} Assume ${cmp}${str_value(Exp[0])}`;
+  }
+
+  print(JSON.stringify(edge, null, 4));
+  throw "unhandled format error";
+}
+
+function str_edge(edge, env) {
+  const {Index, Kind} = edge;
+  const [src, dst] = Index;
+  const prefix = `[${src},${dst}]`;
+
+  if (Kind == "Call")
+    return str_call(prefix, edge, env);
+  if (Kind == 'Assign')
+    return str_assign(prefix, edge);
+  if (Kind == 'Assume')
+    return str_assume(prefix, edge);
+  if (Kind == 'Loop')
+    return str_loop(prefix, edge);
+
+  print(JSON.stringify(edge, null, 4));
+  throw "unhandled edge type";
+}
+
+function str(unknown) {
+  if ("Index" in unknown) {
+    return str_edge(unknown);
+  } else if ("Kind" in unknown) {
+    if ("BlockId" in unknown)
+      return str_Variable(unknown);
+    return str_value(unknown);
+  } else if ("Type" in unknown) {
+    return str_Type(unknown);
+  }
+  return "unknown";
+}
+
+function jdump(x) {
+  print(JSON.stringify(x, null, 4));
+  quit(0);
+}
--- a/js/src/devtools/rootAnalysis/run-test.py
+++ b/js/src/devtools/rootAnalysis/run-test.py
@@ -1,9 +1,9 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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/.
 
 import os
 import site
 import subprocess
 import argparse
@@ -76,14 +76,19 @@ for name in cfg.tests:
     name = os.path.basename(name)
     indir = os.path.join(testdir, name)
     outdir = os.path.join(testdir, 'out', name)
     try:
         os.mkdir(outdir)
     except OSError:
         pass
 
-    test = Test(indir, outdir, cfg)
+    test = Test(indir, outdir, cfg, verbose=cfg.verbose)
 
     os.chdir(outdir)
     subprocess.call(["sh", "-c", "rm *.xdb"])
-    execfile(os.path.join(indir, "test.py"), {'test': test, 'equal': equal})
+    if cfg.verbose:
+        print("Running test %s" % name)
+    testpath = os.path.join(indir, "test.py")
+    testscript = open(testpath).read()
+    testcode = compile(testscript, testpath, 'exec')
+    exec(testcode, {'test': test, 'equal': equal})
     print("TEST-PASSED: %s" % name)
--- a/js/src/devtools/rootAnalysis/t/suppression/test.py
+++ b/js/src/devtools/rootAnalysis/t/suppression/test.py
@@ -4,19 +4,19 @@ test.run_analysis_script('gcTypes', upto
 # The suppressions file uses only mangled names since it's for internal use,
 # though I may change that soon given (1) the unfortunate non-uniqueness of
 # mangled constructor names, and (2) the usefulness of this file for
 # mrgiggles's reporting.
 suppressed = test.load_suppressed_functions()
 
 # Only one of these is fully suppressed (ie, *always* called within the scope
 # of an AutoSuppressGC).
-assert(len(filter(lambda f: 'suppressedFunction' in f, suppressed)) == 1)
-assert(len(filter(lambda f: 'halfSuppressedFunction' in f, suppressed)) == 0)
-assert(len(filter(lambda f: 'unsuppressedFunction' in f, suppressed)) == 0)
+assert(len(list(filter(lambda f: 'suppressedFunction' in f, suppressed))) == 1)
+assert(len(list(filter(lambda f: 'halfSuppressedFunction' in f, suppressed))) == 0)
+assert(len(list(filter(lambda f: 'unsuppressedFunction' in f, suppressed))) == 0)
 
 # gcFunctions should be the inverse, but we get to rely on unmangled names here.
 gcFunctions = test.load_gcFunctions()
 print(gcFunctions)
 assert('void GC()' in gcFunctions)
 assert('void suppressedFunction()' not in gcFunctions)
 assert('void halfSuppressedFunction()' in gcFunctions)
 assert('void unsuppressedFunction()' in gcFunctions)
--- a/js/src/devtools/rootAnalysis/t/testlib.py
+++ b/js/src/devtools/rootAnalysis/t/testlib.py
@@ -5,30 +5,30 @@ import subprocess
 
 from sixgill import Body
 from collections import defaultdict, namedtuple
 
 scriptdir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
 
 HazardSummary = namedtuple('HazardSummary', ['function', 'variable', 'type', 'GCFunction', 'location'])
 
-
 def equal(got, expected):
     if got != expected:
         print("Got '%s', expected '%s'" % (got, expected))
 
 def extract_unmangled(func):
     return func.split('$')[-1]
 
 
 class Test(object):
-    def __init__(self, indir, outdir, cfg):
+    def __init__(self, indir, outdir, cfg, verbose=0):
         self.indir = indir
         self.outdir = outdir
         self.cfg = cfg
+        self.verbose = verbose
 
     def infile(self, path):
         return os.path.join(self.indir, path)
 
     def binpath(self, prog):
         return os.path.join(self.cfg.sixgill_bin, prog)
 
     def compile(self, source, options = ''):
@@ -39,34 +39,36 @@ class Test(object):
         if self.cfg.verbose:
             print("Running %s" % cmd)
         subprocess.check_call(["sh", "-c", cmd])
 
     def load_db_entry(self, dbname, pattern):
         '''Look up an entry from an XDB database file, 'pattern' may be an exact
         matching string, or an re pattern object matching a single entry.'''
 
-        if not isinstance(pattern, basestring):
-            output = subprocess.check_output([self.binpath("xdbkeys"), dbname + ".xdb"])
-            matches = filter(lambda _: re.search(pattern, _), output.splitlines())
+        if hasattr(pattern, 'match'):
+            output = subprocess.check_output([self.binpath("xdbkeys"), dbname + ".xdb"],
+                                             universal_newlines=True)
+            matches = list(filter(lambda _: re.search(pattern, _), output.splitlines()))
             if len(matches) == 0:
                 raise Exception("entry not found")
             if len(matches) > 1:
                 raise Exception("multiple entries found")
             pattern = matches[0]
 
-        output = subprocess.check_output([self.binpath("xdbfind"), "-json", dbname + ".xdb", pattern])
+        output = subprocess.check_output([self.binpath("xdbfind"), "-json", dbname + ".xdb", pattern],
+                                         universal_newlines=True)
         return json.loads(output)
 
     def run_analysis_script(self, phase, upto=None):
-        file("defaults.py", "w").write('''\
+        open("defaults.py", "w").write('''\
 analysis_scriptdir = '{scriptdir}'
 sixgill_bin = '{bindir}'
 '''.format(scriptdir=scriptdir, bindir=self.cfg.sixgill_bin))
-        cmd = [os.path.join(scriptdir, "analyze.py"), phase]
+        cmd = [os.path.join(scriptdir, "analyze.py"), '-v' if self.verbose else '-q', phase]
         if upto:
             cmd += ["--upto", upto]
         cmd.append("--source=%s" % self.indir)
         cmd.append("--objdir=%s" % self.outdir)
         cmd.append("--js=%s" % self.cfg.js)
         if self.cfg.verbose:
             cmd.append("--verbose")
             print("Running " + " ".join(cmd))
@@ -75,18 +77,18 @@ sixgill_bin = '{bindir}'
     def computeGCTypes(self):
         self.run_analysis_script("gcTypes", upto="gcTypes")
 
     def computeHazards(self):
         self.run_analysis_script("gcTypes")
 
     def load_text_file(self, filename, extract=lambda l: l):
         fullpath = os.path.join(self.outdir, filename)
-        values = (extract(line.strip()) for line in file(fullpath))
-        return filter(lambda _: _ is not None, values)
+        values = (extract(line.strip()) for line in open(fullpath, "r"))
+        return list(filter(lambda _: _ is not None, values))
 
     def load_suppressed_functions(self):
         return set(self.load_text_file("suppressedFunctions.lst"))
 
     def load_gcTypes(self):
         def grab_type(line):
             m = re.match(r'^(GC\w+): (.*)', line)
             if m:
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -1102,16 +1102,19 @@ static_assert(sizeof(Chunk) == ChunkSize
 static_assert(js::gc::ChunkMarkBitmapOffset == offsetof(Chunk, bitmap),
               "The hardcoded API bitmap offset must match the actual offset.");
 static_assert(js::gc::ChunkRuntimeOffset == offsetof(Chunk, trailer) +
                                             offsetof(ChunkTrailer, runtime),
               "The hardcoded API runtime offset must match the actual offset.");
 static_assert(js::gc::ChunkLocationOffset == offsetof(Chunk, trailer) +
                                              offsetof(ChunkTrailer, location),
               "The hardcoded API location offset must match the actual offset.");
+static_assert(js::gc::ChunkStoreBufferOffset == offsetof(Chunk, trailer) +
+                                                offsetof(ChunkTrailer, storeBuffer),
+              "The hardcoded API storeBuffer offset must match the actual offset.");
 
 /*
  * Tracks the used sizes for owned heap data and automatically maintains the
  * memory usage relationship between GCRuntime and Zones.
  */
 class HeapUsage
 {
     /*
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -2971,33 +2971,20 @@ js::TenuringTracer::moveObjectToTenured(
         NativeObject* nsrc = &src->as<NativeObject>();
         tenuredSize += moveSlotsToTenured(ndst, nsrc, dstKind);
         tenuredSize += moveElementsToTenured(ndst, nsrc, dstKind);
 
         // There is a pointer into a dictionary mode object from the head of its
         // shape list. This is updated in Nursery::sweepDictionaryModeObjects().
     }
 
-    if (src->is<InlineTypedObject>()) {
-        InlineTypedObject::objectMovedDuringMinorGC(this, dst, src);
-    } else if (src->is<TypedArrayObject>()) {
-        tenuredSize += TypedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
-    } else if (src->is<UnboxedArrayObject>()) {
-        tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
-    } else if (src->is<ArgumentsObject>()) {
-        tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src);
-    } else if (src->is<ProxyObject>()) {
-        // Objects in the nursery are never swapped so the proxy must have an
-        // inline ProxyValueArray.
-        MOZ_ASSERT(src->as<ProxyObject>().usingInlineValueArray());
-        dst->as<ProxyObject>().setInlineValueArray();
-        if (JSObjectMovedOp op = dst->getClass()->extObjectMovedOp())
-            op(dst, src);
-    } else if (JSObjectMovedOp op = dst->getClass()->extObjectMovedOp()) {
-        op(dst, src);
+    JSObjectMovedOp op = dst->getClass()->extObjectMovedOp();
+    MOZ_ASSERT_IF(src->is<ProxyObject>(), op == proxy_ObjectMoved);
+    if (op) {
+        tenuredSize += op(dst, src);
     } else {
         MOZ_ASSERT_IF(src->getClass()->hasFinalize(),
                       CanNurseryAllocateFinalizedClass(src->getClass()));
     }
 
     return tenuredSize;
 }
 
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -212,17 +212,22 @@ class Nursery
      * returns false and leaves |*ref| unset.
      */
     MOZ_ALWAYS_INLINE MOZ_MUST_USE static bool getForwardedPointer(JSObject** ref);
 
     /* Forward a slots/elements pointer stored in an Ion frame. */
     void forwardBufferPointer(HeapSlot** pSlotsElems);
 
     void maybeSetForwardingPointer(JSTracer* trc, void* oldData, void* newData, bool direct) {
-        if (trc->isTenuringTracer() && isInside(oldData))
+        if (trc->isTenuringTracer())
+            setForwardingPointerWhileTenuring(oldData, newData, direct);
+    }
+
+    void setForwardingPointerWhileTenuring(void* oldData, void* newData, bool direct) {
+        if (isInside(oldData))
             setForwardingPointer(oldData, newData, direct);
     }
 
     /* Mark a malloced buffer as no longer needing to be freed. */
     void removeMallocedBuffer(void* buffer) {
         mallocedBuffers.remove(buffer);
     }
 
--- a/js/src/gc/Policy.h
+++ b/js/src/gc/Policy.h
@@ -121,16 +121,19 @@ struct InternalGCPointerPolicy {
     using Type = typename mozilla::RemovePointer<T>::Type;
     static T initial() { return nullptr; }
     static void preBarrier(T v) { Type::writeBarrierPre(v); }
     static void postBarrier(T* vp, T prev, T next) { Type::writeBarrierPost(vp, prev, next); }
     static void readBarrier(T v) { Type::readBarrier(v); }
     static void trace(JSTracer* trc, T* vp, const char* name) {
         TraceManuallyBarrieredEdge(trc, vp, name);
     }
+    static bool isValid(T v) {
+        return gc::IsCellPointerValid(v);
+    }
 };
 
 } // namespace js
 
 namespace JS {
 
 #define DEFINE_INTERNAL_GC_POLICY(type) \
     template <> struct GCPolicy<type> : public js::InternalGCPointerPolicy<type> {};
--- a/js/src/irregexp/RegExpMacroAssembler.cpp
+++ b/js/src/irregexp/RegExpMacroAssembler.cpp
@@ -35,16 +35,18 @@
 using namespace js;
 using namespace js::irregexp;
 
 template <typename CharT>
 int
 irregexp::CaseInsensitiveCompareStrings(const CharT* substring1, const CharT* substring2,
 					size_t byteLength)
 {
+    AutoUnsafeCallWithABI unsafe;
+
     MOZ_ASSERT(byteLength % sizeof(CharT) == 0);
     size_t length = byteLength / sizeof(CharT);
 
     for (size_t i = 0; i < length; i++) {
         char16_t c1 = substring1[i];
         char16_t c2 = substring2[i];
         if (c1 != c2) {
             c1 = unicode::ToLowerCase(c1);
@@ -65,16 +67,18 @@ template int
 irregexp::CaseInsensitiveCompareStrings(const char16_t* substring1, const char16_t* substring2,
 					size_t byteLength);
 
 template <typename CharT>
 int
 irregexp::CaseInsensitiveCompareUCStrings(const CharT* substring1, const CharT* substring2,
                                           size_t byteLength)
 {
+    AutoUnsafeCallWithABI unsafe;
+
     MOZ_ASSERT(byteLength % sizeof(CharT) == 0);
     size_t length = byteLength / sizeof(CharT);
 
     for (size_t i = 0; i < length; i++) {
         char16_t c1 = substring1[i];
         char16_t c2 = substring2[i];
         if (c1 != c2) {
             c1 = unicode::FoldCase(c1);
--- a/js/src/irregexp/RegExpStack.cpp
+++ b/js/src/irregexp/RegExpStack.cpp
@@ -42,16 +42,17 @@ RegExpStackScope::RegExpStackScope(JSCon
 RegExpStackScope::~RegExpStackScope()
 {
     regexp_stack->reset();
 }
 
 int
 irregexp::GrowBacktrackStack(JSRuntime* rt)
 {
+    AutoUnsafeCallWithABI unsafe;
     return TlsContext.get()->regexpStack.ref().grow();
 }
 
 RegExpStack::RegExpStack()
   : base_(nullptr), size(0), limit_(nullptr)
 {}
 
 RegExpStack::~RegExpStack()
--- a/js/src/jit-test/tests/wasm/integer.js
+++ b/js/src/jit-test/tests/wasm/integer.js
@@ -148,16 +148,21 @@ testComparison32('lt_s', 40, 40, 0);
 testComparison32('lt_u', 40, 40, 0);
 testComparison32('le_s', 40, 40, 1);
 testComparison32('le_u', 40, 40, 1);
 testComparison32('gt_s', 40, 40, 0);
 testComparison32('gt_u', 40, 40, 0);
 testComparison32('ge_s', 40, 40, 1);
 testComparison32('ge_u', 40, 40, 1);
 
+// On 32-bit debug builds, with --ion-eager, this test can run into our
+// per-process JIT code limits and OOM. Trigger a GC to discard code.
+if (getJitCompilerOptions()["ion.warmup.trigger"] === 0)
+    gc();
+
 // Test MTest's GVN branch inversion.
 var testTrunc = wasmEvalText(`(module (func (param f32) (result i32) (if i32 (i32.eqz (i32.trunc_s/f32 (get_local 0))) (i32.const 0) (i32.const 1))) (export "" 0))`).exports[""];
 assertEq(testTrunc(0), 0);
 assertEq(testTrunc(13.37), 1);
 
 {
     setJitCompilerOption('wasm.test-mode', 1);
 
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -24,16 +24,18 @@
 using namespace js;
 using namespace js::jit;
 
 using mozilla::IsInRange;
 
 uint32_t
 jit::Bailout(BailoutStack* sp, BaselineBailoutInfo** bailoutInfo)
 {
+    AutoUnsafeCallWithABI unsafe;
+
     JSContext* cx = TlsContext.get();
     MOZ_ASSERT(bailoutInfo);
 
     // We don't have an exit frame.
     MOZ_ASSERT(IsInRange(FAKE_EXITFP_FOR_BAILOUT, 0, 0x1000) &&
                IsInRange(FAKE_EXITFP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000),
                "Fake exitfp pointer should be within the first page.");
 
@@ -99,16 +101,18 @@ jit::Bailout(BailoutStack* sp, BaselineB
 
     return retval;
 }
 
 uint32_t
 jit::InvalidationBailout(InvalidationBailoutStack* sp, size_t* frameSizeOut,
                          BaselineBailoutInfo** bailoutInfo)
 {
+    AutoUnsafeCallWithABI unsafe;
+
     sp->checkInvariants();
 
     JSContext* cx = TlsContext.get();
 
     // We don't have an exit frame.
     cx->activation()->asJit()->setExitFP(FAKE_EXITFP_FOR_BAILOUT);
 
     JitActivationIterator jitActivations(cx);
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -983,16 +983,17 @@ EmitBranchIsReturningFromCallVM(MacroAss
     EmitBranchICEntryKind(masm, entry, ICEntry::Kind_WarmupCounter, label);
     EmitBranchICEntryKind(masm, entry, ICEntry::Kind_StackCheck, label);
     EmitBranchICEntryKind(masm, entry, ICEntry::Kind_EarlyStackCheck, label);
 }
 
 static void
 SyncBaselineDebugModeOSRInfo(BaselineFrame* frame, Value* vp, bool rv)
 {
+    AutoUnsafeCallWithABI unsafe;
     BaselineDebugModeOSRInfo* info = frame->debugModeOSRInfo();
     MOZ_ASSERT(info);
     MOZ_ASSERT(frame->script()->baselineScript()->containsCodeAddress(info->resumeAddr));
 
     if (HasForcedReturn(info, rv)) {
         // Load the frame's rval and overwrite the resume address to go to the
         // epilogue.
         MOZ_ASSERT(R0 == JSReturnOperand);
@@ -1017,16 +1018,17 @@ SyncBaselineDebugModeOSRInfo(BaselineFra
 
     // Scale stackAdjust.
     info->stackAdjust *= sizeof(Value);
 }
 
 static void
 FinishBaselineDebugModeOSR(BaselineFrame* frame)
 {
+    AutoUnsafeCallWithABI unsafe;
     frame->deleteDebugModeOSRInfo();
 
     // We will return to JIT code now so we have to clear the override pc.
     frame->clearOverridePc();
 }
 
 void
 BaselineFrame::deleteDebugModeOSRInfo()
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1586,22 +1586,24 @@ CreateDependentString::generate(MacroAss
     }
 
     masm.bind(&done);
 }
 
 static void*
 AllocateString(JSContext* cx)
 {
+    AutoUnsafeCallWithABI unsafe;
     return js::Allocate<JSString, NoGC>(cx);
 }
 
 static void*
 AllocateFatInlineString(JSContext* cx)
 {
+    AutoUnsafeCallWithABI unsafe;
     return js::Allocate<JSFatInlineString, NoGC>(cx);
 }
 
 void
 CreateDependentString::generateFallback(MacroAssembler& masm, LiveRegisterSet regsToSave)
 {
     regsToSave.take(string_);
     regsToSave.take(temp_);
@@ -1624,16 +1626,17 @@ CreateDependentString::generateFallback(
 
         masm.jump(&joins_[kind]);
     }
 }
 
 static void*
 CreateMatchResultFallbackFunc(JSContext* cx, gc::AllocKind kind, size_t nDynamicSlots)
 {
+    AutoUnsafeCallWithABI unsafe;
     return js::Allocate<JSObject, NoGC>(cx, kind, nDynamicSlots, gc::DefaultHeap,
                                         &ArrayObject::class_);
 }
 
 static void
 CreateMatchResultFallback(MacroAssembler& masm, LiveRegisterSet regsToSave,
                           Register object, Register temp2, Register temp5,
                           ArrayObject* templateObj, Label* fail)
@@ -3995,17 +3998,18 @@ CodeGenerator::visitCallNative(LCallNati
     masm.passABIArg(argUintNReg);
     masm.passABIArg(argVpReg);
     JSNative native = target->native();
     if (call->ignoresReturnValue()) {
         const JSJitInfo* jitInfo = target->jitInfo();
         if (jitInfo && jitInfo->type() == JSJitInfo::IgnoresReturnValueNative)
             native = jitInfo->ignoresReturnValueMethod;
     }
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, native));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, native), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     emitTracelogStopEvent(TraceLogger_Call);
 
     // Test for failure.
     masm.branchIfFalseBool(ReturnReg, masm.failureLabel());
 
     // Load the outparam vp[0] into output register(s).
     masm.loadValue(Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), JSReturnOperand);
@@ -4118,17 +4122,18 @@ CodeGenerator::visitCallDOMNative(LCallD
 
     // Construct and execute call.
     masm.setupUnalignedABICall(argJSContext);
     masm.loadJSContext(argJSContext);
     masm.passABIArg(argJSContext);
     masm.passABIArg(argObj);
     masm.passABIArg(argPrivate);
     masm.passABIArg(argArgs);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->jitInfo()->method));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->jitInfo()->method), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     if (target->jitInfo()->isInfallible) {
         masm.loadValue(Address(masm.getStackPointer(), IonDOMMethodExitFrameLayout::offsetOfResult()),
                        JSReturnOperand);
     } else {
         // Test for failure.
         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
@@ -7054,25 +7059,34 @@ CodeGenerator::visitMathFunctionF(LMathF
     Register temp = ToRegister(ins->temp());
     FloatRegister input = ToFloatRegister(ins->input());
     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnFloat32Reg);
 
     masm.setupUnalignedABICall(temp);
     masm.passABIArg(input, MoveOp::FLOAT32);
 
     void* funptr = nullptr;
+    CheckUnsafeCallWithABI check = CheckUnsafeCallWithABI::Check;
     switch (ins->mir()->function()) {
-      case MMathFunction::Floor: funptr = JS_FUNC_TO_DATA_PTR(void*, floorf);           break;
-      case MMathFunction::Round: funptr = JS_FUNC_TO_DATA_PTR(void*, math_roundf_impl); break;
-      case MMathFunction::Ceil:  funptr = JS_FUNC_TO_DATA_PTR(void*, ceilf);            break;
+      case MMathFunction::Floor:
+        funptr = JS_FUNC_TO_DATA_PTR(void*, floorf);
+        check = CheckUnsafeCallWithABI::DontCheckOther;
+        break;
+      case MMathFunction::Round:
+        funptr = JS_FUNC_TO_DATA_PTR(void*, math_roundf_impl);
+        break;
+      case MMathFunction::Ceil:
+        funptr = JS_FUNC_TO_DATA_PTR(void*, ceilf);
+        check = CheckUnsafeCallWithABI::DontCheckOther;
+        break;
       default:
         MOZ_CRASH("Unknown or unsupported float32 math function");
     }
 
-    masm.callWithABI(funptr, MoveOp::FLOAT32);
+    masm.callWithABI(funptr, MoveOp::FLOAT32, check);
 }
 
 void
 CodeGenerator::visitModD(LModD* ins)
 {
     FloatRegister lhs = ToFloatRegister(ins->lhs());
     FloatRegister rhs = ToFloatRegister(ins->rhs());
 
@@ -7955,17 +7969,18 @@ JitRuntime::generateFreeStub(JSContext* 
     LiveRegisterSet save(regs.asLiveSet());
     masm.PushRegsInMask(save);
 
     const Register regTemp = regs.takeAnyGeneral();
     MOZ_ASSERT(regTemp != regSlots);
 
     masm.setupUnalignedABICall(regTemp);
     masm.passABIArg(regSlots);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js_free));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js_free), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckOther);
 
     masm.PopRegsInMask(save);
 
     masm.ret();
 
     Linker linker(masm);
     AutoFlushICache afc("FreeStub");
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
@@ -7993,17 +8008,18 @@ JitRuntime::generateLazyLinkStub(JSConte
     Register temp0 = regs.takeAny();
 
     masm.loadJSContext(temp0);
     masm.enterFakeExitFrame(temp0, temp0, ExitFrameToken::LazyLink);
     masm.PushStubCode();
 
     masm.setupUnalignedABICall(temp0);
     masm.passABIArg(temp0);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, LazyLinkTopActivation));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, LazyLinkTopActivation), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     masm.leaveExitFrame(/* stub code */ sizeof(JitCode*));
 
 #ifdef JS_USE_LINK_REGISTER
     // Restore the return address such that the emitPrologue function of the
     // CodeGenerator can push it back on the stack with pushReturnAddress.
     masm.popReturnAddress();
 #endif
@@ -11794,17 +11810,18 @@ CodeGenerator::visitGetDOMProperty(LGetD
     markSafepointAt(safepointOffset, ins);
 
     masm.setupUnalignedABICall(JSContextReg);
     masm.loadJSContext(JSContextReg);
     masm.passABIArg(JSContextReg);
     masm.passABIArg(ObjectReg);
     masm.passABIArg(PrivateReg);
     masm.passABIArg(ValueReg);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ins->mir()->fun()));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ins->mir()->fun()), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     if (ins->mir()->isInfallible()) {
         masm.loadValue(Address(masm.getStackPointer(), IonDOMExitFrameLayout::offsetOfResult()),
                        JSReturnOperand);
     } else {
         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
         masm.loadValue(Address(masm.getStackPointer(), IonDOMExitFrameLayout::offsetOfResult()),
@@ -11892,17 +11909,18 @@ CodeGenerator::visitSetDOMProperty(LSetD
     markSafepointAt(safepointOffset, ins);
 
     masm.setupUnalignedABICall(JSContextReg);
     masm.loadJSContext(JSContextReg);
     masm.passABIArg(JSContextReg);
     masm.passABIArg(ObjectReg);
     masm.passABIArg(PrivateReg);
     masm.passABIArg(ValueReg);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ins->mir()->fun()));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ins->mir()->fun()), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
     masm.adjustStack(IonDOMExitFrameLayout::Size());
 
     MOZ_ASSERT(masm.framePushed() == initialStack);
 }
 
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -1073,17 +1073,18 @@ IonCacheIRCompiler::emitCallNativeGetter
         return false;
     masm.enterFakeExitFrame(argJSContext, scratch, ExitFrameToken::IonOOLNative);
 
     // Construct and execute call.
     masm.setupUnalignedABICall(scratch);
     masm.passABIArg(argJSContext);
     masm.passABIArg(argUintN);
     masm.passABIArg(argVp);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     // Test for failure.
     masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
     // Load the outparam vp[0] into output register(s).
     Address outparam(masm.getStackPointer(), IonOOLNativeExitFrameLayout::offsetOfResult());
     masm.loadValue(outparam, output.valueReg());
 
@@ -1131,17 +1132,18 @@ IonCacheIRCompiler::emitCallProxyGetResu
     masm.enterFakeExitFrame(argJSContext, scratch, ExitFrameToken::IonOOLProxy);
 
     // Make the call.
     masm.setupUnalignedABICall(scratch);
     masm.passABIArg(argJSContext);
     masm.passABIArg(argProxy);
     masm.passABIArg(argId);
     masm.passABIArg(argVp);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ProxyGetProperty));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ProxyGetProperty), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     // Test for failure.
     masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
     // Load the outparam vp[0] into output register(s).
     Address outparam(masm.getStackPointer(), IonOOLProxyExitFrameLayout::offsetOfResult());
     masm.loadValue(outparam, output.valueReg());
 
@@ -1319,16 +1321,17 @@ IonCacheIRCompiler::emitCallStringSplitR
 
     masm.storeCallResultValue(output);
     return true;
 }
 
 static bool
 GroupHasPropertyTypes(ObjectGroup* group, jsid* id, Value* v)
 {
+    AutoUnsafeCallWithABI unsafe;
     if (group->unknownPropertiesDontCheckGeneration())
         return true;
     HeapTypeSet* propTypes = group->maybeGetPropertyDontCheckGeneration(*id);
     if (!propTypes)
         return true;
     if (!propTypes->nonConstantProperty())
         return false;
     return propTypes->hasType(TypeSet::GetValueType(*v));
@@ -1986,17 +1989,18 @@ IonCacheIRCompiler::emitCallNativeSetter
         return false;
     masm.enterFakeExitFrame(argJSContext, scratch, ExitFrameToken::IonOOLNative);
 
     // Make the call.
     masm.setupUnalignedABICall(scratch);
     masm.passABIArg(argJSContext);
     masm.passABIArg(argUintN);
     masm.passABIArg(argVp);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     // Test for failure.
     masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
     masm.adjustStack(IonOOLNativeExitFrameLayout::Size(1));
     return true;
 }
 
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -121,18 +121,32 @@ MacroAssembler::passABIArg(Register reg)
 }
 
 void
 MacroAssembler::passABIArg(FloatRegister reg, MoveOp::Type type)
 {
     passABIArg(MoveOperand(reg), type);
 }
 
-template <typename T> void
-MacroAssembler::callWithABI(const T& fun, MoveOp::Type result)
+void
+MacroAssembler::callWithABI(void* fun, MoveOp::Type result, CheckUnsafeCallWithABI check)
+{
+    AutoProfilerCallInstrumentation profiler(*this);
+    callWithABINoProfiler(fun, result, check);
+}
+
+void
+MacroAssembler::callWithABI(Register fun, MoveOp::Type result)
+{
+    AutoProfilerCallInstrumentation profiler(*this);
+    callWithABINoProfiler(fun, result);
+}
+
+void
+MacroAssembler::callWithABI(const Address& fun, MoveOp::Type result)
 {
     AutoProfilerCallInstrumentation profiler(*this);
     callWithABINoProfiler(fun, result);
 }
 
 void
 MacroAssembler::appendSignatureType(MoveOp::Type type)
 {
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1038,17 +1038,17 @@ FindStartOfUninitializedAndUndefinedSlot
     } else {
         *startOfUninitialized = *startOfUndefined;
     }
 }
 
 static void
 AllocateObjectBufferWithInit(JSContext* cx, TypedArrayObject* obj, int32_t count)
 {
-    JS::AutoCheckCannotGC nogc(cx);
+    AutoUnsafeCallWithABI unsafe;
 
     obj->initPrivate(nullptr);
 
     // Negative numbers or zero will bail out to the slow path, which in turn will raise
     // an invalid argument exception or create a correct object with zero elements.
     if (count <= 0 || uint32_t(count) >= INT32_MAX / obj->bytesPerElement()) {
         obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(0));
         return;
@@ -1545,17 +1545,18 @@ MacroAssembler::generateBailoutTail(Regi
     branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_OK), &baseline);
     branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_FATAL_ERROR), exceptionLabel());
 
     // Fall-through: overrecursed.
     {
         loadJSContext(ReturnReg);
         setupUnalignedABICall(scratch);
         passABIArg(ReturnReg);
-        callWithABI(JS_FUNC_TO_DATA_PTR(void*, BailoutReportOverRecursed));
+        callWithABI(JS_FUNC_TO_DATA_PTR(void*, BailoutReportOverRecursed), MoveOp::GENERAL,
+                    CheckUnsafeCallWithABI::DontCheckHasExitFrame);
         jump(exceptionLabel());
     }
 
     bind(&baseline);
     {
         // Prepare a register set for use in this case.
         AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
         MOZ_ASSERT(!regs.has(getStackPointer()));
@@ -1610,17 +1611,18 @@ MacroAssembler::generateBailoutTail(Regi
             pushValue(Address(bailoutInfo, offsetof(BaselineBailoutInfo, valueR0)));
             push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeFramePtr)));
             push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr)));
             push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, monitorStub)));
 
             // Call a stub to free allocated memory and create arguments objects.
             setupUnalignedABICall(temp);
             passABIArg(bailoutInfo);
-            callWithABI(JS_FUNC_TO_DATA_PTR(void*, FinishBailoutToBaseline));
+            callWithABI(JS_FUNC_TO_DATA_PTR(void*, FinishBailoutToBaseline),
+                        MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
             branchTest32(Zero, ReturnReg, ReturnReg, exceptionLabel());
 
             // Restore values where they need to be and resume execution.
             AllocatableGeneralRegisterSet enterMonRegs(GeneralRegisterSet::All());
             enterMonRegs.take(R0);
             enterMonRegs.take(ICStubReg);
             enterMonRegs.take(BaselineFrameReg);
             enterMonRegs.takeUnchecked(ICTailCallReg);
@@ -1648,17 +1650,18 @@ MacroAssembler::generateBailoutTail(Regi
             pushValue(Address(bailoutInfo, offsetof(BaselineBailoutInfo, valueR0)));
             pushValue(Address(bailoutInfo, offsetof(BaselineBailoutInfo, valueR1)));
             push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeFramePtr)));
             push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr)));
 
             // Call a stub to free allocated memory and create arguments objects.
             setupUnalignedABICall(temp);
             passABIArg(bailoutInfo);
-            callWithABI(JS_FUNC_TO_DATA_PTR(void*, FinishBailoutToBaseline));
+            callWithABI(JS_FUNC_TO_DATA_PTR(void*, FinishBailoutToBaseline),
+                        MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
             branchTest32(Zero, ReturnReg, ReturnReg, exceptionLabel());
 
             // Restore values where they need to be and resume execution.
             AllocatableGeneralRegisterSet enterRegs(GeneralRegisterSet::All());
             enterRegs.take(R0);
             enterRegs.take(R1);
             enterRegs.take(BaselineFrameReg);
             Register jitcodeReg = enterRegs.takeAny();
@@ -1724,17 +1727,19 @@ MacroAssembler::assumeUnreachable(const 
         AllocatableRegisterSet regs(RegisterSet::Volatile());
         LiveRegisterSet save(regs.asLiveSet());
         PushRegsInMask(save);
         Register temp = regs.takeAnyGeneral();
 
         setupUnalignedABICall(temp);
         movePtr(ImmPtr(output), temp);
         passABIArg(temp);
-        callWithABI(JS_FUNC_TO_DATA_PTR(void*, AssumeUnreachable_));
+        callWithABI(JS_FUNC_TO_DATA_PTR(void*, AssumeUnreachable_),
+                    MoveOp::GENERAL,
+                    CheckUnsafeCallWithABI::DontCheckOther);
 
         PopRegsInMask(save);
     }
 #endif
 
     breakpoint();
 }
 
@@ -1748,17 +1753,20 @@ MacroAssembler::assertTestInt32(Conditio
     assumeUnreachable(output);
     bind(&ok);
 #endif
 }
 
 template void MacroAssembler::assertTestInt32(Condition, const Address&, const char*);
 
 static void
-Printf0_(const char* output) {
+Printf0_(const char* output)
+{
+    AutoUnsafeCallWithABI unsafe;
+
     // Use stderr instead of stdout because this is only used for debug
     // output. stderr is less likely to interfere with the program's normal
     // output, and it's always unbuffered.
     fprintf(stderr, "%s", output);
 }
 
 void
 MacroAssembler::printf(const char* output)
@@ -1773,17 +1781,19 @@ MacroAssembler::printf(const char* outpu
     movePtr(ImmPtr(output), temp);
     passABIArg(temp);
     callWithABI(JS_FUNC_TO_DATA_PTR(void*, Printf0_));
 
     PopRegsInMask(save);
 }
 
 static void
-Printf1_(const char* output, uintptr_t value) {
+Printf1_(const char* output, uintptr_t value)
+{
+    AutoUnsafeCallWithABI unsafe;
     AutoEnterOOMUnsafeRegion oomUnsafe;
     js::UniqueChars line = JS_sprintf_append(nullptr, output, value);
     if (!line)
         oomUnsafe.crash("OOM at masm.printf");
     fprintf(stderr, "%s", line.get());
 }
 
 void
@@ -1819,17 +1829,18 @@ MacroAssembler::tracelogStartId(Register
     regs.takeUnchecked(logger);
 
     Register temp = regs.takeAnyGeneral();
 
     setupUnalignedABICall(temp);
     passABIArg(logger);
     move32(Imm32(textId), temp);
     passABIArg(temp);
-    callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStartEventPrivate));
+    callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStartEventPrivate), MoveOp::GENERAL,
+                CheckUnsafeCallWithABI::DontCheckOther);
 
     PopRegsInMask(save);
 }
 
 void
 MacroAssembler::tracelogStartId(Register logger, Register textId)
 {
     AllocatableRegisterSet regs(RegisterSet::Volatile());
@@ -1838,17 +1849,18 @@ MacroAssembler::tracelogStartId(Register
     regs.takeUnchecked(logger);
     regs.takeUnchecked(textId);
 
     Register temp = regs.takeAnyGeneral();
 
     setupUnalignedABICall(temp);
     passABIArg(logger);
     passABIArg(textId);
-    callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStartEventPrivate));
+    callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStartEventPrivate), MoveOp::GENERAL,
+                CheckUnsafeCallWithABI::DontCheckOther);
 
     PopRegsInMask(save);
 }
 
 void
 MacroAssembler::tracelogStartEvent(Register logger, Register event)
 {
     void (&TraceLogFunc)(TraceLoggerThread*, const TraceLoggerEvent&) = TraceLogStartEvent;
@@ -1859,17 +1871,18 @@ MacroAssembler::tracelogStartEvent(Regis
     regs.takeUnchecked(logger);
     regs.takeUnchecked(event);
 
     Register temp = regs.takeAnyGeneral();
 
     setupUnalignedABICall(temp);
     passABIArg(logger);
     passABIArg(event);
-    callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogFunc));
+    callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogFunc), MoveOp::GENERAL,
+                CheckUnsafeCallWithABI::DontCheckOther);
 
     PopRegsInMask(save);
 }
 
 void
 MacroAssembler::tracelogStopId(Register logger, uint32_t textId, bool force)
 {
     if (!force && !TraceLogTextIdEnabled(textId))
@@ -1882,17 +1895,18 @@ MacroAssembler::tracelogStopId(Register 
 
     Register temp = regs.takeAnyGeneral();
 
     setupUnalignedABICall(temp);
     passABIArg(logger);
     move32(Imm32(textId), temp);
     passABIArg(temp);
 
-    callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStopEventPrivate));
+    callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStopEventPrivate), MoveOp::GENERAL,
+                CheckUnsafeCallWithABI::DontCheckOther);
 
     PopRegsInMask(save);
 }
 
 void
 MacroAssembler::tracelogStopId(Register logger, Register textId)
 {
     AllocatableRegisterSet regs(RegisterSet::Volatile());
@@ -1901,17 +1915,18 @@ MacroAssembler::tracelogStopId(Register 
     regs.takeUnchecked(logger);
     regs.takeUnchecked(textId);
 
     Register temp = regs.takeAnyGeneral();
 
     setupUnalignedABICall(temp);
     passABIArg(logger);
     passABIArg(textId);
-    callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStopEventPrivate));
+    callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStopEventPrivate), MoveOp::GENERAL,
+                CheckUnsafeCallWithABI::DontCheckOther);
 
     PopRegsInMask(save);
 }
 #endif
 
 void
 MacroAssembler::convertInt32ValueToDouble(const Address& address, Register scratch, Label* done)
 {
@@ -2102,17 +2117,18 @@ MacroAssembler::outOfLineTruncateSlow(Fl
 
     if (compilingWasm) {
         setupWasmABICall();
         passABIArg(src, MoveOp::DOUBLE);
         callWithABI(callOffset, wasm::SymbolicAddress::ToInt32);
     } else {
         setupUnalignedABICall(dest);
         passABIArg(src, MoveOp::DOUBLE);
-        callWithABI(mozilla::BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
+        callWithABI(mozilla::BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32),
+                    MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther);
     }
     storeCallWordResult(dest);
 
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
     defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     // Nothing
 #elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     if (widenFloatToDouble)
@@ -2796,27 +2812,52 @@ MacroAssembler::passABIArg(const MoveOpe
         return;
 
     if (oom())
         return;
     propagateOOM(moveResolver_.addMove(from, to, type));
 }
 
 void
-MacroAssembler::callWithABINoProfiler(void* fun, MoveOp::Type result)
+MacroAssembler::callWithABINoProfiler(void* fun, MoveOp::Type result, CheckUnsafeCallWithABI check)
 {
     appendSignatureType(result);
 #ifdef JS_SIMULATOR
     fun = Simulator::RedirectNativeFunction(fun, signature());
 #endif
 
     uint32_t stackAdjust;
     callWithABIPre(&stackAdjust);
+
+#ifdef DEBUG
+    if (check == CheckUnsafeCallWithABI::Check) {
+        push(ReturnReg);
+        loadJSContext(ReturnReg);
+        Address flagAddr(ReturnReg, JSContext::offsetOfInUnsafeCallWithABI());
+        store32(Imm32(1), flagAddr);
+        pop(ReturnReg);
+    }
+#endif
+
     call(ImmPtr(fun));
+
     callWithABIPost(stackAdjust, result);
+
+#ifdef DEBUG
+    if (check == CheckUnsafeCallWithABI::Check) {
+        Label ok;
+        push(ReturnReg);
+        loadJSContext(ReturnReg);
+        Address flagAddr(ReturnReg, JSContext::offsetOfInUnsafeCallWithABI());
+        branch32(Assembler::Equal, flagAddr, Imm32(0), &ok);
+        assumeUnreachable("callWithABI: callee did not use AutoInUnsafeCallWithABI");
+        bind(&ok);
+        pop(ReturnReg);
+    }
+#endif
 }
 
 void
 MacroAssembler::callWithABI(wasm::BytecodeOffset callOffset, wasm::SymbolicAddress imm,
                             MoveOp::Type result)
 {
     MOZ_ASSERT(wasm::NeedsBuiltinThunk(imm));
 
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -188,16 +188,30 @@ using mozilla::FloatingPoint;
 namespace js {
 namespace jit {
 
 // Defined in JitFrames.h
 enum class ExitFrameToken : uint8_t;
 
 class AutoSaveLiveRegisters;
 
+enum class CheckUnsafeCallWithABI {
+    // Require the callee to use AutoUnsafeCallWithABI.
+    Check,
+
+    // We pushed an exit frame so this callWithABI can safely GC and walk the
+    // stack.
+    DontCheckHasExitFrame,
+
+    // Don't check this callWithABI uses AutoUnsafeCallWithABI, for instance
+    // because we're calling a simple helper function (like malloc or js_free)
+    // that we can't change and/or that we know won't GC.
+    DontCheckOther,
+};
+
 // The public entrypoint for emitting assembly. Note that a MacroAssembler can
 // use cx->lifoAlloc, so take care not to interleave masm use with other
 // lifoAlloc use if one will be destroyed before the other.
 class MacroAssembler : public MacroAssemblerSpecific
 {
     MacroAssembler* thisFromCtor() {
         return this;
     }
@@ -560,32 +574,34 @@ class MacroAssembler : public MacroAssem
     // temporarily use more stack, in which case esp-relative addresses will be
     // automatically adjusted. It is extremely important that esp-relative
     // addresses are computed *after* setupABICall(). Furthermore, no
     // operations should be emitted while setting arguments.
     void passABIArg(const MoveOperand& from, MoveOp::Type type);
     inline void passABIArg(Register reg);
     inline void passABIArg(FloatRegister reg, MoveOp::Type type);
 
-    template <typename T>
-    inline void callWithABI(const T& fun, MoveOp::Type result = MoveOp::GENERAL);
+    inline void callWithABI(void* fun, MoveOp::Type result = MoveOp::GENERAL,
+                            CheckUnsafeCallWithABI check = CheckUnsafeCallWithABI::Check);
+    inline void callWithABI(Register fun, MoveOp::Type result = MoveOp::GENERAL);
+    inline void callWithABI(const Address& fun, MoveOp::Type result = MoveOp::GENERAL);
 
     void callWithABI(wasm::BytecodeOffset offset, wasm::SymbolicAddress fun,
                      MoveOp::Type result = MoveOp::GENERAL);
 
   private:
     // Reinitialize the variables which have to be cleared before making a call
     // with callWithABI.
     void setupABICall();
 
     // Reserve the stack and resolve the arguments move.
     void callWithABIPre(uint32_t* stackAdjust, bool callFromWasm = false) PER_ARCH;
 
     // Emits a call to a C/C++ function, resolving all argument moves.
-    void callWithABINoProfiler(void* fun, MoveOp::Type result);
+    void callWithABINoProfiler(void* fun, MoveOp::Type result, CheckUnsafeCallWithABI check);
     void callWithABINoProfiler(Register fun, MoveOp::Type result) PER_ARCH;
     void callWithABINoProfiler(const Address& fun, MoveOp::Type result) PER_ARCH;
 
     // Restore the stack to its state before the setup function call.
     void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result, bool callFromWasm = false) PER_ARCH;
 
     // Create the signature to be able to decode the arguments of a native
     // function, when calling a function within the simulator.
--- a/js/src/jit/RematerializedFrame.h
+++ b/js/src/jit/RematerializedFrame.h
@@ -254,22 +254,14 @@ namespace JS {
 template <>
 struct MapTypeToRootKind<js::jit::RematerializedFrame*>
 {
     static const RootKind kind = RootKind::Traceable;
 };
 
 template <>
 struct GCPolicy<js::jit::RematerializedFrame*>
-{
-    static js::jit::RematerializedFrame* initial() {
-        return nullptr;
-    }
-
-    static void trace(JSTracer* trc, js::jit::RematerializedFrame** frame, const char* name) {
-        if (*frame)
-            (*frame)->trace(trc);
-    }
-};
+  : public NonGCPointerPolicy<js::jit::RematerializedFrame*>
+{};
 
 } // namespace JS
 
 #endif // jit_RematerializedFrame_h
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -1200,17 +1200,18 @@ ICBinaryArith_DoubleWithInt32::Compiler:
         Label truncateABICall;
         masm.branchTruncateDoubleMaybeModUint32(FloatReg0, scratchReg, &truncateABICall);
         masm.jump(&doneTruncate);
 
         masm.bind(&truncateABICall);
         masm.push(intReg);
         masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
-        masm.callWithABI(mozilla::BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
+        masm.callWithABI(mozilla::BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32),
+                         MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther);
         masm.storeCallWordResult(scratchReg);
         masm.pop(intReg);
 
         masm.bind(&doneTruncate);
     }
 
     Register intReg2 = scratchReg;
     // All handled ops commute, so no need to worry about ordering.
@@ -1353,17 +1354,18 @@ ICUnaryArith_Double::Compiler::generateS
         Label doneTruncate;
         Label truncateABICall;
         masm.branchTruncateDoubleMaybeModUint32(FloatReg0, scratchReg, &truncateABICall);
         masm.jump(&doneTruncate);
 
         masm.bind(&truncateABICall);
         masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
-        masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
+        masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32),
+                         MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther);
         masm.storeCallWordResult(scratchReg);
 
         masm.bind(&doneTruncate);
         masm.not32(scratchReg);
         masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
     }
 
     EmitReturnFromIC(masm);
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -539,16 +539,17 @@ InterruptCheck(JSContext* cx)
     }
 
     return CheckForInterrupt(cx);
 }
 
 void*
 MallocWrapper(JSRuntime* rt, size_t nbytes)
 {
+    AutoUnsafeCallWithABI unsafe;
     return rt->pod_malloc<uint8_t>(nbytes);
 }
 
 JSObject*
 NewCallObject(JSContext* cx, HandleShape shape, HandleObjectGroup group)
 {
     JSObject* obj = CallObject::create(cx, shape, group);
     if (!obj)
@@ -644,16 +645,18 @@ CreateThis(JSContext* cx, HandleObject c
 
 void
 GetDynamicName(JSContext* cx, JSObject* envChain, JSString* str, Value* vp)
 {
     // Lookup a string on the env chain, returning either the value found or
     // undefined through rval. This function is infallible, and cannot GC or
     // invalidate.
 
+    AutoUnsafeCallWithABI unsafe;
+
     JSAtom* atom;
     if (str->isAtom()) {
         atom = &str->asAtom();
     } else {
         atom = AtomizeString(cx, str);
         if (!atom) {
             vp->setUndefined();
             return;
@@ -674,28 +677,28 @@ GetDynamicName(JSContext* cx, JSObject* 
     }
 
     vp->setUndefined();
 }
 
 void
 PostWriteBarrier(JSRuntime* rt, JSObject* obj)
 {
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
     MOZ_ASSERT(!IsInsideNursery(obj));
     rt->gc.storeBuffer().putWholeCell(obj);
 }
 
 static const size_t MAX_WHOLE_CELL_BUFFER_SIZE = 4096;
 
 template <IndexInBounds InBounds>
 void
 PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, int32_t index)
 {
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
 
     MOZ_ASSERT(!IsInsideNursery(obj));
 
     if (InBounds == IndexInBounds::Yes) {
         MOZ_ASSERT(uint32_t(index) < obj->as<NativeObject>().getDenseInitializedLength());
     } else {
         if (MOZ_UNLIKELY(!obj->is<NativeObject>()) ||
             uint32_t(index) >= obj->as<NativeObject>().getDenseInitializedLength())
@@ -739,33 +742,33 @@ PostGlobalWriteBarrier(JSRuntime* rt, JS
         obj->compartment()->globalWriteBarriered = 1;
     }
 }
 
 int32_t
 GetIndexFromString(JSString* str)
 {
     // We shouldn't GC here as this is called directly from IC code.
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
 
     if (!str->isFlat())
         return -1;
 
     uint32_t index;
     if (!str->asFlat().isIndex(&index) || index > INT32_MAX)
         return -1;
 
     return int32_t(index);
 }
 
 JSObject*
 WrapObjectPure(JSContext* cx, JSObject* obj)
 {
     // IC code calls this directly so we shouldn't GC.
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
 
     MOZ_ASSERT(obj);
     MOZ_ASSERT(cx->compartment() != obj->compartment());
 
     // From: JSCompartment::getNonWrapperObjectForCurrentCompartment
     // Note that if the object is same-compartment, but has been wrapped into a
     // different compartment, we need to unwrap it and return the bare same-
     // compartment object. Note again that windows are always wrapped by a
@@ -859,16 +862,17 @@ DebugEpilogue(JSContext* cx, BaselineFra
     // builds after each callVM, to ensure this flag is not set.
     frame->clearOverridePc();
     return true;
 }
 
 void
 FrameIsDebuggeeCheck(BaselineFrame* frame)
 {
+    AutoUnsafeCallWithABI unsafe;
     if (frame->script()->isDebuggee())
         frame->setIsDebuggee();
 }
 
 JSObject*
 CreateGenerator(JSContext* cx, BaselineFrame* frame)
 {
     return GeneratorObject::create(cx, frame);
@@ -1135,16 +1139,17 @@ OnDebuggerStatement(JSContext* cx, Basel
       default:
         MOZ_CRASH("Invalid trap status");
     }
 }
 
 bool
 GlobalHasLiveOnDebuggerStatement(JSContext* cx)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cx->compartment()->isDebuggee() &&
            Debugger::hasLiveHook(cx->global(), Debugger::OnDebuggerStatement);
 }
 
 bool
 PushLexicalEnv(JSContext* cx, BaselineFrame* frame, Handle<LexicalScope*> scope)
 {
     return frame->pushLexicalEnvironment(cx, scope);
@@ -1227,16 +1232,17 @@ LeaveWith(JSContext* cx, BaselineFrame* 
     frame->popOffEnvironmentChain<WithEnvironmentObject>();
     return true;
 }
 
 bool
 InitBaselineFrameForOsr(BaselineFrame* frame, InterpreterFrame* interpFrame,
                         uint32_t numStackValues)
 {
+    AutoUnsafeCallWithABI unsafe;
     return frame->initForOsr(interpFrame, numStackValues);
 }
 
 JSObject*
 CreateDerivedTypedObj(JSContext* cx, HandleObject descr,
                       HandleObject owner, int32_t offset)
 {
     MOZ_ASSERT(descr->is<TypeDescr>());
@@ -1313,16 +1319,17 @@ void
 AutoDetectInvalidation::setReturnOverride()
 {
     cx_->setIonReturnOverride(rval_.get());
 }
 
 void
 AssertValidObjectPtr(JSContext* cx, JSObject* obj)
 {
+    AutoUnsafeCallWithABI unsafe;
 #ifdef DEBUG
     // Check what we can, so that we'll hopefully assert/crash if we get a
     // bogus object (pointer).
     MOZ_ASSERT(obj->compartment() == cx->compartment());
     MOZ_ASSERT(obj->runtimeFromActiveCooperatingThread() == cx->runtime());
 
     MOZ_ASSERT_IF(!obj->hasLazyGroup() && obj->maybeShape(),
                   obj->group()->clasp() == obj->maybeShape()->getObjectClass());
@@ -1334,23 +1341,25 @@ AssertValidObjectPtr(JSContext* cx, JSOb
         MOZ_ASSERT(obj->asTenured().zone() == cx->zone());
     }
 #endif
 }
 
 void
 AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj)
 {
+    AutoUnsafeCallWithABI unsafe;
     if (obj)
         AssertValidObjectPtr(cx, obj);
 }
 
 void
 AssertValidStringPtr(JSContext* cx, JSString* str)
 {
+    AutoUnsafeCallWithABI unsafe;
 #ifdef DEBUG
     // We can't closely inspect strings from another runtime.
     if (str->runtimeFromAnyThread() != cx->runtime()) {
         MOZ_ASSERT(str->isPermanentAtom());
         return;
     }
 
     if (str->isAtom())
@@ -1377,16 +1386,18 @@ AssertValidStringPtr(JSContext* cx, JSSt
         MOZ_ASSERT(kind == gc::AllocKind::STRING);
     }
 #endif
 }
 
 void
 AssertValidSymbolPtr(JSContext* cx, JS::Symbol* sym)
 {
+    AutoUnsafeCallWithABI unsafe;
+
     // We can't closely inspect symbols from another runtime.
     if (sym->runtimeFromAnyThread() != cx->runtime()) {
         MOZ_ASSERT(sym->isWellKnownSymbol());
         return;
     }
 
     MOZ_ASSERT(sym->zone()->isAtomsZone());
     MOZ_ASSERT(sym->isAligned());
@@ -1396,65 +1407,73 @@ AssertValidSymbolPtr(JSContext* cx, JS::
     }
 
     MOZ_ASSERT(sym->getAllocKind() == gc::AllocKind::SYMBOL);
 }
 
 void
 AssertValidValue(JSContext* cx, Value* v)
 {
+    AutoUnsafeCallWithABI unsafe;
     if (v->isObject())
         AssertValidObjectPtr(cx, &v->toObject());
     else if (v->isString())
         AssertValidStringPtr(cx, v->toString());
     else if (v->isSymbol())
         AssertValidSymbolPtr(cx, v->toSymbol());
 }
 
 bool
 ObjectIsCallable(JSObject* obj)
 {
+    AutoUnsafeCallWithABI unsafe;
     return obj->isCallable();
 }
 
 bool
 ObjectIsConstructor(JSObject* obj)
 {
+    AutoUnsafeCallWithABI unsafe;
     return obj->isConstructor();
 }
 
 void
 MarkValueFromIon(JSRuntime* rt, Value* vp)
 {
+    AutoUnsafeCallWithABI unsafe;
     TraceManuallyBarrieredEdge(&rt->gc.marker, vp, "write barrier");
 }
 
 void
 MarkStringFromIon(JSRuntime* rt, JSString** stringp)
 {
+    AutoUnsafeCallWithABI unsafe;
     MOZ_ASSERT(*stringp);
     TraceManuallyBarrieredEdge(&rt->gc.marker, stringp, "write barrier");
 }
 
 void
 MarkObjectFromIon(JSRuntime* rt, JSObject** objp)
 {
+    AutoUnsafeCallWithABI unsafe;
     MOZ_ASSERT(*objp);
     TraceManuallyBarrieredEdge(&rt->gc.marker, objp, "write barrier");
 }
 
 void
 MarkShapeFromIon(JSRuntime* rt, Shape** shapep)
 {
+    AutoUnsafeCallWithABI unsafe;
     TraceManuallyBarrieredEdge(&rt->gc.marker, shapep, "write barrier");
 }
 
 void
 MarkObjectGroupFromIon(JSRuntime* rt, ObjectGroup** groupp)
 {
+    AutoUnsafeCallWithABI unsafe;
     TraceManuallyBarrieredEdge(&rt->gc.marker, groupp, "write barrier");
 }
 
 bool
 ThrowRuntimeLexicalError(JSContext* cx, unsigned errorNumber)
 {
     ScriptFrameIter iter(cx);
     RootedScript script(cx, iter.script());
@@ -1546,17 +1565,17 @@ CallNativeSetter(JSContext* cx, HandleFu
 
     return natfun(cx, 1, vp.begin());
 }
 
 bool
 EqualStringsHelper(JSString* str1, JSString* str2)
 {
     // IC code calls this directly so we shouldn't GC.
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
 
     MOZ_ASSERT(str1->isAtom());
     MOZ_ASSERT(!str2->isAtom());
     MOZ_ASSERT(str1->length() == str2->length());
 
     JSLinearString* str2Linear = str2->ensureLinear(nullptr);
     if (!str2Linear)
         return false;
@@ -1576,17 +1595,17 @@ CheckIsCallable(JSContext* cx, HandleVal
 template <bool HandleMissing>
 static MOZ_ALWAYS_INLINE bool
 GetNativeDataProperty(JSContext* cx, NativeObject* obj, jsid id, Value* vp)
 {
     // Fast path used by megamorphic IC stubs. Unlike our other property
     // lookup paths, this is optimized to be as fast as possible for simple
     // data property lookups.
 
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
 
     MOZ_ASSERT(JSID_IS_ATOM(id) || JSID_IS_SYMBOL(id));
 
     while (true) {
         if (Shape* shape = obj->lastProperty()->search(cx, id)) {
             if (!shape->hasSlot() || !shape->hasDefaultGetter())
                 return false;
 
@@ -1628,17 +1647,17 @@ template bool
 GetNativeDataProperty<true>(JSContext* cx, JSObject* obj, PropertyName* name, Value* vp);
 
 template bool
 GetNativeDataProperty<false>(JSContext* cx, JSObject* obj, PropertyName* name, Value* vp);
 
 static MOZ_ALWAYS_INLINE bool
 ValueToAtomOrSymbol(JSContext* cx, Value& idVal, jsid* id)
 {
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
 
     if (MOZ_LIKELY(idVal.isString())) {
         JSString* s = idVal.toString();
         JSAtom* atom;
         if (s->isAtom()) {
             atom = &s->asAtom();
         } else {
             atom = AtomizeString(cx, s);
@@ -1661,17 +1680,17 @@ ValueToAtomOrSymbol(JSContext* cx, Value
 
     return true;
 }
 
 template <bool HandleMissing>
 bool
 GetNativeDataPropertyByValue(JSContext* cx, JSObject* obj, Value* vp)
 {
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
 
     // Condition checked by caller.
     MOZ_ASSERT(obj->isNative());
 
     // vp[0] contains the id, result will be stored in vp[1].
     Value idVal = vp[0];
     jsid id;
     if (!ValueToAtomOrSymbol(cx, idVal, &id))
@@ -1686,17 +1705,17 @@ GetNativeDataPropertyByValue<true>(JSCon
 
 template bool
 GetNativeDataPropertyByValue<false>(JSContext* cx, JSObject* obj, Value* vp);
 
 template <bool NeedsTypeBarrier>
 bool
 SetNativeDataProperty(JSContext* cx, JSObject* obj, PropertyName* name, Value* val)
 {
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
 
     if (MOZ_UNLIKELY(!obj->isNative()))
         return false;
 
     NativeObject* nobj = &obj->as<NativeObject>();
     Shape* shape = nobj->lastProperty()->search(cx, NameToId(name));
     if (!shape ||
         !shape->hasSlot() ||
@@ -1718,17 +1737,17 @@ template bool
 SetNativeDataProperty<true>(JSContext* cx, JSObject* obj, PropertyName* name, Value* val);
 
 template bool
 SetNativeDataProperty<false>(JSContext* cx, JSObject* obj, PropertyName* name, Value* val);
 
 bool
 ObjectHasGetterSetter(JSContext* cx, JSObject* objArg, Shape* propShape)
 {
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
 
     MOZ_ASSERT(propShape->hasGetterObject() || propShape->hasSetterObject());
 
     // Window objects may require outerizing (passing the WindowProxy to the
     // getter/setter), so we don't support them here.
     if (MOZ_UNLIKELY(!objArg->isNative() || IsWindow(objArg)))
         return false;
 
@@ -1761,17 +1780,17 @@ ObjectHasGetterSetter(JSContext* cx, JSO
             return false;
         nobj = &proto->as<NativeObject>();
     }
 }
 
 bool
 HasOwnNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp)
 {
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
 
     // vp[0] contains the id, result will be stored in vp[1].
     Value idVal = vp[0];
     jsid id;
     if (!ValueToAtomOrSymbol(cx, idVal, &id))
         return false;
 
     if (!obj->isNative()) {
@@ -1798,16 +1817,17 @@ HasOwnNativeDataProperty(JSContext* cx, 
     // Missing property.
     vp[1].setBoolean(false);
     return true;
 }
 
 JSString*
 TypeOfObject(JSObject* obj, JSRuntime* rt)
 {
+    AutoUnsafeCallWithABI unsafe;
     JSType type = js::TypeOfObject(obj);
     return TypeName(type, *rt->commonNames);
 }
 
 bool
 GetPrototypeOf(JSContext* cx, HandleObject target, MutableHandleValue rval)
 {
     MOZ_ASSERT(target->hasDynamicPrototype());
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -625,17 +625,18 @@ CodeGeneratorARM::visitSoftDivI(LSoftDiv
         masm.setupWasmABICall();
         masm.passABIArg(lhs);
         masm.passABIArg(rhs);
         masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::aeabi_idivmod);
     } else {
         masm.setupAlignedABICall();
         masm.passABIArg(lhs);
         masm.passABIArg(rhs);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod));
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod), MoveOp::GENERAL,
+                         CheckUnsafeCallWithABI::DontCheckOther);
     }
 
     // idivmod returns the quotient in r0, and the remainder in r1.
     if (!mir->canTruncateRemainder()) {
         MOZ_ASSERT(mir->fallible());
         masm.as_cmp(r1, Imm8(0));
         bailoutIf(Assembler::NonZero, ins->snapshot());
     }
@@ -814,17 +815,18 @@ CodeGeneratorARM::visitSoftModI(LSoftMod
         masm.setupWasmABICall();
         masm.passABIArg(lhs);
         masm.passABIArg(rhs);
         masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::aeabi_idivmod);
     } else {
         masm.setupAlignedABICall();
         masm.passABIArg(lhs);
         masm.passABIArg(rhs);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod));
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod), MoveOp::GENERAL,
+                         CheckUnsafeCallWithABI::DontCheckOther);
     }
 
     MOZ_ASSERT(r1 != output);
     masm.move32(r1, output);
 
     // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0
     if (mir->canBeNegativeDividend()) {
         if (mir->isTruncated()) {
@@ -2836,17 +2838,18 @@ CodeGeneratorARM::visitSoftUDivOrMod(LSo
         masm.passABIArg(lhs);
         masm.passABIArg(rhs);
         wasm::BytecodeOffset bytecodeOffset = (div ? div->bytecodeOffset() : mod->bytecodeOffset());
         masm.callWithABI(bytecodeOffset, wasm::SymbolicAddress::aeabi_uidivmod);
     } else {
         masm.setupAlignedABICall();
         masm.passABIArg(lhs);
         masm.passABIArg(rhs);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_uidivmod));
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_uidivmod), MoveOp::GENERAL,
+                         CheckUnsafeCallWithABI::DontCheckOther);
     }
 
     if (mod) {
         MOZ_ASSERT(output == r0, "output should not be r1 for mod");
         masm.move32(r1, output);
     }
 
     // uidivmod returns the quotient in r0, and the remainder in r1.
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -3593,17 +3593,17 @@ MacroAssemblerARMCompat::handleFailureWi
 
     Imm8 size8(size);
     as_sub(sp, sp, size8);
     ma_mov(sp, r0);
 
     // Call the handler.
     asMasm().setupUnalignedABICall(r1);
     asMasm().passABIArg(r0);
-    asMasm().callWithABI(handler);
+    asMasm().callWithABI(handler, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     Label entryFrame;
     Label catch_;
     Label finally;
     Label return_;
     Label bailout;
 
     {
--- a/js/src/jit/arm/SharedIC-arm.cpp
+++ b/js/src/jit/arm/SharedIC-arm.cpp
@@ -89,17 +89,18 @@ ICBinaryArith_Int32::Compiler::generateS
         // register.
         MOZ_ASSERT(R1 == ValueOperand(r5, r4));
         MOZ_ASSERT(R0 == ValueOperand(r3, r2));
         masm.moveValue(R0, savedValue);
 
         masm.setupAlignedABICall();
         masm.passABIArg(R0.payloadReg());
         masm.passABIArg(R1.payloadReg());
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod));
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod), MoveOp::GENERAL,
+                         CheckUnsafeCallWithABI::DontCheckOther);
 
         // idivmod returns the quotient in r0, and the remainder in r1.
         if (op_ == JSOP_DIV) {
             // Result is a double if the remainder != 0.
             masm.branch32(Assembler::NotEqual, r1, Imm32(0), &revertRegister);
             masm.tagValue(JSVAL_TYPE_INT32, r0, R0);
         } else {
             // If X % Y == 0 and X < 0, the result is -0.
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -877,17 +877,17 @@ JitRuntime::generateVMWrapper(JSContext*
             break;
         }
     }
 
     // Copy the implicit outparam, if any.
     if (outReg != InvalidReg)
         masm.passABIArg(outReg);
 
-    masm.callWithABI(f.wrapped);
+    masm.callWithABI(f.wrapped, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     if (!generateTLExitVM(cx, masm, f))
         return nullptr;
 
     // Test for failure.
     switch (f.failType()) {
       case Type_Object:
         masm.branchTestPtr(Assembler::Zero, r0, r0, masm.failureLabel());
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -140,17 +140,17 @@ MacroAssemblerCompat::handleFailureWithH
     if (!GetStackPointer64().Is(sp))
         Mov(sp, GetStackPointer64());
 
     Mov(x0, GetStackPointer64());
 
     // Call the handler.
     asMasm().setupUnalignedABICall(r1);
     asMasm().passABIArg(r0);
-    asMasm().callWithABI(handler);
+    asMasm().callWithABI(handler, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     Label entryFrame;
     Label catch_;
     Label finally;
     Label return_;
     Label bailout;
 
     MOZ_ASSERT(GetStackPointer64().Is(x28)); // Lets the code below be a little cleaner.
--- a/js/src/jit/arm64/Trampoline-arm64.cpp
+++ b/js/src/jit/arm64/Trampoline-arm64.cpp
@@ -672,17 +672,17 @@ JitRuntime::generateVMWrapper(JSContext*
 
     // Copy the semi-implicit outparam, if any.
     // It is not a C++-abi outparam, which would get passed in the
     // outparam register, but a real parameter to the function, which
     // was stack-allocated above.
     if (outReg != InvalidReg)
         masm.passABIArg(outReg);
 
-    masm.callWithABI(f.wrapped);
+    masm.callWithABI(f.wrapped, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     if (!generateTLExitVM(cx, masm, f))
         return nullptr;
 
     // SP is used to transfer stack across call boundaries.
     if (!masm.GetStackPointer64().Is(vixl::sp))
         masm.Mov(masm.GetStackPointer64(), vixl::sp);
 
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -1843,17 +1843,17 @@ MacroAssemblerMIPSCompat::handleFailureW
     // Reserve space for exception information.
     int size = (sizeof(ResumeFromException) + ABIStackAlignment) & ~(ABIStackAlignment - 1);
     asMasm().subPtr(Imm32(size), StackPointer);
     ma_move(a0, StackPointer); // Use a0 since it is a first function argument
 
     // Call the handler.
     asMasm().setupUnalignedABICall(a1);
     asMasm().passABIArg(a0);
-    asMasm().callWithABI(handler);
+    asMasm().callWithABI(handler, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     Label entryFrame;
     Label catch_;
     Label finally;
     Label return_;
     Label bailout;
 
     // Already clobbered a0, so use it...
--- a/js/src/jit/mips32/Trampoline-mips32.cpp
+++ b/js/src/jit/mips32/Trampoline-mips32.cpp
@@ -842,17 +842,17 @@ JitRuntime::generateVMWrapper(JSContext*
                   doubleArgDisp + sizeof(double) == outParamOffset + outParamSize);
 
     // Copy the implicit outparam, if any.
     if (f.outParam != Type_Void) {
         masm.passABIArg(MoveOperand(doubleArgs, outParamOffset, MoveOperand::EFFECTIVE_ADDRESS),
                             MoveOp::GENERAL);
     }
 
-    masm.callWithABI(f.wrapped);
+    masm.callWithABI(f.wrapped, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     if (!generateTLExitVM(cx, masm, f))
         return nullptr;
 
     // Test for failure.
     switch (f.failType()) {
       case Type_Object:
         masm.branchTestPtr(Assembler::Zero, v0, v0, masm.failureLabel());
--- a/js/src/jit/mips64/MacroAssembler-mips64.cpp
+++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp
@@ -2025,17 +2025,17 @@ MacroAssemblerMIPS64Compat::handleFailur
     // Reserve space for exception information.
     int size = (sizeof(ResumeFromException) + ABIStackAlignment) & ~(ABIStackAlignment - 1);
     asMasm().subPtr(Imm32(size), StackPointer);
     ma_move(a0, StackPointer); // Use a0 since it is a first function argument
 
     // Call the handler.
     asMasm().setupUnalignedABICall(a1);
     asMasm().passABIArg(a0);
-    asMasm().callWithABI(handler);
+    asMasm().callWithABI(handler, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     Label entryFrame;
     Label catch_;
     Label finally;
     Label return_;
     Label bailout;
 
     // Already clobbered a0, so use it...
--- a/js/src/jit/mips64/Trampoline-mips64.cpp
+++ b/js/src/jit/mips64/Trampoline-mips64.cpp
@@ -788,17 +788,17 @@ JitRuntime::generateVMWrapper(JSContext*
             break;
         }
     }
 
     // Copy the implicit outparam, if any.
     if (InvalidReg != outReg)
         masm.passABIArg(outReg);
 
-    masm.callWithABI(f.wrapped);
+    masm.callWithABI(f.wrapped, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     if (!generateTLExitVM(cx, masm, f))
         return nullptr;
 
     // Test for failure.
     switch (f.failType()) {
       case Type_Object:
         masm.branchTestPtr(Assembler::Zero, v0, v0, masm.failureLabel());
--- a/js/src/jit/x64/MacroAssembler-x64.cpp
+++ b/js/src/jit/x64/MacroAssembler-x64.cpp
@@ -302,17 +302,17 @@ MacroAssemblerX64::handleFailureWithHand
 {
     // Reserve space for exception information.
     subq(Imm32(sizeof(ResumeFromException)), rsp);
     movq(rsp, rax);
 
     // Call the handler.
     asMasm().setupUnalignedABICall(rcx);
     asMasm().passABIArg(rax);
-    asMasm().callWithABI(handler);
+    asMasm().callWithABI(handler, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     Label entryFrame;
     Label catch_;
     Label finally;
     Label return_;
     Label bailout;
 
     load32(Address(rsp, offsetof(ResumeFromException, kind)), rax);
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -760,17 +760,17 @@ JitRuntime::generateVMWrapper(JSContext*
             MOZ_CRASH("NYI: x64 callVM should not be used with 128bits values.");
         }
     }
 
     // Copy the implicit outparam, if any.
     if (outReg != InvalidReg)
         masm.passABIArg(outReg);
 
-    masm.callWithABI(f.wrapped);
+    masm.callWithABI(f.wrapped, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     if (!generateTLExitVM(cx, masm, f))
         return nullptr;
 
     // Test for failure.
     switch (f.failType()) {
       case Type_Object:
         masm.branchTestPtr(Assembler::Zero, rax, rax, masm.failureLabel());
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -739,17 +739,18 @@ CodeGeneratorX86::visitOutOfLineTruncate
 
         if (gen->compilingWasm()) {
             masm.setupWasmABICall();
             masm.passABIArg(input, MoveOp::DOUBLE);
             masm.callWithABI(ins->mir()->bytecodeOffset(), wasm::SymbolicAddress::ToInt32);
         } else {
             masm.setupUnalignedABICall(output);
             masm.passABIArg(input, MoveOp::DOUBLE);
-            masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
+            masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32), MoveOp::GENERAL,
+                             CheckUnsafeCallWithABI::DontCheckOther);
         }
         masm.storeCallWordResult(output);
 
         restoreVolatile(output);
     }
 
     masm.jump(ool->rejoin());
 }
@@ -826,20 +827,22 @@ CodeGeneratorX86::visitOutOfLineTruncate
         if (gen->compilingWasm())
             masm.setupWasmABICall();
         else
             masm.setupUnalignedABICall(output);
 
         masm.vcvtss2sd(input, input, input);
         masm.passABIArg(input.asDouble(), MoveOp::DOUBLE);
 
-        if (gen->compilingWasm())
+        if (gen->compilingWasm()) {
             masm.callWithABI(ins->mir()->bytecodeOffset(), wasm::SymbolicAddress::ToInt32);
-        else
-            masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
+        } else {
+            masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32), MoveOp::GENERAL,
+                             CheckUnsafeCallWithABI::DontCheckOther);
+        }
 
         masm.storeCallWordResult(output);
         masm.Pop(input);
 
         restoreVolatile(output);
     }
 
     masm.jump(ool->rejoin());
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -201,17 +201,17 @@ MacroAssemblerX86::handleFailureWithHand
 {
     // Reserve space for exception information.
     subl(Imm32(sizeof(ResumeFromException)), esp);
     movl(esp, eax);
 
     // Call the handler.
     asMasm().setupUnalignedABICall(ecx);
     asMasm().passABIArg(eax);
-    asMasm().callWithABI(handler);
+    asMasm().callWithABI(handler, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     Label entryFrame;
     Label catch_;
     Label finally;
     Label return_;
     Label bailout;
 
     loadPtr(Address(esp, offsetof(ResumeFromException, kind)), eax);
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -790,17 +790,17 @@ JitRuntime::generateVMWrapper(JSContext*
             break;
         }
     }
 
     // Copy the implicit outparam, if any.
     if (outReg != InvalidReg)
         masm.passABIArg(outReg);
 
-    masm.callWithABI(f.wrapped);
+    masm.callWithABI(f.wrapped, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     if (!generateTLExitVM(cx, masm, f))
         return nullptr;
 
     // Test for failure.
     switch (f.failType()) {
       case Type_Object:
         masm.branchTestPtr(Assembler::Zero, eax, eax, masm.failureLabel());
--- a/js/src/jsapi-tests/testBug604087.cpp
+++ b/js/src/jsapi-tests/testBug604087.cpp
@@ -9,24 +9,19 @@
 
 #include "jsobj.h"
 #include "jswrapper.h"
 
 #include "jsapi-tests/tests.h"
 
 #include "vm/ProxyObject.h"
 
-static const js::ClassExtension OuterWrapperClassExtension = PROXY_MAKE_EXT(
-    nullptr  /* objectMoved */
-);
-
-const js::Class OuterWrapperClass = PROXY_CLASS_WITH_EXT(
+const js::Class OuterWrapperClass = PROXY_CLASS_DEF(
     "Proxy",
-    JSCLASS_HAS_RESERVED_SLOTS(1), /* additional class flags */
-    &OuterWrapperClassExtension);
+    JSCLASS_HAS_RESERVED_SLOTS(1) /* additional class flags */);
 
 static JSObject*
 wrap(JSContext* cx, JS::HandleObject toWrap, JS::HandleObject target)
 {
     JSAutoCompartment ac(cx, target);
     JS::RootedObject wrapper(cx, toWrap);
     if (!JS_WrapObject(cx, &wrapper))
         return nullptr;
--- a/js/src/jsapi-tests/testGCGrayMarking.cpp
+++ b/js/src/jsapi-tests/testGCGrayMarking.cpp
@@ -22,21 +22,18 @@ struct DeletePolicy<js::ObjectWeakMap> :
 {};
 
 template <>
 struct MapTypeToRootKind<js::ObjectWeakMap*> {
     static const JS::RootKind kind = JS::RootKind::Traceable;
 };
 
 template <>
-struct GCPolicy<js::ObjectWeakMap*> {
-    static void trace(JSTracer* trc, js::ObjectWeakMap** tp, const char* name) {
-        (*tp)->trace(trc);
-    }
-};
+struct GCPolicy<js::ObjectWeakMap*> : public NonGCPointerPolicy<js::ObjectWeakMap*>
+{};
 
 } // namespace JS
 
 class AutoNoAnalysisForTest
 {
   public:
     AutoNoAnalysisForTest() {}
 } JS_HAZ_GC_SUPPRESSED;
--- a/js/src/jsapi-tests/testWeakMap.cpp
+++ b/js/src/jsapi-tests/testWeakMap.cpp
@@ -135,23 +135,25 @@ BEGIN_TEST(testWeakMap_keyDelegates)
     delegateRoot = nullptr;
     keyDelegate = nullptr;
     JS_GC(cx);
     CHECK(checkSize(map, 0));
 
     return true;
 }
 
-static void DelegateObjectMoved(JSObject* obj, const JSObject* old)
+static size_t
+DelegateObjectMoved(JSObject* obj, JSObject* old)
 {
     if (!keyDelegate)
-        return;  // Object got moved before we set keyDelegate to point to it.
+        return 0;  // Object got moved before we set keyDelegate to point to it.
 
     MOZ_RELEASE_ASSERT(keyDelegate == old);
     keyDelegate = obj;
+    return 0;
 }
 
 static JSObject* GetKeyDelegate(JSObject* obj)
 {
     return keyDelegate;
 }
 
 JSObject* newKey()
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2419,16 +2419,17 @@ ShiftMoveBoxedOrUnboxedDenseElements(JSO
     return DenseElementResult::Success;
 }
 
 DefineBoxedOrUnboxedFunctor1(ShiftMoveBoxedOrUnboxedDenseElements, JSObject*);
 
 void
 js::ArrayShiftMoveElements(JSObject* obj)
 {
+    AutoUnsafeCallWithABI unsafe;
     MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
 
     ShiftMoveBoxedOrUnboxedDenseElementsFunctor functor(obj);
     JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
 }
 
 template <JSValueType Type>
 DenseElementResult
--- a/js/src/jsboolinlines.h
+++ b/js/src/jsboolinlines.h
@@ -4,25 +4,28 @@
  * 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/. */
 
 #ifndef jsboolinlines_h
 #define jsboolinlines_h
 
 #include "jsbool.h"
 
+#include "jscntxt.h"
+
 #include "vm/BooleanObject.h"
 #include "vm/WrapperObject.h"
 
 namespace js {
 
 inline bool
 EmulatesUndefined(JSObject* obj)
 {
     // This may be called off the main thread. It's OK not to expose the object
     // here as it doesn't escape.
+    AutoUnsafeCallWithABI unsafe;
     JSObject* actual = MOZ_LIKELY(!obj->is<WrapperObject>()) ? obj : UncheckedUnwrapWithoutExpose(obj);
     return actual->getClass()->emulatesUndefined();
 }
 
 } /* namespace js */
 
 #endif /* jsboolinlines_h */
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1288,16 +1288,18 @@ JSContext::JSContext(JSRuntime* runtime,
     entryMonitor(nullptr),
     noExecuteDebuggerTop(nullptr),
     handlingSegFault(false),
     activityCallback(nullptr),
     activityCallbackArg(nullptr),
     requestDepth(0),
 #ifdef DEBUG
     checkRequestDepth(0),
+    inUnsafeCallWithABI(false),
+    hasAutoUnsafeCallWithABI(false),
 #endif
 #ifdef JS_SIMULATOR
     simulator_(nullptr),
 #endif
 #ifdef JS_TRACE_LOGGING
     traceLogger(nullptr),
 #endif
     autoFlushICache_(nullptr),
@@ -1663,8 +1665,27 @@ AutoEnterOOMUnsafeRegion::crash(size_t s
 {
     {
         JS::AutoSuppressGCAnalysis suppress;
         if (annotateOOMSizeCallback)
             annotateOOMSizeCallback(size);
     }
     crash(reason);
 }
+
+#ifdef DEBUG
+AutoUnsafeCallWithABI::AutoUnsafeCallWithABI()
+  : cx_(TlsContext.get()),
+    nested_(cx_->hasAutoUnsafeCallWithABI),
+    nogc(cx_)
+{
+    cx_->hasAutoUnsafeCallWithABI = true;
+}
+
+AutoUnsafeCallWithABI::~AutoUnsafeCallWithABI()
+{
+    MOZ_ASSERT(cx_->hasAutoUnsafeCallWithABI);
+    if (!nested_) {
+        cx_->hasAutoUnsafeCallWithABI = false;
+        cx_->inUnsafeCallWithABI = false;
+    }
+}
+#endif
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -374,16 +374,22 @@ struct JSContext : public JS::RootingCon
 
     js::Activation* profilingActivation() const {
         return profilingActivation_;
     }
     static size_t offsetOfProfilingActivation() {
         return offsetof(JSContext, profilingActivation_);
      }
 
+#ifdef DEBUG
+    static size_t offsetOfInUnsafeCallWithABI() {
+        return offsetof(JSContext, inUnsafeCallWithABI);
+    }
+#endif
+
   private:
     /* Space for interpreter frames. */
     js::ThreadLocalData<js::InterpreterStack> interpreterStack_;
 
   public:
     js::InterpreterStack& interpreterStack() {
         return interpreterStack_.ref();
     }
@@ -414,16 +420,18 @@ struct JSContext : public JS::RootingCon
     js::ThreadLocalData<void*>                activityCallbackArg;
     void triggerActivityCallback(bool active);
 
     /* The request depth for this thread. */
     js::ThreadLocalData<unsigned> requestDepth;
 
 #ifdef DEBUG
     js::ThreadLocalData<unsigned> checkRequestDepth;
+    js::ThreadLocalData<uint32_t> inUnsafeCallWithABI;
+    js::ThreadLocalData<bool> hasAutoUnsafeCallWithABI;
 #endif
 
 #ifdef JS_SIMULATOR
   private:
     js::ThreadLocalData<js::jit::Simulator*> simulator_;
   public:
     js::jit::Simulator* simulator() const;
     uintptr_t* addressOfSimulatorStackLimit();
@@ -1281,16 +1289,33 @@ class MOZ_RAII AutoEnterIonCompilation
         cx->ionCompiling = false;
         cx->ionCompilingSafeForMinorGC = false;
 #endif
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
+// Should be used in functions called directly from JIT code (with
+// masm.callWithABI) to assert invariants in debug builds.
+class MOZ_RAII AutoUnsafeCallWithABI
+{
+#ifdef DEBUG
+    JSContext* cx_;
+    bool nested_;
+#endif
+    JS::AutoCheckCannotGC nogc;
+
+  public:
+#ifdef DEBUG
+    AutoUnsafeCallWithABI();
+    ~AutoUnsafeCallWithABI();
+#endif
+};
+
 namespace gc {
 
 // In debug builds, set/unset the performing GC flag for the current thread.
 struct MOZ_RAII AutoSetThreadIsPerformingGC
 {
 #ifdef DEBUG
     AutoSetThreadIsPerformingGC()
       : cx(TlsContext.get())
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -8078,16 +8078,17 @@ JS::AssertGCThingIsNotAnObjectSubclass(C
 {
     MOZ_ASSERT(cell);
     MOZ_ASSERT(cell->getTraceKind() != JS::TraceKind::Object);
 }
 
 JS_FRIEND_API(void)
 js::gc::AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind)
 {
+    MOZ_ASSERT(IsCellPointerValid(cell));
     if (!cell)
         MOZ_ASSERT(kind == JS::TraceKind::Null);
     else if (IsInsideNursery(cell))
         MOZ_ASSERT(kind == JS::TraceKind::Object);
     else
         MOZ_ASSERT(MapAllocToTraceKind(cell->asTenured().getAllocKind()) == kind);
 }
 #endif
--- a/js/src/jslibmath.h
+++ b/js/src/jslibmath.h
@@ -6,16 +6,17 @@
 
 #ifndef jslibmath_h
 #define jslibmath_h
 
 #include "mozilla/FloatingPoint.h"
 
 #include <math.h>
 
+#include "jscntxt.h"
 #include "jsnum.h"
 
 /*
  * Use system provided math routines.
  */
 
 /* The right copysign function is not always named the same thing. */
 #ifdef __GNUC__
@@ -43,16 +44,17 @@ js_fmod(double d, double d2)
     return fmod(d, d2);
 }
 
 namespace js {
 
 inline double
 NumberDiv(double a, double b)
 {
+    AutoUnsafeCallWithABI unsafe;
     if (b == 0) {
         if (a == 0 || mozilla::IsNaN(a)
 #ifdef XP_WIN
             || mozilla::IsNaN(b) /* XXX MSVC miscompiles such that (NaN == 0) */
 #endif
         )
             return JS::GenericNaN();
 
@@ -60,17 +62,19 @@ NumberDiv(double a, double b)
             return mozilla::NegativeInfinity<double>();
         return mozilla::PositiveInfinity<double>();
     }
 
     return a / b;
 }
 
 inline double
-NumberMod(double a, double b) {
+NumberMod(double a, double b)
+{
+    AutoUnsafeCallWithABI unsafe;
     if (b == 0)
         return JS::GenericNaN();
     return js_fmod(a, b);
 }
 
 } // namespace js
 
 #endif /* jslibmath_h */
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -178,22 +178,24 @@ js::math_abs(JSContext* cx, unsigned arg
     }
 
     return math_abs_handle(cx, args[0], args.rval());
 }
 
 double
 js::math_acos_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::acos, x, MathCache::Acos);
 }
 
 double
 js::math_acos_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::acos(x);
 }
 
 bool
 js::math_acos(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -213,22 +215,24 @@ js::math_acos(JSContext* cx, unsigned ar
     double z = math_acos_impl(mathCache, x);
     args.rval().setDouble(z);
     return true;
 }
 
 double
 js::math_asin_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::asin, x, MathCache::Asin);
 }
 
 double
 js::math_asin_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::asin(x);
 }
 
 bool
 js::math_asin(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -248,22 +252,24 @@ js::math_asin(JSContext* cx, unsigned ar
     double z = math_asin_impl(mathCache, x);
     args.rval().setDouble(z);
     return true;
 }
 
 double
 js::math_atan_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::atan, x, MathCache::Atan);
 }
 
 double
 js::math_atan_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::atan(x);
 }
 
 bool
 js::math_atan(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -283,16 +289,17 @@ js::math_atan(JSContext* cx, unsigned ar
     double z = math_atan_impl(mathCache, x);
     args.rval().setDouble(z);
     return true;
 }
 
 double
 js::ecmaAtan2(double y, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::atan2(y, x);
 }
 
 bool
 js::math_atan2_handle(JSContext* cx, HandleValue y, HandleValue x, MutableHandleValue res)
 {
     double dy;
     if (!ToNumber(cx, y, &dy))
@@ -313,16 +320,17 @@ js::math_atan2(JSContext* cx, unsigned a
     CallArgs args = CallArgsFromVp(argc, vp);
 
     return math_atan2_handle(cx, args.get(0), args.get(1), args.rval());
 }
 
 double
 js::math_ceil_impl(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::ceil(x);
 }
 
 bool
 js::math_ceil_handle(JSContext* cx, HandleValue v, MutableHandleValue res)
 {
     double d;
     if(!ToNumber(cx, v, &d))
@@ -367,22 +375,24 @@ js::math_clz32(JSContext* cx, unsigned a
 
     args.rval().setInt32(mozilla::CountLeadingZeroes32(n));
     return true;
 }
 
 double
 js::math_cos_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(cos, x, MathCache::Cos);
 }
 
 double
 js::math_cos_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cos(x);
 }
 
 bool
 js::math_cos(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -402,22 +412,24 @@ js::math_cos(JSContext* cx, unsigned arg
     double z = math_cos_impl(mathCache, x);
     args.rval().setDouble(z);
     return true;
 }
 
 double
 js::math_exp_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::exp, x, MathCache::Exp);
 }
 
 double
 js::math_exp_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::exp(x);
 }
 
 bool
 js::math_exp(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -437,16 +449,17 @@ js::math_exp(JSContext* cx, unsigned arg
     double z = math_exp_impl(mathCache, x);
     args.rval().setNumber(z);
     return true;
 }
 
 double
 js::math_floor_impl(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::floor(x);
 }
 
 bool
 js::math_floor_handle(JSContext* cx, HandleValue v, MutableHandleValue r)
 {
     double d;
     if (!ToNumber(cx, v, &d))
@@ -527,22 +540,24 @@ js::math_fround(JSContext* cx, unsigned 
     }
 
     return RoundFloat32(cx, args[0], args.rval());
 }
 
 double
 js::math_log_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(math_log_uncached, x, MathCache::Log);
 }
 
 double
 js::math_log_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::log(x);
 }
 
 bool
 js::math_log_handle(JSContext* cx, HandleValue val, MutableHandleValue res)
 {
     double in;
     if (!ToNumber(cx, val, &in))
@@ -568,16 +583,18 @@ js::math_log(JSContext* cx, unsigned arg
     }
 
     return math_log_handle(cx, args[0], args.rval());
 }
 
 double
 js::math_max_impl(double x, double y)
 {
+    AutoUnsafeCallWithABI unsafe;
+
     // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
     if (x > y || IsNaN(x) || (x == y && IsNegative(y)))
         return x;
     return y;
 }
 
 bool
 js::math_max(JSContext* cx, unsigned argc, Value* vp)
@@ -593,16 +610,18 @@ js::math_max(JSContext* cx, unsigned arg
     }
     args.rval().setNumber(maxval);
     return true;
 }
 
 double
 js::math_min_impl(double x, double y)
 {
+    AutoUnsafeCallWithABI unsafe;
+
     // Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
     if (x < y || IsNaN(x) || (x == y && IsNegativeZero(x)))
         return x;
     return y;
 }
 
 bool
 js::math_min(JSContext* cx, unsigned argc, Value* vp)
@@ -636,16 +655,17 @@ js::minmax_impl(JSContext* cx, bool max,
         res.setNumber(math_min_impl(x, y));
 
     return true;
 }
 
 double
 js::powi(double x, int y)
 {
+    AutoUnsafeCallWithABI unsafe;
     unsigned n = (y < 0) ? -y : y;
     double m = x;
     double p = 1;
     while (true) {
         if ((n & 1) != 0) p *= m;
         n >>= 1;
         if (n == 0) {
             if (y < 0) {
@@ -664,16 +684,18 @@ js::powi(double x, int y)
         }
         m *= m;
     }
 }
 
 double
 js::ecmaPow(double x, double y)
 {
+    AutoUnsafeCallWithABI unsafe;
+
     /*
      * Use powi if the exponent is an integer-valued double. We don't have to
      * check for NaN since a comparison with NaN is always false.
      */
     int32_t yi;
     if (NumberEqualsInt32(y, &yi))
         return powi(x, yi);
 
@@ -820,31 +842,35 @@ js::GetBiggestNumberLessThan(T x)
 }
 
 template double js::GetBiggestNumberLessThan<>(double x);
 template float js::GetBiggestNumberLessThan<>(float x);
 
 double
 js::math_round_impl(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
+
     int32_t ignored;
     if (NumberIsInt32(x, &ignored))
         return x;
 
     /* Some numbers are so big that adding 0.5 would give the wrong number. */
     if (ExponentComponent(x) >= int_fast16_t(FloatingPoint<double>::kExponentShift))
         return x;
 
     double add = (x >= 0) ? GetBiggestNumberLessThan(0.5) : 0.5;
     return js_copysign(fdlibm::floor(x + add), x);
 }
 
 float
 js::math_roundf_impl(float x)
 {
+    AutoUnsafeCallWithABI unsafe;
+
     int32_t ignored;
     if (NumberIsInt32(x, &ignored))
         return x;
 
     /* Some numbers are so big that adding 0.5 would give the wrong number. */
     if (ExponentComponent(x) >= int_fast16_t(FloatingPoint<float>::kExponentShift))
         return x;
 
@@ -863,22 +889,24 @@ js::math_round(JSContext* cx, unsigned a
     }
 
     return math_round_handle(cx, args[0], args.rval());
 }
 
 double
 js::math_sin_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(math_sin_uncached, x, MathCache::Sin);
 }
 
 double
 js::math_sin_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
 #ifdef _WIN64
     // Workaround MSVC bug where sin(-0) is +0 instead of -0 on x64 on
     // CPUs without FMA3 (pre-Haswell). See bug 1076670.
     if (IsNegativeZero(x))
         return -0.0;
 #endif
     return sin(x);
 }
@@ -910,29 +938,31 @@ js::math_sin(JSContext* cx, unsigned arg
     }
 
     return math_sin_handle(cx, args[0], args.rval());
 }
 
 void
 js::math_sincos_uncached(double x, double *sin, double *cos)
 {
+    AutoUnsafeCallWithABI unsafe;
 #if defined(HAVE_SINCOS)
     sincos(x, sin, cos);
 #elif defined(HAVE___SINCOS)
     __sincos(x, sin, cos);
 #else
     *sin = js::math_sin_uncached(x);
     *cos = js::math_cos_uncached(x);
 #endif
 }
 
 void
 js::math_sincos_impl(MathCache* mathCache, double x, double *sin, double *cos)
 {
+    AutoUnsafeCallWithABI unsafe;
     unsigned indexSin;
     unsigned indexCos;
     bool hasSin = mathCache->isCached(x, MathCache::Sin, sin, &indexSin);
     bool hasCos = mathCache->isCached(x, MathCache::Cos, cos, &indexCos);
     if (!(hasSin || hasCos)) {
         js::math_sincos_uncached(x, sin, cos);
         mathCache->store(MathCache::Sin, x, *sin, indexSin);
         mathCache->store(MathCache::Cos, x, *cos, indexCos);
@@ -973,22 +1003,24 @@ js::math_sqrt(JSContext* cx, unsigned ar
     }
 
     return math_sqrt_handle(cx, args[0], args.rval());
 }
 
 double
 js::math_tan_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(tan, x, MathCache::Tan);
 }
 
 double
 js::math_tan_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return tan(x);
 }
 
 bool
 js::math_tan(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -1032,197 +1064,218 @@ static bool math_function(JSContext* cx,
     args.rval().setNumber(z);
 
     return true;
 }
 
 double
 js::math_log10_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::log10, x, MathCache::Log10);
 }
 
 double
 js::math_log10_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::log10(x);
 }
 
 bool
 js::math_log10(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_log10_impl>(cx, argc, vp);
 }
 
 double
 js::math_log2_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::log2, x, MathCache::Log2);
 }
 
 double
 js::math_log2_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::log2(x);
 }
 
 bool
 js::math_log2(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_log2_impl>(cx, argc, vp);
 }
 
 double
 js::math_log1p_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::log1p, x, MathCache::Log1p);
 }
 
 double
 js::math_log1p_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::log1p(x);
 }
 
 bool
 js::math_log1p(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_log1p_impl>(cx, argc, vp);
 }
 
 double
 js::math_expm1_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::expm1, x, MathCache::Expm1);
 }
 
 double
 js::math_expm1_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::expm1(x);
 }
 
 bool
 js::math_expm1(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_expm1_impl>(cx, argc, vp);
 }
 
 double
 js::math_cosh_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::cosh, x, MathCache::Cosh);
 }
 
 double
 js::math_cosh_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::cosh(x);
 }
 
 bool
 js::math_cosh(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_cosh_impl>(cx, argc, vp);
 }
 
 double
 js::math_sinh_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::sinh, x, MathCache::Sinh);
 }
 
 double
 js::math_sinh_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::sinh(x);
 }
 
 bool
 js::math_sinh(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_sinh_impl>(cx, argc, vp);
 }
 
 double
 js::math_tanh_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::tanh, x, MathCache::Tanh);
 }
 
 double
 js::math_tanh_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::tanh(x);
 }
 
 bool
 js::math_tanh(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_tanh_impl>(cx, argc, vp);
 }
 
 double
 js::math_acosh_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::acosh, x, MathCache::Acosh);
 }
 
 double
 js::math_acosh_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::acosh(x);
 }
 
 bool
 js::math_acosh(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_acosh_impl>(cx, argc, vp);
 }
 
 double
 js::math_asinh_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::asinh, x, MathCache::Asinh);
 }
 
 double
 js::math_asinh_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::asinh(x);
 }
 
 bool
 js::math_asinh(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_asinh_impl>(cx, argc, vp);
 }
 
 double
 js::math_atanh_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::atanh, x, MathCache::Atanh);
 }
 
 double
 js::math_atanh_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::atanh(x);
 }
 
 bool
 js::math_atanh(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_atanh_impl>(cx, argc, vp);
 }
 
 /* Consistency wrapper for platform deviations in hypot() */
 double
 js::ecmaHypot(double x, double y)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::hypot(x, y);
 }
 
 static inline
 void
 hypot_step(double& scale, double& sumsq, double x)
 {
     double xabs = mozilla::Abs(x);
@@ -1232,16 +1285,18 @@ hypot_step(double& scale, double& sumsq,
     } else if (scale != 0) {
         sumsq += (xabs / scale) * (xabs / scale);
     }
 }
 
 double
 js::hypot4(double x, double y, double z, double w)
 {
+    AutoUnsafeCallWithABI unsafe;
+
     /* Check for infinity or NaNs so that we can return immediatelly.
      * Does not need to be WIN_XP specific as ecmaHypot
      */
     if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y) ||
             mozilla::IsInfinite(z) || mozilla::IsInfinite(w))
         return mozilla::PositiveInfinity<double>();
 
     if (mozilla::IsNaN(x) || mozilla::IsNaN(y) || mozilla::IsNaN(z) ||
@@ -1257,16 +1312,17 @@ js::hypot4(double x, double y, double z,
     hypot_step(scale, sumsq, w);
 
     return scale * sqrt(sumsq);
 }
 
 double
 js::hypot3(double x, double y, double z)
 {
+    AutoUnsafeCallWithABI unsafe;
     return hypot4(x, y, z, 0.0);
 }
 
 bool
 js::math_hypot(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return math_hypot_handle(cx, args, args.rval());
@@ -1313,22 +1369,24 @@ js::math_hypot_handle(JSContext* cx, Han
                     scale * sqrt(sumsq);
     res.setNumber(result);
     return true;
 }
 
 double
 js::math_trunc_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::trunc, x, MathCache::Trunc);
 }
 
 double
 js::math_trunc_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::trunc(x);
 }
 
 bool
 js::math_trunc(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_trunc_impl>(cx, argc, vp);
 }
@@ -1339,40 +1397,44 @@ static double sign(double x)
         return GenericNaN();
 
     return x == 0 ? x : x < 0 ? -1 : 1;
 }
 
 double
 js::math_sign_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(sign, x, MathCache::Sign);
 }
 
 double
 js::math_sign_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return sign(x);
 }
 
 bool
 js::math_sign(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_sign_impl>(cx, argc, vp);
 }
 
 double
 js::math_cbrt_impl(MathCache* cache, double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return cache->lookup(fdlibm::cbrt, x, MathCache::Cbrt);
 }
 
 double
 js::math_cbrt_uncached(double x)
 {
+    AutoUnsafeCallWithABI unsafe;
     return fdlibm::cbrt(x);
 }
 
 bool
 js::math_cbrt(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_cbrt_impl>(cx, argc, vp);
 }
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -368,19 +368,20 @@ BaseProxyHandler::trace(JSTracer* trc, J
 {
 }
 
 void
 BaseProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const
 {
 }
 
-void
-BaseProxyHandler::objectMoved(JSObject* proxy, const JSObject* old) const
+size_t
+BaseProxyHandler::objectMoved(JSObject* proxy, JSObject* old) const
 {
+    return 0;
 }
 
 JSObject*
 BaseProxyHandler::weakmapKeyDelegate(JSObject* proxy) const
 {
     return nullptr;
 }
 
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -736,21 +736,29 @@ proxy_Finalize(FreeOp* fop, JSObject* ob
 
     MOZ_ASSERT(obj->is<ProxyObject>());
     obj->as<ProxyObject>().handler()->finalize(fop, obj);
 
     if (!obj->as<ProxyObject>().usingInlineValueArray())
         js_free(js::detail::GetProxyDataLayout(obj)->values());
 }
 
-static void
-proxy_ObjectMoved(JSObject* obj, const JSObject* old)
+size_t
+js::proxy_ObjectMoved(JSObject* obj, JSObject* old)
 {
-    MOZ_ASSERT(obj->is<ProxyObject>());
-    obj->as<ProxyObject>().handler()->objectMoved(obj, old);
+    ProxyObject& proxy = obj->as<ProxyObject>();
+
+    if (IsInsideNursery(old)) {
+        // Objects in the nursery are never swapped so the proxy must have an
+        // inline ProxyValueArray.
+        MOZ_ASSERT(old->as<ProxyObject>().usingInlineValueArray());
+        proxy.setInlineValueArray();
+    }
+
+    return proxy.handler()->objectMoved(obj, old);
 }
 
 bool
 js::proxy_Call(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject proxy(cx, &args.callee());
     MOZ_ASSERT(proxy->is<ProxyObject>());
@@ -775,19 +783,20 @@ const ClassOps js::ProxyClassOps = {
     nullptr,                 /* mayResolve  */
     proxy_Finalize,          /* finalize    */
     nullptr,                 /* call        */
     Proxy::hasInstance,      /* hasInstance */
     nullptr,                 /* construct   */
     ProxyObject::trace,      /* trace       */
 };
 
-const ClassExtension js::ProxyClassExtension = PROXY_MAKE_EXT(
+const ClassExtension js::ProxyClassExtension = {
+    proxy_WeakmapKeyDelegate,
     proxy_ObjectMoved
-);
+};
 
 const ObjectOps js::ProxyObjectOps = {
     proxy_LookupProperty,
     Proxy::defineProperty,
     Proxy::has,
     Proxy::get,
     Proxy::set,
     Proxy::getOwnPropertyDescriptor,
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -75,16 +75,18 @@ class Proxy
 
     static void trace(JSTracer* trc, JSObject* obj);
 };
 
 bool
 proxy_Call(JSContext* cx, unsigned argc, Value* vp);
 bool
 proxy_Construct(JSContext* cx, unsigned argc, Value* vp);
+size_t
+proxy_ObjectMoved(JSObject* obj, JSObject* old);
 
 // These functions are used by JIT code
 
 bool
 ProxyHasOwn(JSContext* cx, HandleObject proxy, HandleValue idVal, MutableHandleValue result);
 
 bool
 ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp);
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -583,18 +583,19 @@ osfile_close(JSContext* cx, unsigned arg
 
     args.rval().setUndefined();
     return true;
 }
 
 static const JSFunctionSpecWithHelp osfile_functions[] = {
     JS_FN_HELP("readFile", osfile_readFile, 1, 0,
 "readFile(filename, [\"binary\"])",
-"  Read filename into returned string. Filename is relative to the current\n"
-               "  working directory."),
+"  Read entire contents of filename. Returns a string, unless \"binary\" is passed\n"
+"  as the second argument, in which case it returns a Uint8Array. Filename is\n"
+"  relative to the current working directory."),
 
     JS_FN_HELP("readRelativeToScript", osfile_readRelativeToScript, 1, 0,
 "readRelativeToScript(filename, [\"binary\"])",
 "  Read filename into returned string. Filename is relative to the directory\n"
 "  containing the current script."),
 
     JS_FS_HELP_END
 };
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -58,16 +58,17 @@
 #include "jsutil.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "jswrapper.h"
 #include "shellmoduleloader.out.h"
 
 #include "builtin/ModuleObject.h"
+#include "builtin/RegExp.h"
 #include "builtin/TestingFunctions.h"
 #include "frontend/Parser.h"
 #include "gc/GCInternals.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
 #include "jit/JitcodeMap.h"
 #include "jit/OptimizationTracking.h"
@@ -6279,17 +6280,17 @@ static const JSFunctionSpecWithHelp shel
 "putstr([exp])",
 "  Evaluate and print expression without newline."),
 
     JS_FN_HELP("dateNow", Now, 0, 0,
 "dateNow()",
 "  Return the current time with sub-ms precision."),
 
     JS_FN_HELP("help", Help, 0, 0,
-"help([name ...])",
+"help([function or interface object or /pattern/])",
 "  Display usage and help messages."),
 
     JS_FN_HELP("quit", Quit, 0, 0,
 "quit()",
 "  Quit the shell."),
 
     JS_FN_HELP("assertEq", AssertEq, 2, 0,
 "assertEq(actual, expected[, msg])",
@@ -6857,68 +6858,116 @@ PrintHelp(JSContext* cx, HandleObject ob
 
     if (!usage.isString() || !help.isString())
         return true;
 
     return PrintHelpString(cx, usage) && PrintHelpString(cx, help);
 }
 
 static bool
-PrintEnumeratedHelp(JSContext* cx, HandleObject obj, bool brief)
+PrintEnumeratedHelp(JSContext* cx, HandleObject obj, HandleObject pattern, bool brief)
 {
     AutoIdVector idv(cx);
     if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &idv))
         return false;
 
+    Rooted<RegExpObject*> regex(cx);
+    if (pattern)
+        regex = &UncheckedUnwrap(pattern)->as<RegExpObject>();
+
     for (size_t i = 0; i < idv.length(); i++) {
         RootedValue v(cx);
         RootedId id(cx, idv[i]);
         if (!JS_GetPropertyById(cx, obj, id, &v))
             return false;
-        if (v.isObject()) {
-            RootedObject funcObj(cx, &v.toObject());
-            if (!PrintHelp(cx, funcObj))
+        if (!v.isObject())
+            continue;
+
+        RootedObject funcObj(cx, &v.toObject());
+        if (regex) {
+            // Only pay attention to objects with a 'help' property, which will
+            // either be documented functions or interface objects.
+            if (!JS_GetProperty(cx, funcObj, "help", &v))
+                return false;
+            if (!v.isString())
+                continue;
+
+            // For functions, match against the name. For interface objects,
+            // match against the usage string.
+            if (!JS_GetProperty(cx, funcObj, "name", &v))
                 return false;
-        }
+            if (!v.isString()) {
+                if (!JS_GetProperty(cx, funcObj, "usage", &v))
+                    return false;
+                if (!v.isString())
+                    continue;
+            }
+
+            size_t ignored = 0;
+            if (!JSString::ensureLinear(cx, v.toString()))
+                return false;
+            RootedLinearString input(cx, &v.toString()->asLinear());
+            if (!ExecuteRegExpLegacy(cx, nullptr, regex, input, &ignored, true, &v))
+                return false;
+            if (v.isNull())
+                continue;
+        }
+
+        if (!PrintHelp(cx, funcObj))
+            return false;
     }
 
     return true;
 }
 
 static bool
 Help(JSContext* cx, unsigned argc, Value* vp)
 {
     if (!gOutFile->isOpen()) {
         JS_ReportErrorASCII(cx, "output file is closed");
         return false;
     }
 
     CallArgs args = CallArgsFromVp(argc, vp);
-
-    RootedObject obj(cx);
+    args.rval().setUndefined();
+    RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
+
+    // help() - display the version and dump out help for all functions on the
+    // global.
     if (args.length() == 0) {
         fprintf(gOutFile->fp, "%s\n", JS_GetImplementationVersion());
 
-        RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
-        if (!PrintEnumeratedHelp(cx, global, false))
-            return false;
-    } else {
-        for (unsigned i = 0; i < args.length(); i++) {
-            if (args[i].isPrimitive()) {
-                JS_ReportErrorASCII(cx, "primitive arg");
-                return false;
-            }
-            obj = args[i].toObjectOrNull();
-            if (!PrintHelp(cx, obj))
-                return false;
-        }
-    }
-
-    args.rval().setUndefined();
-    return true;
+        if (!PrintEnumeratedHelp(cx, global, nullptr, false))
+            return false;
+        return true;
+    }
+
+    RootedValue v(cx);
+
+    if (args[0].isPrimitive()) {
+        // help("foo")
+        JS_ReportErrorASCII(cx, "primitive arg");
+        return false;
+    }
+
+    RootedObject obj(cx, &args[0].toObject());
+    if (!obj)
+        return true;
+    bool isRegexp;
+    if (!JS_ObjectIsRegExp(cx, obj, &isRegexp))
+        return false;
+
+    if (isRegexp) {
+        // help(/pattern/)
+        return PrintEnumeratedHelp(cx, global, obj, false);
+    }
+
+    // help(function)
+    // help(namespace_obj)
+    return PrintHelp(cx, obj);
 }
 
 static const JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = {
 #define MSG_DEF(name, count, exception, format) \
     { #name, format, count, JSEXN_ERR } ,
 #include "jsshell.msg"
 #undef MSG_DEF
 };
--- a/js/src/shell/jsshell.cpp
+++ b/js/src/shell/jsshell.cpp
@@ -3,16 +3,18 @@
  * 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/. */
 
 // jsshell.cpp - Utilities for the JS shell
 
 #include "shell/jsshell.h"
 
+#include "mozilla/Sprintf.h"
+
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 #include "vm/StringBuffer.h"
 
 using namespace JS;
 
 namespace js {
@@ -33,24 +35,17 @@ namespace shell {
 bool
 GenerateInterfaceHelp(JSContext* cx, HandleObject obj, const char* name)
 {
     AutoIdVector idv(cx);
     if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &idv))
         return false;
 
     StringBuffer buf(cx);
-    if (!buf.append(name, strlen(name)) || !buf.append(" - interface object", 19))
-        return false;
-    RootedString s(cx, buf.finishString());
-    if (!s || !JS_DefineProperty(cx, obj, "usage", s, 0))
-        return false;
-    buf.clear();
-
-    bool first = true;
+    int numEntries = 0;
     for (size_t i = 0; i < idv.length(); i++) {
         RootedId id(cx, idv[i]);
         RootedValue v(cx);
         if (!JS_GetPropertyById(cx, obj, id, &v))
             return false;
         if (!v.isObject())
             continue;
         RootedObject prop(cx, &v.toObject());
@@ -59,29 +54,40 @@ GenerateInterfaceHelp(JSContext* cx, Han
         RootedValue help(cx);
         if (!JS_GetProperty(cx, prop, "usage", &usage))
             return false;
         if (!JS_GetProperty(cx, prop, "help", &help))
             return false;
         if (!usage.isString() && !help.isString())
             continue;
 
-        if (!first && !buf.append("\n"))
+        if (numEntries && !buf.append("\n"))
             return false;
-        first = false;
+        numEntries++;
 
         if (!buf.append("  ", 2))
             return false;
 
         if (!buf.append(usage.isString() ? usage.toString() : JSID_TO_FLAT_STRING(id)))
             return false;
     }
 
+    RootedString s(cx, buf.finishString());
+    if (!s || !JS_DefineProperty(cx, obj, "help", s, 0))
+        return false;
+
+    buf.clear();
+    if (!buf.append(name, strlen(name)) || !buf.append(" - interface object with ", 25))
+        return false;
+    char cbuf[100];
+    SprintfLiteral(cbuf, "%d %s", numEntries, numEntries == 1 ? "entry" : "entries");
+    if (!buf.append(cbuf, strlen(cbuf)))
+        return false;
     s = buf.finishString();
-    if (!s || !JS_DefineProperty(cx, obj, "help", s, 0))
+    if (!s || !JS_DefineProperty(cx, obj, "usage", s, 0))
         return false;
 
     return true;
 }
 
 bool
 CreateAlias(JSContext* cx, const char* dstName, JS::HandleObject namespaceObj, const char* srcName)
 {
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -366,17 +366,17 @@ ArgumentsObject::createForIon(JSContext*
 }
 
 /* static */ ArgumentsObject*
 ArgumentsObject::finishForIon(JSContext* cx, jit::JitFrameLayout* frame,
                               JSObject* scopeChain, ArgumentsObject* obj)
 {
     // JIT code calls this directly (no callVM), because it's faster, so we're
     // not allowed to GC in here.
-    JS::AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
 
     JSFunction* callee = jit::CalleeTokenToFunction(frame->calleeToken());
     RootedObject callObj(cx, scopeChain->is<CallObject>() ? scopeChain : nullptr);
     CopyJitFrameArgs copy(frame, callObj);
 
     unsigned numActuals = frame->numActualArgs();
     unsigned numFormals = callee->nargs();
     unsigned numArgs = Max(numActuals, numFormals);
@@ -850,22 +850,25 @@ void
 ArgumentsObject::trace(JSTracer* trc, JSObject* obj)
 {
     ArgumentsObject& argsobj = obj->as<ArgumentsObject>();
     if (ArgumentsData* data = argsobj.data()) // Template objects have no ArgumentsData.
         TraceRange(trc, data->numArgs, data->begin(), js_arguments_str);
 }
 
 /* static */ size_t
-ArgumentsObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src)
+ArgumentsObject::objectMoved(JSObject* dst, JSObject* src)
 {
     ArgumentsObject* ndst = &dst->as<ArgumentsObject>();
-    ArgumentsObject* nsrc = &src->as<ArgumentsObject>();
+    const ArgumentsObject* nsrc = &src->as<ArgumentsObject>();
     MOZ_ASSERT(ndst->data() == nsrc->data());
 
+    if (!IsInsideNursery(src))
+        return 0;
+
     Nursery& nursery = dst->zone()->group()->nursery();
 
     size_t nbytesTotal = 0;
     if (!nursery.isInside(nsrc->data())) {
         nursery.removeMallocedBuffer(nsrc->data());
     } else {
         AutoEnterOOMUnsafeRegion oomUnsafe;
         uint32_t nbytes = ArgumentsData::bytesRequired(nsrc->data()->numArgs);
@@ -912,31 +915,36 @@ const ClassOps MappedArgumentsObject::cl
     ArgumentsObject::obj_mayResolve,
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
 
+const js::ClassExtension MappedArgumentsObject::classExt_ = {
+    nullptr,                      /* weakmapKeyDelegateOp */
+    ArgumentsObject::objectMoved  /* objectMovedOp */
+};
+
 const ObjectOps MappedArgumentsObject::objectOps_ = {
     nullptr,                 /* lookupProperty */
     MappedArgumentsObject::obj_defineProperty
 };
 
 const Class MappedArgumentsObject::class_ = {
     "Arguments",
     JSCLASS_DELAY_METADATA_BUILDER |
     JSCLASS_HAS_RESERVED_SLOTS(MappedArgumentsObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
     JSCLASS_SKIP_NURSERY_FINALIZE |
     JSCLASS_BACKGROUND_FINALIZE,
     &MappedArgumentsObject::classOps_,
     nullptr,
-    nullptr,
+    &MappedArgumentsObject::classExt_,
     &MappedArgumentsObject::objectOps_
 };
 
 /*
  * Unmapped arguments is significantly less magical than mapped arguments, so
  * it is represented by a different class while sharing some functionality.
  */
 const ClassOps UnmappedArgumentsObject::classOps_ = {
@@ -948,17 +956,24 @@ const ClassOps UnmappedArgumentsObject::
     ArgumentsObject::obj_mayResolve,
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
 
+const js::ClassExtension UnmappedArgumentsObject::classExt_ = {
+    nullptr,                      /* weakmapKeyDelegateOp */
+    ArgumentsObject::objectMoved  /* objectMovedOp */
+};
+
 const Class UnmappedArgumentsObject::class_ = {
     "Arguments",
     JSCLASS_DELAY_METADATA_BUILDER |
     JSCLASS_HAS_RESERVED_SLOTS(UnmappedArgumentsObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
     JSCLASS_SKIP_NURSERY_FINALIZE |
     JSCLASS_BACKGROUND_FINALIZE,
-    &UnmappedArgumentsObject::classOps_
+    &UnmappedArgumentsObject::classOps_,
+    nullptr,
+    &UnmappedArgumentsObject::classExt_
 };
--- a/js/src/vm/ArgumentsObject.h
+++ b/js/src/vm/ArgumentsObject.h
@@ -356,17 +356,17 @@ class ArgumentsObject : public NativeObj
     }
     size_t sizeOfData() const {
         return ArgumentsData::bytesRequired(data()->numArgs) +
                (maybeRareData() ? RareArgumentsData::bytesRequired(initialLength()) : 0);
     }
 
     static void finalize(FreeOp* fop, JSObject* obj);
     static void trace(JSTracer* trc, JSObject* obj);
-    static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src);
+    static size_t objectMoved(JSObject* dst, JSObject* src);
 
     /* For jit use: */
     static size_t getDataSlotOffset() {
         return getFixedSlotOffset(DATA_SLOT);
     }
     static size_t getInitialLengthSlotOffset() {
         return getFixedSlotOffset(INITIAL_LENGTH_SLOT);
     }
@@ -394,16 +394,17 @@ class ArgumentsObject : public NativeObj
                                          ArgumentsData* data);
     static void MaybeForwardToCallObject(jit::JitFrameLayout* frame, HandleObject callObj,
                                          ArgumentsObject* obj, ArgumentsData* data);
 };
 
 class MappedArgumentsObject : public ArgumentsObject
 {
     static const ClassOps classOps_;
+    static const ClassExtension classExt_;
     static const ObjectOps objectOps_;
 
   public:
     static const Class class_;
 
     JSFunction& callee() const {
         return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
     }
@@ -423,16 +424,17 @@ class MappedArgumentsObject : public Arg
     static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp);
     static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    Handle<JS::PropertyDescriptor> desc, ObjectOpResult& result);
 };
 
 class UnmappedArgumentsObject : public ArgumentsObject
 {
     static const ClassOps classOps_;
+    static const ClassExtension classExt_;
 
   public:
     static const Class class_;
 
   private:
     static bool obj_enumerate(JSContext* cx, HandleObject obj);
     static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp);
 };
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -1249,25 +1249,27 @@ ArrayBufferObject::trace(JSTracer* trc, 
 
     JSObject* view = MaybeForwarded(buf.firstView());
     MOZ_ASSERT(view && view->is<InlineTransparentTypedObject>());
 
     TraceManuallyBarrieredEdge(trc, &view, "array buffer inline typed object owner");
     buf.setSlot(DATA_SLOT, PrivateValue(view->as<InlineTransparentTypedObject>().inlineTypedMem()));
 }
 
-/* static */ void
-ArrayBufferObject::objectMoved(JSObject* obj, const JSObject* old)
+/* static */ size_t
+ArrayBufferObject::objectMoved(JSObject* obj, JSObject* old)
 {
     ArrayBufferObject& dst = obj->as<ArrayBufferObject>();
     const ArrayBufferObject& src = old->as<ArrayBufferObject>();
 
     // Fix up possible inline data pointer.
     if (src.hasInlineData())
         dst.setSlot(DATA_SLOT, PrivateValue(dst.inlineDataPointer()));
+
+    return 0;
 }
 
 ArrayBufferViewObject*
 ArrayBufferObject::firstView()
 {
     return getSlot(FIRST_VIEW_SLOT).isObject()
         ? static_cast<ArrayBufferViewObject*>(&getSlot(FIRST_VIEW_SLOT).toObject())
         : nullptr;
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -254,17 +254,17 @@ class ArrayBufferObject : public ArrayBu
     // initialize()d to become a real, content-visible ArrayBufferObject.
     static ArrayBufferObject* createEmpty(JSContext* cx);
 
     static void copyData(Handle<ArrayBufferObject*> toBuffer, uint32_t toIndex,
                          Handle<ArrayBufferObject*> fromBuffer, uint32_t fromIndex,
                          uint32_t count);
 
     static void trace(JSTracer* trc, JSObject* obj);
-    static void objectMoved(JSObject* obj, const JSObject* old);
+    static size_t objectMoved(JSObject* obj, JSObject* old);
 
     static BufferContents externalizeContents(JSContext* cx,
                                               Handle<ArrayBufferObject*> buffer,
                                               bool hasStealableContents);
     static BufferContents stealContents(JSContext* cx,
                                         Handle<ArrayBufferObject*> buffer,
                                         bool hasStealableContents);
 
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -401,30 +401,30 @@ NativeObject::growSlots(JSContext* cx, u
 
     return true;
 }
 
 /* static */ bool
 NativeObject::growSlotsDontReportOOM(JSContext* cx, NativeObject* obj, uint32_t newCount)
 {
     // IC code calls this directly.
-    AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
 
     if (!obj->growSlots(cx, obj->numDynamicSlots(), newCount)) {
         cx->recoverFromOutOfMemory();
         return false;
     }
     return true;
 }
 
 /* static */ bool
 NativeObject::addDenseElementDontReportOOM(JSContext* cx, NativeObject* obj)
 {
     // IC code calls this directly.
-    AutoCheckCannotGC nogc;
+    AutoUnsafeCallWithABI unsafe;
 
     MOZ_ASSERT(obj->getDenseInitializedLength() == obj->getDenseCapacity());
     MOZ_ASSERT(!obj->denseElementsAreCopyOnWrite());
     MOZ_ASSERT(!obj->denseElementsAreFrozen());
     MOZ_ASSERT(!obj->isIndexed());
     MOZ_ASSERT(!obj->is<TypedArrayObject>());
     MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
 
--- a/js/src/vm/Scope.h
+++ b/js/src/vm/Scope.h
@@ -1512,27 +1512,17 @@ class MutableWrappedPtrOperations<ScopeI
 
 namespace JS {
 
 template <>
 struct GCPolicy<js::ScopeKind> : public IgnoreGCPolicy<js::ScopeKind>
 { };
 
 template <typename T>
-struct ScopeDataGCPolicy
-{
-    static T initial() {
-        return nullptr;
-    }
-
-    static void trace(JSTracer* trc, T* vp, const char* name) {
-        if (*vp)
-            (*vp)->trace(trc);
-    }
-};
+struct ScopeDataGCPolicy : public NonGCPointerPolicy<T> {};
 
 #define DEFINE_SCOPE_DATA_GCPOLICY(Data)                        \
     template <>                                                 \
     struct MapTypeToRootKind<Data*> {                           \
         static const RootKind kind = RootKind::Traceable;       \
     };                                                          \
     template <>                                                 \
     struct GCPolicy<Data*> : public ScopeDataGCPolicy<Data*>    \
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -175,44 +175,36 @@ TypedArrayObject::finalize(FreeOp* fop, 
     if (curObj->hasBuffer())
         return;
 
     // Free the data slot pointer if it does not point into the old JSObject.
     if (!curObj->hasInlineElements())
         js_free(curObj->elements());
 }
 
-/* static */ void
-TypedArrayObject::objectMoved(JSObject* obj, const JSObject* old)
+/* static */ size_t
+TypedArrayObject::objectMoved(JSObject* obj, JSObject* old)
 {
     TypedArrayObject* newObj = &obj->as<TypedArrayObject>();
-    const TypedArrayObject* oldObj = &old->as<TypedArrayObject>();
-
-    // Typed arrays with a buffer object do not need an update.
-    if (oldObj->hasBuffer())
-        return;
-
-    // Update the data slot pointer if it points to the old JSObject.
-    if (oldObj->hasInlineElements())
-        newObj->setInlineElements();
-}
-
-/* static */ size_t
-TypedArrayObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* obj, const JSObject* old,
-                                           gc::AllocKind newAllocKind)
-{
-    TypedArrayObject* newObj = &obj->as<TypedArrayObject>();
-    const TypedArrayObject* oldObj = &old->as<TypedArrayObject>();
+    TypedArrayObject* oldObj = &old->as<TypedArrayObject>();
     MOZ_ASSERT(newObj->elementsRaw() == oldObj->elementsRaw());
     MOZ_ASSERT(obj->isTenured());
 
     // Typed arrays with a buffer object do not need an update.
     if (oldObj->hasBuffer())
         return 0;
 
+    if (!IsInsideNursery(old)) {
+        // Update the data slot pointer if it points to the old JSObject.
+        if (oldObj->hasInlineElements())
+            newObj->setInlineElements();
+
+        return 0;
+    }
+
     Nursery& nursery = obj->zone()->group()->nursery();
     void* buf = oldObj->elements();
 
     if (!nursery.isInside(buf)) {
         nursery.removeMallocedBuffer(buf);
         return 0;
     }
 
@@ -229,16 +221,17 @@ JS_FOR_EACH_TYPED_ARRAY(OBJECT_MOVED_TYP
 #undef OBJECT_MOVED_TYPED_ARRAY
       default:
         MOZ_CRASH("Unsupported TypedArray type");
     }
 
     size_t headerSize = dataOffset() + sizeof(HeapSlot);
 
     // See AllocKindForLazyBuffer.
+    AllocKind newAllocKind = obj->asTenured().getAllocKind();
     MOZ_ASSERT_IF(nbytes == 0, headerSize + sizeof(uint8_t) <= GetGCKindBytes(newAllocKind));
 
     if (headerSize + nbytes <= GetGCKindBytes(newAllocKind)) {
         MOZ_ASSERT(oldObj->hasInlineElements());
 #ifdef DEBUG
         if (nbytes == 0) {
             uint8_t* output = newObj->fixedData(TypedArrayObject::FIXED_DATA_START);
             output[0] = ZeroLengthArrayData;
@@ -255,18 +248,18 @@ JS_FOR_EACH_TYPED_ARRAY(OBJECT_MOVED_TYP
         MOZ_ASSERT(!nursery.isInside(data));
         newObj->initPrivate(data);
     }
 
     mozilla::PodCopy(newObj->elements(), oldObj->elements(), nbytes);
 
     // Set a forwarding pointer for the element buffers in case they were
     // preserved on the stack by Ion.
-    nursery.maybeSetForwardingPointer(trc, oldObj->elements(), newObj->elements(),
-                                      /* direct = */nbytes >= sizeof(uintptr_t));
+    nursery.setForwardingPointerWhileTenuring(oldObj->elements(), newObj->elements(),
+                                              /* direct = */nbytes >= sizeof(uintptr_t));
 
     return newObj->hasInlineElements() ? 0 : nbytes;
 }
 
 bool
 TypedArrayObject::hasInlineElements() const
 {
     return elements() == this->fixedData(TypedArrayObject::FIXED_DATA_START) &&
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -268,19 +268,17 @@ class TypedArrayObject : public NativeOb
         // Note, do not check whether shared or not
         // Keep synced with js::Get<Type>ArrayLengthAndData in jsfriendapi.h!
         return static_cast<void*>(getPrivate(DATA_SLOT));
     }
 
   public:
     static void trace(JSTracer* trc, JSObject* obj);
     static void finalize(FreeOp* fop, JSObject* obj);
-    static void objectMoved(JSObject* obj, const JSObject* old);
-    static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* obj, const JSObject* old,
-                                           gc::AllocKind allocKind);
+    static size_t objectMoved(JSObject* obj, JSObject* old);
 
     /* Initialization bits */
 
     template<Value ValueGetter(TypedArrayObject* tarr)>
     static bool
     GetterImpl(JSContext* cx, const CallArgs& args)
     {
         MOZ_ASSERT(is(args.thisv()));
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -1199,71 +1199,67 @@ UnboxedArrayObject::trace(JSTracer* trc,
         break;
 
       default:
         MOZ_CRASH();
     }
 }
 
 /* static */ void
-UnboxedArrayObject::objectMoved(JSObject* obj, const JSObject* old)
-{
-    UnboxedArrayObject& dst = obj->as<UnboxedArrayObject>();
-    const UnboxedArrayObject& src = old->as<UnboxedArrayObject>();
-
-    // Fix up possible inline data pointer.
-    if (src.hasInlineElements())
-        dst.setInlineElements();
-}
-
-/* static */ void
 UnboxedArrayObject::finalize(FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(!IsInsideNursery(obj));
     if (!obj->as<UnboxedArrayObject>().hasInlineElements())
         js_free(obj->as<UnboxedArrayObject>().elements());
 }
 
 /* static */ size_t
-UnboxedArrayObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
-                                             gc::AllocKind allocKind)
+UnboxedArrayObject::objectMoved(JSObject* dst, JSObject* src)
 {
     UnboxedArrayObject* ndst = &dst->as<UnboxedArrayObject>();
     UnboxedArrayObject* nsrc = &src->as<UnboxedArrayObject>();
     MOZ_ASSERT(ndst->elements() == nsrc->elements());
 
+    if (!IsInsideNursery(src)) {
+        // Fix up possible inline data pointer.
+        if (nsrc->hasInlineElements())
+            ndst->setInlineElements();
+
+        return 0;
+    }
+
     Nursery& nursery = dst->zone()->group()->nursery();
-
     if (!nursery.isInside(nsrc->elements())) {
         nursery.removeMallocedBuffer(nsrc->elements());
         return 0;
     }
 
     // Determine if we can use inline data for the target array. If this is
     // possible, the nursery will have picked an allocation size that is large
     // enough.
     size_t nbytes = nsrc->capacity() * nsrc->elementSize();
-    if (offsetOfInlineElements() + nbytes <= GetGCKindBytes(allocKind)) {
+    gc::AllocKind allocKind = dst->asTenured().getAllocKind();
+    if (offsetOfInlineElements() + nbytes <= gc::GetGCKindBytes(allocKind)) {
         ndst->setInlineElements();
     } else {
         MOZ_ASSERT(allocKind == gc::AllocKind::OBJECT0);
 
         AutoEnterOOMUnsafeRegion oomUnsafe;
         uint8_t* data = nsrc->zone()->pod_malloc<uint8_t>(nbytes);
         if (!data)
             oomUnsafe.crash("Failed to allocate unboxed array elements while tenuring.");
         ndst->elements_ = data;
     }
 
     PodCopy(ndst->elements(), nsrc->elements(), nsrc->initializedLength() * nsrc->elementSize());
 
     // Set a forwarding pointer for the element buffers in case they were
     // preserved on the stack by Ion.
     bool direct = nsrc->capacity() * nsrc->elementSize() >= sizeof(uintptr_t);
-    nursery.maybeSetForwardingPointer(trc, nsrc->elements(), ndst->elements(), direct);
+    nursery.setForwardingPointerWhileTenuring(nsrc->elements(), ndst->elements(), direct);
 
     return ndst->hasInlineElements() ? 0 : nbytes;
 }
 
 // Possible capacities for unboxed arrays. Some of these capacities might seem
 // a little weird, but were chosen to allow the inline data of objects of each
 // size to be fully utilized for arrays of the various types on both 32 bit and
 // 64 bit platforms.
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -438,22 +438,19 @@ class UnboxedArrayObject : public Unboxe
     static bool convertToNativeWithGroup(JSContext* cx, JSObject* obj,
                                          ObjectGroup* group, Shape* shape);
     bool convertInt32ToDouble(JSContext* cx, ObjectGroup* group);
 
     void fillAfterConvert(JSContext* cx,
                           Handle<GCVector<Value>> values, size_t* valueCursor);
 
     static void trace(JSTracer* trc, JSObject* object);
-    static void objectMoved(JSObject* obj, const JSObject* old);
+    static size_t objectMoved(JSObject* obj, JSObject* old);
     static void finalize(FreeOp* fop, JSObject* obj);
 
-    static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
-                                           gc::AllocKind allocKind);
-
     uint8_t* elements() {
         return elements_;
     }
 
     bool hasInlineElements() const {
         return elements_ == &inlineElements_[0];
     }
 
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -419,26 +419,28 @@ sandbox_finalize(js::FreeOp* fop, JSObje
         return;
     }
 
     static_cast<SandboxPrivate*>(sop)->ForgetGlobalObject(obj);
     DestroyProtoAndIfaceCache(obj);
     DeferredFinalize(sop);
 }
 
-static void
-sandbox_moved(JSObject* obj, const JSObject* old)
+static size_t
+sandbox_moved(JSObject* obj, JSObject* old)
 {
     // Note that this hook can be called before the private pointer is set. In
     // this case the SandboxPrivate will not exist yet, so there is nothing to
     // do.
     nsIScriptObjectPrincipal* sop =
         static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(obj));
-    if (sop)
-        static_cast<SandboxPrivate*>(sop)->ObjectMoved(obj, old);
+    if (!sop)
+        return 0;
+
+    return static_cast<SandboxPrivate*>(sop)->ObjectMoved(obj, old);
 }
 
 static bool
 writeToProto_setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                          JS::MutableHandleValue vp, JS::ObjectOpResult& result)
 {
     RootedObject proto(cx);
     if (!JS_GetPrototype(cx, obj, &proto))
--- a/js/xpconnect/src/SandboxPrivate.h
+++ b/js/xpconnect/src/SandboxPrivate.h
@@ -48,19 +48,20 @@ public:
         ClearWrapper(obj);
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override
     {
         MOZ_CRASH("SandboxPrivate doesn't use DOM bindings!");
     }
 
-    void ObjectMoved(JSObject* obj, const JSObject* old)
+    size_t ObjectMoved(JSObject* obj, JSObject* old)
     {
         UpdateWrapper(obj, old);
+        return 0;
     }
 
 private:
     virtual ~SandboxPrivate() { }
 
     nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -540,25 +540,26 @@ WrappedNativeFinalize(js::FreeOp* fop, J
         return;
 
     XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
     if (helperType == WN_HELPER)
         wrapper->GetScriptable()->Finalize(wrapper, js::CastToJSFreeOp(fop), obj);
     wrapper->FlatJSObjectFinalized();
 }
 
-static void
-WrappedNativeObjectMoved(JSObject* obj, const JSObject* old)
+static size_t
+WrappedNativeObjectMoved(JSObject* obj, JSObject* old)
 {
     nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
     if (!p)
-        return;
+        return 0;
 
     XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
     wrapper->FlatJSObjectMoved(obj, old);
+    return 0;
 }
 
 void
 XPC_WN_NoHelper_Finalize(js::FreeOp* fop, JSObject* obj)
 {
     WrappedNativeFinalize(fop, obj, WN_NOHELPER);
 }
 
@@ -1004,23 +1005,26 @@ static void
 XPC_WN_Shared_Proto_Finalize(js::FreeOp* fop, JSObject* obj)
 {
     // This can be null if xpc shutdown has already happened
     XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if (p)
         p->JSProtoObjectFinalized(fop, obj);
 }
 
-static void
-XPC_WN_Shared_Proto_ObjectMoved(JSObject* obj, const JSObject* old)
+static size_t
+XPC_WN_Shared_Proto_ObjectMoved(JSObject* obj, JSObject* old)
 {
     // This can be null if xpc shutdown has already happened
     XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
-    if (p)
-        p->JSProtoObjectMoved(obj, old);
+    if (!p)
+        return 0;
+
+    p->JSProtoObjectMoved(obj, old);
+    return 0;
 }
 
 static void
 XPC_WN_Shared_Proto_Trace(JSTracer* trc, JSObject* obj)
 {
     // This can be null if xpc shutdown has already happened
     XPCWrappedNativeProto* p =
         (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
@@ -1205,24 +1209,25 @@ XPC_WN_TearOff_Finalize(js::FreeOp* fop,
 {
     XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)
         xpc_GetJSPrivate(obj);
     if (!p)
         return;
     p->JSObjectFinalized();
 }
 
-static void
-XPC_WN_TearOff_ObjectMoved(JSObject* obj, const JSObject* old)
+static size_t
+XPC_WN_TearOff_ObjectMoved(JSObject* obj, JSObject* old)
 {
     XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)
         xpc_GetJSPrivate(obj);
     if (!p)
-        return;
+        return 0;
     p->JSObjectMoved(obj, old);
+    return 0;
 }
 
 // Make sure XPC_WRAPPER_FLAGS has no reserved slots, so our
 // XPC_WN_TEAROFF_RESERVED_SLOTS value is OK.
 
 static_assert(((XPC_WRAPPER_FLAGS >> JSCLASS_RESERVED_SLOTS_SHIFT) &
                JSCLASS_RESERVED_SLOTS_MASK) == 0,
               "XPC_WRAPPER_FLAGS should not include any reserved slots");
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1400599-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<script>
+onload = function() {
+  document.documentElement.remove();
+}
+</script>
+<body style="overflow: scroll">
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -496,8 +496,9 @@ load 1381323.html
 asserts-if(!stylo,1) load 1388625-1.html # bug 1389286
 load 1390389.html
 load 1395591-1.html
 load 1395715-1.html
 load 1397398-1.html
 load 1397398-2.html
 load 1397398-3.html
 load 1398500.html
+load 1400599-1.html
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -8610,17 +8610,25 @@ nsCSSFrameConstructor::ContentRemoved(ns
 {
   MOZ_ASSERT(aChild);
   AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
   NS_PRECONDITION(mUpdateCount != 0,
                   "Should be in an update while destroying frames");
   nsPresContext* presContext = mPresShell->GetPresContext();
   MOZ_ASSERT(presContext, "Our presShell should have a valid presContext");
 
-  if (aChild == presContext->GetViewportScrollbarStylesOverrideElement()) {
+  // We want to detect when the viewport override element stored in the
+  // prescontext is in the subtree being removed.  Except in fullscreen cases
+  // (which are handled in Element::UnbindFromTree and do not get stored on the
+  // prescontext), the override element is always either the root element or a
+  // <body> child of the root element.  So we can only be removing the stored
+  // override element if the thing being removed is either the override element
+  // itself or the root element (which can be a parent of the override element).
+  if (aChild == presContext->GetViewportScrollbarStylesOverrideElement() ||
+      (!aContainer && aChild->IsElement())) {
     // We might be removing the element that we propagated viewport scrollbar
     // styles from.  Recompute those. (This clause covers two of the three
     // possible scrollbar-propagation sources: the <body> [as aChild or a
     // descendant] and the root node. The other possible scrollbar-propagation
     // source is a fullscreen element, and we have code elsewhere to update
     // scrollbars after fullscreen elements are removed -- specifically, it's
     // part of the fullscreen cleanup code called by Element::UnbindFromTree.
     // We don't handle the fullscreen case here, because it doesn't change the
--- a/layout/base/tests/chrome/chrome.ini
+++ b/layout/base/tests/chrome/chrome.ini
@@ -26,16 +26,17 @@ support-files =
 [test_bug533845.xul]
 skip-if = os == 'linux' && !debug # Bug 1208197
 [test_bug551434.html]
 [test_bug708062.html]
 [test_bug812817.xul]
 [test_bug847890_paintFlashing.html]
 [test_bug1018265.xul]
 [test_bug1041200.xul]
+skip-if = os == 'win' && bits == 64 # Bug 1272321
 support-files =
   bug1041200_frame.html
   bug1041200_window.html
 [test_chrome_content_integration.xul]
 [test_chrome_over_plugin.xul]
 support-files =
   chrome_over_plugin_window.xul
   chrome_over_plugin_window_frame.html
--- a/layout/reftests/abs-pos/reftest.list
+++ b/layout/reftests/abs-pos/reftest.list
@@ -48,15 +48,15 @@ fuzzy-if(/^Windows\x20NT\x206\.1/.test(h
 == table-footer-group-7.html table-internal-7-ref.html
 == continuation-positioned-inline-1.html continuation-positioned-inline-ref.html
 == continuation-positioned-inline-2.html continuation-positioned-inline-ref.html
 == scrollframe-1.html scrollframe-1-ref.html
 fuzzy-if(gtkWidget,1,1) fuzzy-if(Android,9,185) fuzzy-if(asyncPan&&!layersGPUAccelerated,140,144) == scrollframe-2.html scrollframe-2-ref.html #bug 756530
 fuzzy-if(gtkWidget,1,8) == select-1.html select-1-ref.html
 fuzzy-if(gtkWidget,1,8) == select-1-dynamic.html select-1-ref.html
 == select-2.html select-2-ref.html
-fuzzy-if(gtkWidget,1,19) fuzzy-if(Android,17,726) fuzzy-if(asyncPan&&!layersGPUAccelerated,110,114) fuzzy-if(browserIsRemote&&winWidget,110,114) == select-3.html select-3-ref.html
+fuzzy-if(gtkWidget,1,19) fuzzy-if(Android,17,726) fuzzy-if(asyncPan&&!layersGPUAccelerated,110,114) fuzzy-if(browserIsRemote&&winWidget,143,114) == select-3.html select-3-ref.html
 == multi-column-1.html multi-column-1-ref.html
 == button-1.html button-1-ref.html
 == button-2.html button-2-ref.html
 == relative-row-animation-1.html relative-row-animation-1-ref.html
 fuzzy-if(Android,12,50) == fixed-pos-auto-offset-1a.html fixed-pos-auto-offset-1-ref.html
 fuzzy-if(Android,12,50) == fixed-pos-auto-offset-1b.html fixed-pos-auto-offset-1-ref.html
--- a/layout/reftests/columns/reftest.list
+++ b/layout/reftests/columns/reftest.list
@@ -29,13 +29,13 @@ HTTP(..) == columnfill-auto-3.html colum
 == columnfill-overflow.html columnfill-overflow-ref.html
 == margin-collapsing-bug616722-1.html margin-collapsing-bug616722-1-ref.html
 == margin-collapsing-bug616722-2.html margin-collapsing-bug616722-2-ref.html
 == column-balancing-nested-000.html column-balancing-nested-000-ref.html
 == column-balancing-nested-001.html column-balancing-nested-001-ref.html
 == columnrule-overflow.html columnrule-overflow-ref.html
 == columns-table-caption-000.html columns-table-caption-000-ref.html
 == positioning-transforms-bug1112501.html positioning-transforms-bug1112501-ref.html
-fuzzy-if(browserIsRemote&&winWidget,140,276) == fieldset-columns-001.html fieldset-columns-001-ref.html
+fuzzy-if(browserIsRemote&&winWidget,142,276) == fieldset-columns-001.html fieldset-columns-001-ref.html
 == dynamic-change-with-overflow-1.html dynamic-change-with-overflow-1-ref.html
 == dynamic-text-indent-1.html dynamic-text-indent-1-ref.html
 == dynamic-text-indent-2.html dynamic-text-indent-2-ref.html
 == break-avoid-line-position-1.html break-avoid-line-position-1-ref.html
--- a/layout/reftests/forms/fieldset/reftest.list
+++ b/layout/reftests/forms/fieldset/reftest.list
@@ -1,16 +1,16 @@
 fuzzy-if(skiaContent,2,13) == dynamic-legend-scroll-1.html dynamic-legend-scroll-1-ref.html
 == fieldset-hidden-1.html fieldset-hidden-1-ref.html
 == fieldset-intrinsic-width-1.html fieldset-intrinsic-width-1-ref.html
 == fieldset-percentage-padding-1.html fieldset-percentage-padding-1-ref.html
 == fieldset-scroll-1.html fieldset-scroll-1-ref.html
 == fieldset-scrolled-1.html fieldset-scrolled-1-ref.html
 == fieldset-overflow-auto-1.html fieldset-overflow-auto-1-ref.html
-fuzzy-if(winWidget&&!layersGPUAccelerated,140,276) == positioned-container-1.html positioned-container-1-ref.html
+fuzzy-if(winWidget&&!layersGPUAccelerated,142,276) == positioned-container-1.html positioned-container-1-ref.html
 == relpos-legend-1.html relpos-legend-1-ref.html
 == relpos-legend-2.html relpos-legend-2-ref.html
 == relpos-legend-3.html relpos-legend-3-ref.html
 == relpos-legend-4.html relpos-legend-4-ref.html
 == sticky-legend-1.html sticky-legend-1-ref.html
 fuzzy-if(skiaContent,1,40768) == abs-pos-child-sizing.html abs-pos-child-sizing-ref.html
 == overflow-hidden.html overflow-hidden-ref.html
 == legend-rtl.html legend-rtl-ref.html
--- a/layout/reftests/forms/input/text/reftest.list
+++ b/layout/reftests/forms/input/text/reftest.list
@@ -1,10 +1,10 @@
 == bounds-1.html bounds-1-ref.html
-fuzzy-if(asyncPan&&!layersGPUAccelerated,140,111) == size-1.html size-1-ref.html
+fuzzy-if(asyncPan&&!layersGPUAccelerated,151,111) == size-1.html size-1-ref.html
 == size-2.html size-2-ref.html
 HTTP(..) == baseline-1.html baseline-1-ref.html
 HTTP(..) == centering-1.xul centering-1-ref.xul
 == dynamic-height-1.xul dynamic-height-1-ref.xul
 fuzzy-if(skiaContent,1,500) needs-focus == select.html select-ref.html
 == intrinsic-size.html intrinsic-size-ref.html
 == line-height-0.5.html line-height-1.0.html
 != line-height-1.5.html line-height-1.0.html
--- a/layout/reftests/forms/reftest.list
+++ b/layout/reftests/forms/reftest.list
@@ -1,10 +1,10 @@
 fuzzy-if(skiaContent,1,10) HTTP(..) == text-control-baseline-1.html text-control-baseline-1-ref.html
-fuzzy-if(cocoaWidget,16,64) fuzzy-if(Android,52,64) fuzzy-if(winWidget,88,400) == display-block-baselines-1.html display-block-baselines-1-ref.html # anti-aliasing issues
+fuzzy-if(cocoaWidget,16,64) fuzzy-if(Android,52,64) fuzzy-if(winWidget,88,624) == display-block-baselines-1.html display-block-baselines-1-ref.html # anti-aliasing issues
 == display-block-baselines-2.html display-block-baselines-2-ref.html
 == display-block-baselines-3.html display-block-baselines-3-ref.html
 == display-block-baselines-4.html display-block-baselines-4-ref.html
 fuzzy-if(Android,4,8) fuzzy-if(skiaContent,7,2) == display-block-baselines-5.html display-block-baselines-5-ref.html
 
 # button element
 include button/reftest.list
 
--- a/layout/reftests/position-dynamic-changes/relative/reftest.list
+++ b/layout/reftests/position-dynamic-changes/relative/reftest.list
@@ -1,5 +1,5 @@
-fuzzy-if(cocoaWidget,1,2) fuzzy-if(d2d,47,26) fuzzy-if(asyncPan&&!layersGPUAccelerated,140,716) == move-right-bottom.html move-right-bottom-ref.html
-fuzzy-if(cocoaWidget,1,2) fuzzy-if(asyncPan&&!layersGPUAccelerated,140,716) == move-top-left.html move-top-left-ref.html # Bug 688545
-fuzzy-if(cocoaWidget,1,3) fuzzy-if(asyncPan&&!layersGPUAccelerated,140,580) == move-right-bottom-table.html move-right-bottom-table-ref.html
-fuzzy-if(cocoaWidget,1,3) fuzzy-if(asyncPan&&!layersGPUAccelerated,140,580) == move-top-left-table.html move-top-left-table-ref.html # Bug 688545
+fuzzy-if(cocoaWidget,1,2) fuzzy-if(d2d,47,26) fuzzy-if(asyncPan&&!layersGPUAccelerated,149,716) == move-right-bottom.html move-right-bottom-ref.html
+fuzzy-if(cocoaWidget,1,2) fuzzy-if(asyncPan&&!layersGPUAccelerated,149,716) == move-top-left.html move-top-left-ref.html # Bug 688545
+fuzzy-if(cocoaWidget,1,3) fuzzy-if(asyncPan&&!layersGPUAccelerated,144,580) == move-right-bottom-table.html move-right-bottom-table-ref.html
+fuzzy-if(cocoaWidget,1,3) fuzzy-if(asyncPan&&!layersGPUAccelerated,144,580) == move-top-left-table.html move-top-left-table-ref.html # Bug 688545
 == percent.html percent-ref.html
--- a/layout/reftests/scrolling/reftest.list
+++ b/layout/reftests/scrolling/reftest.list
@@ -24,17 +24,17 @@ pref(layout.css.scroll-behavior.enabled,
 HTTP == simple-1.html simple-1.html?ref
 skip-if(styloVsGecko) HTTP == subpixel-1.html#d subpixel-1-ref.html#d # bug 1354406
 fuzzy-if(Android,4,120) HTTP == text-1.html text-1.html?ref
 fuzzy-if(Android,4,120) HTTP == text-2.html?up text-2.html?ref
 fuzzy-if(d2d,1,4) HTTP == transformed-1.html transformed-1.html?ref
 HTTP == transformed-1.html?up transformed-1.html?ref
 fuzzy-if(Android,5,20000) == uncovering-1.html uncovering-1-ref.html
 fuzzy-if(Android,5,20000) == uncovering-2.html uncovering-2-ref.html
-fuzzy-if(asyncPan&&!layersGPUAccelerated,140,4520) == less-than-scrollbar-height.html less-than-scrollbar-height-ref.html
+fuzzy-if(asyncPan&&!layersGPUAccelerated,149,4520) == less-than-scrollbar-height.html less-than-scrollbar-height-ref.html
 == huge-horizontal-overflow.html huge-horizontal-overflow-ref.html
 == huge-vertical-overflow.html huge-vertical-overflow-ref.html
 fuzzy-if(asyncPan&&!layersGPUAccelerated,102,6818) == iframe-scrolling-attr-1.html iframe-scrolling-attr-ref.html
 fuzzy-if(asyncPan&&!layersGPUAccelerated,140,6818) == iframe-scrolling-attr-2.html iframe-scrolling-attr-ref.html
 == frame-scrolling-attr-1.html frame-scrolling-attr-ref.html
 fuzzy-if(asyncPan&&!layersGPUAccelerated,102,2420) == frame-scrolling-attr-2.html frame-scrolling-attr-ref.html
 == move-item.html move-item-ref.html # bug 1125750
 == fractional-scroll-area.html?top=-0.4&outerBottom=100&innerBottom=200 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
--- a/layout/reftests/svg/svg-integration/reftest.list
+++ b/layout/reftests/svg/svg-integration/reftest.list
@@ -43,10 +43,10 @@ fuzzy(1,5000) == mask-clipPath-opacity-0
 fuzzy(1,5000) == mask-clipPath-opacity-01c.xhtml mask-clipPath-opacity-01-ref.xhtml
 fuzzy(1,5000) == mask-clipPath-opacity-01d.xhtml mask-clipPath-opacity-01-ref.xhtml
 fuzzy(1,5000) == mask-clipPath-opacity-01e.xhtml mask-clipPath-opacity-01-ref.xhtml
 
 == transform-outer-svg-01.xhtml transform-outer-svg-01-ref.xhtml
 
 # box-decoration-break tests
 fuzzy-if(Android,4,10) == box-decoration-break-01.xhtml box-decoration-break-01-ref.xhtml
-fuzzy(56,14) == box-decoration-break-02.xhtml box-decoration-break-02-ref.xhtml
+fuzzy(62,14) == box-decoration-break-02.xhtml box-decoration-break-02-ref.xhtml
 fuzzy(67,234) == box-decoration-break-03.xhtml box-decoration-break-01-ref.xhtml
--- a/layout/reftests/text-overflow/reftest.list
+++ b/layout/reftests/text-overflow/reftest.list
@@ -1,15 +1,15 @@
 == ellipsis-font-fallback.html ellipsis-font-fallback-ref.html
 == line-clipping.html line-clipping-ref.html
 fuzzy-if(Android,16,244) HTTP(..) == marker-basic.html marker-basic-ref.html  # Bug 1128229
 HTTP(..) == marker-string.html marker-string-ref.html
 skip-if(Android) HTTP(..) == bidi-simple.html bidi-simple-ref.html # Fails on Android due to anti-aliasing
 skip-if(!gtkWidget) fuzzy-if(gtkWidget,2,289) HTTP(..) == bidi-simple-scrolled.html bidi-simple-scrolled-ref.html # Fails on Windows and OSX due to anti-aliasing
-fuzzy-if(Android,24,4000) fuzzy-if(cocoaWidget,1,40) fuzzy-if(asyncPan&&!layersGPUAccelerated,140,1836) HTTP(..) == scroll-rounding.html scroll-rounding-ref.html # bug 760264
+fuzzy-if(Android,24,4000) fuzzy-if(cocoaWidget,1,40) fuzzy-if(asyncPan&&!layersGPUAccelerated,149,1836) HTTP(..) == scroll-rounding.html scroll-rounding-ref.html # bug 760264
 fuzzy(2,453) fuzzy-if(skiaContent,9,2100) fails-if(gtkWidget) HTTP(..) == anonymous-block.html anonymous-block-ref.html # gtkWidget:bug 1309103
 HTTP(..) == false-marker-overlap.html false-marker-overlap-ref.html
 HTTP(..) == visibility-hidden.html visibility-hidden-ref.html
 fuzzy-if(asyncPan&&!layersGPUAccelerated,102,1724) fuzzy-if(gtkWidget,10,8) HTTP(..) == block-padding.html block-padding-ref.html
 HTTP(..) == quirks-decorations.html quirks-decorations-ref.html
 HTTP(..) == quirks-line-height.html quirks-line-height-ref.html
 HTTP(..) == standards-decorations.html standards-decorations-ref.html
 HTTP(..) == standards-line-height.html standards-line-height-ref.html
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -2642,16 +2642,17 @@ Gecko_RegisterNamespace(nsIAtom* aNamesp
     return -1;
   }
   return id;
 }
 
 bool
 Gecko_ShouldCreateStyleThreadPool()
 {
+  MOZ_ASSERT(NS_IsMainThread());
   return !mozilla::BrowserTabsRemoteAutostart() || XRE_IsContentProcess();
 }
 
 NS_IMPL_FFI_REFCOUNTING(nsCSSFontFaceRule, CSSFontFaceRule);
 
 nsCSSCounterStyleRule*
 Gecko_CSSCounterStyle_Create(nsIAtom* aName)
 {
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -393,16 +393,20 @@ enum nsStyleImageType {
   eStyleImageType_Image,
   eStyleImageType_Gradient,
   eStyleImageType_Element,
   eStyleImageType_URL
 };
 
 struct CachedBorderImageData
 {
+  ~CachedBorderImageData() {
+    PurgeCachedImages();
+  }
+
   // Caller are expected to ensure that the value of aSVGViewportSize is
   // different from the cached one since the method won't do the check.
   void SetCachedSVGViewportSize(const mozilla::Maybe<nsSize>& aSVGViewportSize);
   const mozilla::Maybe<nsSize>& GetCachedSVGViewportSize();
   void PurgeCachedImages();
   void SetSubImage(uint8_t aIndex, imgIContainer* aSubImage);
   imgIContainer* GetSubImage(uint8_t aIndex);
 
--- a/mobile/android/config/mozconfigs/common
+++ b/mobile/android/config/mozconfigs/common
@@ -5,29 +5,37 @@
 # This file is included at the top of all native android mozconfigs
 if [ "x$IS_NIGHTLY" = "xyes" ]; then
   MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1}
 fi
 
 MOZ_AUTOMATION_L10N_CHECK=0
 . "$topsrcdir/build/mozconfig.common"
 
-# In TaskCluster, the Java JRE/JDK are installed from tooltool, but that
-# install doesn't work on the old Buildbot mock builders (CentOS 6.2), so
-# the relevant env vars are not set up in that case, leaving the build to
-# run from the JRE/JDK in /usr/lib/jvm.
-if [ ! -f /etc/redhat-release ] || [ "$(< /etc/redhat-release)" != "CentOS release 6.2 (Final)" ]; then
+# For actual Android builds in TaskCluster, the system image is Debian,
+# and we use the Java JRE/JDK from the system, as well as the system
+# GCC for the host compiler.  l10n builds are still special, however:
+# they are run on older CentOS systems, and l10n builds on release are
+# still run on Buildbot.  So we have to set things up so this mozconfig
+# works in all cases.
+if [ -f /etc/debian_version ]; then
+    # We're on Debian, there's nothing to do.
+    true
+elif [ ! -f /etc/redhat-release ] || [ "$(< /etc/redhat-release)" != "CentOS release 6.2 (Final)" ]; then
     # set JAVA_HOME to find the JRE/JDK from tooltool.  Several scripts in the JDK
     # assume `java` is in PATH, so set that too.  To see how this tarball is built,
-    # see taskcluster/scripts/misc/repackage-jdk.sh
+    # see taskcluster/scripts/builder/build-android-dependencies/repackage-jdk-centos.sh
     export JAVA_HOME="$topsrcdir/java_home"
     export PATH="$PATH:$topsrcdir/java_home/bin"
 
     mk_add_options "export JAVA_HOME=$topsrcdir/java_home"
     mk_add_options "export PATH=$PATH:$topsrcdir/java_home/bin"
+
+    HOST_CC="$topsrcdir/gcc/bin/gcc"
+    HOST_CXX="$topsrcdir/gcc/bin/g++"
 fi
 
 ac_add_options --enable-elf-hack
 
 ANDROID_NDK_VERSION="r10e"
 ANDROID_NDK_VERSION_32BIT="r8c"
 
 # Build Fennec
@@ -93,19 +101,14 @@ else
     ac_add_options --with-pocket-api-keyfile="$topsrcdir/mobile/android/base/pocket-api-sandbox.token"
 fi
 
 export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
-# Use ccache
-
-HOST_CC="$topsrcdir/gcc/bin/gcc"
-HOST_CXX="$topsrcdir/gcc/bin/g++"
-
 . "$topsrcdir/build/unix/mozconfig.stdcxx"
 
 # Use libc++ as our C++ standard library
 ac_add_options --with-android-cxx-stl=libc++
 
 JS_BINARY="$topsrcdir/mobile/android/config/js_wrapper.sh"
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -1334,58 +1334,16 @@ NS_NewLocalFileStream(nsIFileStream **re
         rv = stream->Init(file, ioFlags, perm, behaviorFlags);
         if (NS_SUCCEEDED(rv))
             stream.forget(result);
     }
     return rv;
 }
 
 nsresult
-NS_BackgroundInputStream(nsIInputStream **result,
-                         nsIInputStream  *stream,
-                         uint32_t         segmentSize /* = 0 */,
-                         uint32_t         segmentCount /* = 0 */)
-{
-    nsresult rv;
-    nsCOMPtr<nsIStreamTransportService> sts =
-        do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
-    if (NS_SUCCEEDED(rv)) {
-        nsCOMPtr<nsITransport> inTransport;
-        rv = sts->CreateInputTransport(stream, int64_t(-1), int64_t(-1),
-                                       true, getter_AddRefs(inTransport));
-        if (NS_SUCCEEDED(rv))
-            rv = inTransport->OpenInputStream(nsITransport::OPEN_BLOCKING,
-                                              segmentSize, segmentCount,
-                                              result);
-    }
-    return rv;
-}
-
-nsresult
-NS_BackgroundOutputStream(nsIOutputStream **result,
-                          nsIOutputStream  *stream,
-                          uint32_t          segmentSize  /* = 0 */,
-                          uint32_t          segmentCount /* = 0 */)
-{
-    nsresult rv;
-    nsCOMPtr<nsIStreamTransportService> sts =
-        do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
-    if (NS_SUCCEEDED(rv)) {
-        nsCOMPtr<nsITransport> inTransport;
-        rv = sts->CreateOutputTransport(stream, int64_t(-1), int64_t(-1),
-                                        true, getter_AddRefs(inTransport));
-        if (NS_SUCCEEDED(rv))
-            rv = inTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING,
-                                               segmentSize, segmentCount,
-                                               result);
-    }
-    return rv;
-}
-
-nsresult
 NS_NewBufferedOutputStream(nsIOutputStream **result,
                            nsIOutputStream  *str,
                            uint32_t          bufferSize)
 {
     nsresult rv;
     nsCOMPtr<nsIBufferedOutputStream> out =
         do_CreateInstance(NS_BUFFEREDOUTPUTSTREAM_CONTRACTID, &rv);
     if (NS_SUCCEEDED(rv)) {
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -507,32 +507,16 @@ nsresult NS_NewSafeLocalFileOutputStream
                                          int32_t           behaviorFlags = 0);
 
 nsresult NS_NewLocalFileStream(nsIFileStream **result,
                                nsIFile        *file,
                                int32_t         ioFlags       = -1,
                                int32_t         perm          = -1,
                                int32_t         behaviorFlags = 0);
 
-// returns the input end of a pipe.  the output end of the pipe
-// is attached to the original stream.  data from the original
-// stream is read into the pipe on a background thread.
-nsresult NS_BackgroundInputStream(nsIInputStream **result,
-                                  nsIInputStream  *stream,
-                                  uint32_t         segmentSize  = 0,
-                                  uint32_t         segmentCount = 0);
-
-// returns the output end of a pipe.  the input end of the pipe
-// is attached to the original stream.  data written to the pipe
-// is copied to the original stream on a background thread.
-nsresult NS_BackgroundOutputStream(nsIOutputStream **result,
-                                   nsIOutputStream  *stream,
-                                   uint32_t          segmentSize  = 0,
-                                   uint32_t          segmentCount = 0);
-
 MOZ_MUST_USE nsresult
 NS_NewBufferedInputStream(nsIInputStream **result,
                           nsIInputStream  *str,
                           uint32_t         bufferSize);
 
 // note: the resulting stream can be QI'ed to nsISafeOutputStream iff the
 // provided stream supports it.
 nsresult NS_NewBufferedOutputStream(nsIOutputStream **result,
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -377,17 +377,18 @@ nsHttpTransaction::Init(uint32_t caps,
         if (NS_FAILED(rv)) return rv;
 
         rv = multi->AppendStream(requestBody);
         if (NS_FAILED(rv)) return rv;
 
         // wrap the multiplexed input stream with a buffered input stream, so
         // that we write data in the largest chunks possible.  this is actually
         // necessary to workaround some common server bugs (see bug 137155).
-        rv = NS_NewBufferedInputStream(getter_AddRefs(mRequestStream), multi,
+        nsCOMPtr<nsIInputStream> stream(do_QueryInterface(multi));
+        rv = NS_NewBufferedInputStream(getter_AddRefs(mRequestStream), stream,
                                        nsIOService::gDefaultSegmentSize);
         if (NS_FAILED(rv)) return rv;
     } else {
         mRequestStream = headers;
     }
 
     nsCOMPtr<nsIThrottledInputChannel> throttled = do_QueryInterface(mChannel);
     nsIInputChannelThrottleQueue* queue;
--- a/netwerk/test/browser/browser.ini
+++ b/netwerk/test/browser/browser.ini
@@ -1,12 +1,12 @@
 [DEFAULT]
 support-files =
   dummy.html
 
 [browser_about_cache.js]
 [browser_NetUtil.js]
 [browser_child_resource.js]
-skip-if = !crashreporter || (e10s && debug && os == "linux" && bits == 64)
+skip-if = !crashreporter || (e10s && debug && os == "linux" && bits == 64) || debug # Bug 1370783
 [browser_post_file.js]
 [browser_nsIFormPOSTActionChannel.js]
 skip-if = e10s # protocol handler and channel does not work in content process
 [browser_resource_navigation.js]
--- a/nsprpub/TAG-INFO
+++ b/nsprpub/TAG-INFO
@@ -1,1 +1,1 @@
-NSPR_4_17_BETA2
+NSPR_4_17_RTM
--- a/nsprpub/config/prdepend.h
+++ b/nsprpub/config/prdepend.h
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSPR in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/nsprpub/pr/include/prinit.h
+++ b/nsprpub/pr/include/prinit.h
@@ -26,21 +26,21 @@ PR_BEGIN_EXTERN_C
 /*
 ** NSPR's version is used to determine the likelihood that the version you
 ** used to build your component is anywhere close to being compatible with
 ** what is in the underlying library.
 **
 ** The format of the version string is
 **     "<major version>.<minor version>[.<patch level>] [<Beta>]"
 */
-#define PR_VERSION  "4.17 Beta"
+#define PR_VERSION  "4.17"
 #define PR_VMAJOR   4
 #define PR_VMINOR   17
 #define PR_VPATCH   0
-#define PR_BETA     PR_TRUE
+#define PR_BETA     PR_FALSE
 
 /*
 ** PRVersionCheck
 **
 ** The basic signature of the function that is called to provide version
 ** checking. The result will be a boolean that indicates the likelihood
 ** that the underling library will perform as the caller expects.
 **
--- a/old-configure.in
+++ b/old-configure.in
@@ -43,17 +43,17 @@ dnl ====================================
 _SUBDIR_HOST_LDFLAGS="$HOST_LDFLAGS"
 _SUBDIR_CONFIG_ARGS="$ac_configure_args"
 
 dnl Set the version number of the libs included with mozilla
 dnl ========================================================
 MOZJPEG=62
 MOZPNG=10631
 NSPR_VERSION=4
-NSPR_MINVER=4.16
+NSPR_MINVER=4.17
 NSS_VERSION=3
 
 dnl Set the minimum version of toolkit libs used by mozilla
 dnl ========================================================
 GLIB_VERSION=2.22
 # 2_26 is the earliest version we can set GLIB_VERSION_MIN_REQUIRED.
 # The macro won't be used when compiling with earlier versions anyway.
 GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26
--- a/security/manager/ssl/StaticHPKPins.h
+++ b/security/manager/ssl/StaticHPKPins.h
@@ -1135,9 +1135,9 @@ static const TransportSecurityPreload kP
   { "za.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "zh.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
 };
 
 // Pinning Preload List Length = 479;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1514223123051000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1514309412515000);
--- a/security/manager/ssl/nsSTSPreloadList.errors
+++ b/security/manager/ssl/nsSTSPreloadList.errors
@@ -30,22 +30,22 @@ 123.gg: could not connect to host
 123share.org: could not connect to host
 123test.de: did not receive HSTS header
 123test.es: did not receive HSTS header
 123test.fr: did not receive HSTS header
 126ium.moe: could not connect to host
 127011-networks.ch: could not connect to host
 12vpn.org: could not connect to host
 12vpnchina.com: could not connect to host
+13318522.com: could not connect to host
 1536.cf: could not connect to host
 163pwd.com: could not connect to host
 166166.com: could not connect to host
 16deza.com: did not receive HSTS header
 16packets.com: could not connect to host
-173vpn.cn: could not connect to host
 188betwarriors.co.uk: could not connect to host
 188trafalgar.ca: did not receive HSTS header
 1921958389.rsc.cdn77.org: did not receive HSTS header
 195gm.com: could not connect to host
 1a-jva.de: could not connect to host
 1atic.com: could not connect to host
 1cover.com: could not connect to host
 1k8b.com: could not connect to host
@@ -57,48 +57,44 @@ 1years.cc: could not connect to host
 206rc.net: max-age too low: 2592000
 20hs.cn: did not receive HSTS header
 21lg.co: could not connect to host
 22228522.com: could not connect to host
 247quickbooks.com: did not receive HSTS header
 24hourpaint.com: could not connect to host
 24sihu.com: could not connect to host
 25daysof.io: could not connect to host
-27728522.com: could not connect to host
 2859cc.com: could not connect to host
 28spots.net: could not connect to host
 2acbi-asso.fr: did not receive HSTS header
 2bizi.ru: could not connect to host
 2brokegirls.org: could not connect to host
 2carpros.com: did not receive HSTS header
 2intermediate.co.uk: did not receive HSTS header
 2or3.tk: could not connect to host
 2programmers.net: could not connect to host
-2ss.jp: could not connect to host
+2ss.jp: did not receive HSTS header
 300651.ru: did not receive HSTS header
 300m.com: did not receive HSTS header
 300mbmovies4u.cc: could not connect to host
 301.website: could not connect to host
 302.nyc: could not connect to host
-314chan.org: could not connect to host
 32ph.com: could not connect to host
 33338522.com: could not connect to host
 3338522.com: could not connect to host
 33drugstore.com: did not receive HSTS header
-341.mg: could not connect to host
+341.mg: did not receive HSTS header
 3555aa.com: could not connect to host
 35792.de: could not connect to host
 360gradus.com: did not receive HSTS header
-360live.fr: did not receive HSTS header
 365.or.jp: could not connect to host
 368mibn.com: could not connect to host
 3778xl.com: did not receive HSTS header
 38sihu.com: could not connect to host
 39sihu.com: could not connect to host
-3ags.de: did not receive HSTS header
 3chit.cf: could not connect to host
 3click-loan.com: could not connect to host
 3d-bastler.de: could not connect to host
 3dcart.com: did not receive HSTS header
 3delivered.com: could not connect to host
 3dproteinimaging.com: did not receive HSTS header
 3sreporting.com: did not receive HSTS header
 404.sh: could not connect to host
@@ -115,17 +111,16 @@ 4azino777.ru: could not connect to host
 4cclothing.com: could not connect to host
 4elements.com: did not receive HSTS header
 4eyes.ch: did not receive HSTS header
 4miners.net: could not connect to host
 4ourty2.org: did not receive HSTS header
 4sqsu.eu: could not connect to host
 4w-performers.link: could not connect to host
 50millionablaze.org: could not connect to host
-517vpn.cn: could not connect to host
 540.co: did not receive HSTS header
 54bf.com: could not connect to host
 55558522.com: could not connect to host
 56ct.com: could not connect to host
 5piecesofadvice.com: could not connect to host
 5w5.la: could not connect to host
 60ych.net: did not receive HSTS header
 6120.eu: did not receive HSTS header
@@ -343,24 +338,25 @@ al-shami.net: could not connect to host
 aladdin.ie: did not receive HSTS header
 alanlee.net: could not connect to host
 alanrickmanflipstable.com: could not connect to host
 alariel.de: did not receive HSTS header
 alarme-gps.ch: could not connect to host
 alarmegps.ch: could not connect to host
 alarmsystemreviews.com: did not receive HSTS header
 alaundeil.xyz: could not connect to host
-albanien.guide: did not receive HSTS header
 alberguecimballa.es: could not connect to host
 albertbogdanowicz.pl: did not receive HSTS header
 albertopimienta.com: did not receive HSTS header
 alcazaar.com: could not connect to host
 alecvannoten.be: did not receive HSTS header
+alela.fr: could not connect to host
 alenan.org: could not connect to host
 alessandro.pw: did not receive HSTS header
+alessandroz.pro: could not connect to host
 alethearose.com: did not receive HSTS header
 alexandre.sh: did not receive HSTS header
 alexbaker.org: did not receive HSTS header
 alexberts.ch: did not receive HSTS header
 alexismeza.com: could not connect to host
 alexismeza.com.mx: could not connect to host
 alexismeza.dk: could not connect to host
 alexismeza.es: could not connect to host
@@ -604,21 +600,24 @@ aschaefer.net: could not connect to host
 asdpress.cn: could not connect to host
 ashlane-cottages.com: could not connect to host
 asianodor.com: could not connect to host
 ask.pe: did not receive HSTS header
 askfit.cz: did not receive HSTS header
 asm-x.com: could not connect to host
 asmui.ga: could not connect to host
 asmui.ml: could not connect to host
-asr.cloud: could not connect to host
+asr.li: could not connect to host
+asr.rocks: could not connect to host
+asr.solar: could not connect to host
 ass.org.au: did not receive HSTS header
 assekuranzjobs.de: could not connect to host
 asset-alive.com: did not receive HSTS header
 asset-alive.net: did not receive HSTS header
+assindia.nl: could not connect to host
 astrath.net: could not connect to host
 astrolpost.com: could not connect to host
 astromelody.com: did not receive HSTS header
 asuhe.cc: did not receive HSTS header
 asuhe.win: did not receive HSTS header
 atavio.at: could not connect to host
 atavio.ch: could not connect to host
 atavio.de: did not receive HSTS header
@@ -664,26 +663,24 @@ autodeploy.it: could not connect to host
 autoeet.cz: did not receive HSTS header
 autoepc.ro: did not receive HSTS header
 autojuhos.sk: could not connect to host
 autokovrik-diskont.ru: did not receive HSTS header
 automobiles5.com: could not connect to host
 autotsum.com: could not connect to host
 autumnwindsagility.com: could not connect to host
 auverbox.ovh: could not connect to host
-auvernet.org: could not connect to host
 aux-arts-de-la-table.com: did not receive HSTS header
 auxetek.se: could not connect to host
 auxiliumincrementum.co.uk: could not connect to host
 av.de: did not receive HSTS header
 avantmfg.com: did not receive HSTS header
 avec-ou-sans-ordonnance.fr: could not connect to host
 avepol.cz: did not receive HSTS header
 avepol.eu: did not receive HSTS header
-avi9526.pp.ua: could not connect to host
 aviacao.pt: did not receive HSTS header
 aviodeals.com: could not connect to host
 avonlearningcampus.com: could not connect to host
 avqueen.cn: did not receive HSTS header
 avus-automobile.com: did not receive HSTS header
 awanderlustadventure.com: did not receive HSTS header
 awg-mode.de: did not receive HSTS header
 aww.moe: did not receive HSTS header
@@ -716,31 +713,33 @@ badcronjob.com: could not connect to hos
 badenhard.eu: could not connect to host
 badkamergigant.com: could not connect to host
 badlink.org: could not connect to host
 baff.lu: did not receive HSTS header
 bagiobella.com: max-age too low: 0
 baiduaccount.com: could not connect to host
 bailbondsaffordable.com: did not receive HSTS header
 bair.io: could not connect to host
+baiyangliu.com: could not connect to host
 bakhansen.com: did not receive HSTS header
 bakingstone.com: could not connect to host
 bakkerdesignandbuild.com: did not receive HSTS header
 balcan-underground.net: could not connect to host
 baldur.cc: could not connect to host
 baldwinkoo.com: could not connect to host
 baleares.party: could not connect to host
 balloonphp.com: could not connect to host
 bambooforest.nl: could not connect to host
 bananabandy.com: could not connect to host
 banbanchs.com: could not connect to host
 banchethai.com: did not receive HSTS header
 bandally.net: did not receive HSTS header
 bandb.xyz: could not connect to host
 bandrcrafts.com: did not receive HSTS header
+bangzafran.com: max-age too low: 36000
 banqingdiao.com: could not connect to host
 barely.sexy: did not receive HSTS header
 bariller.fr: could not connect to host
 barrelhead.org: could not connect to host
 barshout.co.uk: could not connect to host
 barss.io: could not connect to host
 barunisystems.com: could not connect to host
 bashc.at: could not connect to host
@@ -797,21 +796,19 @@ beetleroadstories.com: could not connect
 befundup.com: could not connect to host
 behere.be: could not connect to host
 behoerden-online-dienste.de: did not receive HSTS header
 beholdthehurricane.com: could not connect to host
 beichtgenerator.de: did not receive HSTS header
 beier.io: could not connect to host
 beikeil.de: did not receive HSTS header
 belairsewvac.com: could not connect to host
-belegit.org: could not connect to host
 belewpictures.com: did not receive HSTS header
-belgien.guide: did not receive HSTS header
 belize-firmengruendung.com: could not connect to host
-belliash.eu.org: did not receive HSTS header
+belliash.eu.org: could not connect to host
 belltower.io: could not connect to host
 belmontprom.com: could not connect to host
 bemyvictim.com: max-age too low: 2678400
 bendechrai.com: did not receive HSTS header
 beneffy.com: did not receive HSTS header
 benjakesjohnson.com: could not connect to host
 benk.press: could not connect to host
 benny003.de: did not receive HSTS header
@@ -873,16 +870,17 @@ bidon.ca: did not receive HSTS header
 bieberium.de: could not connect to host
 biego.cn: did not receive HSTS header
 bielsa.me: could not connect to host
 bienenblog.cc: could not connect to host
 bierbringer.at: could not connect to host
 big-black.de: did not receive HSTS header
 bigbbqbrush.bid: could not connect to host
 bigbrownpromotions.com.au: did not receive HSTS header
+biglou.com: max-age too low: 3153600
 bigshinylock.minazo.net: could not connect to host
 biguixhe.net: did not receive HSTS header
 bijouxdegriffe.com.br: did not receive HSTS header
 bikermusic.net: could not connect to host
 bildiri.ci: did not receive HSTS header
 bildschirmflackern.de: did not receive HSTS header
 biletua.de: could not connect to host
 billin.net: did not receive HSTS header
@@ -911,17 +909,16 @@ biteoftech.com: did not receive HSTS hea
 bitf.ly: could not connect to host
 bitfactory.ws: could not connect to host
 bitfarm-archiv.com: did not receive HSTS header
 bitfarm-archiv.de: did not receive HSTS header
 bitheus.com: could not connect to host
 bithosting.io: did not receive HSTS header
 bitlish.com: max-age too low: 86400
 bitnet.io: did not receive HSTS header
-bitpod.de: could not connect to host
 bitrage.de: could not connect to host
 bitraum.io: could not connect to host
 bitsafe.systems: could not connect to host
 bitvigor.com: could not connect to host
 bitwrought.net: could not connect to host
 bivsi.com: could not connect to host
 bizcms.com: did not receive HSTS header
 bizon.sk: did not receive HSTS header
@@ -934,18 +931,16 @@ blackburn.link: could not connect to hos
 blacklane.com: did not receive HSTS header
 blackly.uk: max-age too low: 0
 blackpayment.ru: could not connect to host
 blackunicorn.wtf: could not connect to host
 blakerandall.xyz: could not connect to host
 blantik.net: could not connect to host
 blauerhunger.de: could not connect to host
 blauwwit.be: did not receive HSTS header
-blayne.me: could not connect to host
-blayneallan.com: could not connect to host
 blazor.nl: did not receive HSTS header
 blendlecdn.com: could not connect to host
 blenheimchalcot.com: did not receive HSTS header
 blessnet.jp: did not receive HSTS header
 blha303.com.au: could not connect to host
 blindsexdate.nl: could not connect to host
 blitzprog.org: could not connect to host
 blmiller.com: could not connect to host
@@ -1007,16 +1002,17 @@ bootjp.me: did not receive HSTS header
 borchers-media.de: could not connect to host
 borderlinegroup.com: could not connect to host
 boringsecurity.net: could not connect to host
 boris.one: did not receive HSTS header
 borscheid-wenig.com: did not receive HSTS header
 boschee.net: could not connect to host
 botox.bz: did not receive HSTS header
 botserver.de: could not connect to host
+bouncourseplanner.net: could not connect to host
 bouwbedrijfpurmerend.nl: could not connect to host
 bowlroll.net: max-age too low: 0
 boxcryptor.com: did not receive HSTS header
 boxintense.com: did not receive HSTS header
 bp-wahl.at: did not receive HSTS header
 bqtoolbox.com: could not connect to host
 bragasoft.com.br: did not receive HSTS header
 braineet.com: did not receive HSTS header
@@ -1026,30 +1022,30 @@ braintreegateway.com: did not receive HS
 braintreepayments.com: did not receive HSTS header
 brainvation.de: did not receive HSTS header
 bran.cc: could not connect to host
 branchtrack.com: did not receive HSTS header
 brandnewdays.nl: could not connect to host
 brandon.so: could not connect to host
 brandred.net: could not connect to host
 brandspray.com: could not connect to host
-brasilien.guide: did not receive HSTS header
 brasilmorar.com: did not receive HSTS header
 bratteng.xyz: could not connect to host
 brava.bg: did not receive HSTS header
 bravz.de: could not connect to host
 bregnedalsystems.dk: did not receive HSTS header
 bremensaki.com: max-age too low: 2592000
 brettabel.com: could not connect to host
 brfvh24.se: could not connect to host
 brickoo.com: could not connect to host
 brickyardbuffalo.com: did not receive HSTS header
 bridholm.se: could not connect to host
 brightstarkids.com.au: did not receive HSTS header
 brilliantbuilders.co.uk: did not receive HSTS header
+britishsciencefestival.org: could not connect to host
 britzer-toner.de: did not receive HSTS header
 brks.xyz: could not connect to host
 broken-oak.com: could not connect to host
 brookechase.com: did not receive HSTS header
 browserid.org: could not connect to host
 brrd.io: could not connect to host
 brunix.net: could not connect to host
 brunohenc.from.hr: did not receive HSTS header
@@ -1081,36 +1077,36 @@ buildingclouds.at: could not connect to 
 buildingclouds.ch: could not connect to host
 buildingclouds.de: did not receive HSTS header
 buildingclouds.es: could not connect to host
 buildingclouds.eu: could not connect to host
 buildingclouds.fr: could not connect to host
 buildsaver.co.za: did not receive HSTS header
 built.by: did not receive HSTS header
 bukatv.cz: could not connect to host
-bulgarien.guide: did not receive HSTS header
 bulkbuy.tech: could not connect to host
 bulletpoint.cz: could not connect to host
 bullterrier.me: could not connect to host
 bulmafox.com: could not connect to host
+bumarkamoda.com: did not receive HSTS header
 bunaken.asia: did not receive HSTS header
 bunbomenu.de: could not connect to host
 bunsenlabs.org: max-age too low: 2592000
 bupu.ml: could not connect to host
 bureaubolster.nl: did not receive HSTS header
-burgers.io: could not connect to host
 burian-server.cz: could not connect to host
 burningcrash.de: max-age too low: 600000
 burpsuite.site: could not connect to host
 burrow.ovh: could not connect to host
 burrowingsec.com: could not connect to host
 burtrum.me: could not connect to host
 burtrum.top: could not connect to host
 buryat-mongol.cf: could not connect to host
 business.lookout.com: could not connect to host
+businessadviceperth.com.au: did not receive HSTS header
 businesshosting.nl: did not receive HSTS header
 businessloanconnection.org: did not receive HSTS header
 busold.ws: could not connect to host
 bustimes.org: could not connect to host
 butchersworkshop.com: did not receive HSTS header
 butian518.com: did not receive HSTS header
 buttercoin.com: could not connect to host
 butterfieldstraining.com: did not receive HSTS header
@@ -1225,17 +1221,17 @@ carlolly.co.uk: could not connect to hos
 carlosalves.info: could not connect to host
 carpliyz.com: could not connect to host
 carroarmato0.be: did not receive HSTS header
 carsforbackpackers.com: could not connect to host
 carwashvapeur.be: could not connect to host
 casc.cz: did not receive HSTS header
 casedi.org: max-age too low: 0
 casefall.com: could not connect to host
-cash-pos.com: did not receive HSTS header
+cash-pos.com: could not connect to host
 cashlink.io: did not receive HSTS header
 cashmyphone.ch: could not connect to host
 casino-cashflow.ru: did not receive HSTS header
 casinostest.com: did not receive HSTS header
 casioshop.eu: did not receive HSTS header
 casovi.cf: could not connect to host
 castagnonavocats.com: did not receive HSTS header
 cata.ga: could not connect to host
@@ -1297,17 +1293,16 @@ charlipopkids.com.au: could not connect 
 charnleyhouse.co.uk: did not receive HSTS header
 charp.eu: could not connect to host
 chartstoffarm.de: could not connect to host
 chaska.co.za: did not receive HSTS header
 chat-porc.eu: did not receive HSTS header
 chatbot.me: did not receive HSTS header
 chateauconstellation.ch: did not receive HSTS header
 chatup.cf: could not connect to host
-chazay.net: could not connect to host
 chcemvediet.sk: max-age too low: 1555200
 cheapdns.org: could not connect to host
 cheazey.net: did not receive HSTS header
 chebedara.com: could not connect to host
 checkout.google.com: did not receive HSTS header (error ignored - included regardless)
 cheerflow.com: could not connect to host
 cheesetart.my: could not connect to host
 cheetah85.de: could not connect to host
@@ -1323,16 +1318,17 @@ chez-janine.de: could not connect to hos
 chiamata-aiuto.ch: could not connect to host
 chib.chat: could not connect to host
 chihiro.xyz: did not receive HSTS header
 chijiokeindustries.co.uk: could not connect to host
 childcaresolutionscny.org: did not receive HSTS header
 chinacdn.org: could not connect to host
 chinawhale.com: did not receive HSTS header
 chirgui.eu: could not connect to host
+chloe.re: could not connect to host
 chlouis.net: could not connect to host
 chm.vn: did not receive HSTS header
 chodobien.com: could not connect to host
 choiralberta.ca: could not connect to host
 chontalpa.pw: could not connect to host
 chordso.com: could not connect to host
 choruscrowd.com: could not connect to host
 chotu.net: could not connect to host
@@ -1345,17 +1341,17 @@ chriskirchner.de: did not receive HSTS h
 chriskyrouac.com: could not connect to host
 chrisopperwall.com: did not receive HSTS header
 christiaandruif.nl: could not connect to host
 christianbro.gq: could not connect to host
 christophercolumbusfoundation.gov: could not connect to host
 christophersole.com: could not connect to host
 christophheich.me: could not connect to host
 chrisupjohn.com: could not connect to host
-chrisvicmall.com: could not connect to host
+chrisvicmall.com: did not receive HSTS header
 chrome-devtools-frontend.appspot.com: did not receive HSTS header (error ignored - included regardless)
 chrome.google.com: did not receive HSTS header (error ignored - included regardless)
 chrst.ph: could not connect to host
 chua.cf: could not connect to host
 chulado.com: did not receive HSTS header
 churchux.co: did not receive HSTS header
 churrasqueirafacil.com.br: could not connect to host
 cidr.ml: could not connect to host
@@ -1392,23 +1388,23 @@ clearc.tk: could not connect to host
 clearsky.me: did not receive HSTS header
 clerkendweller.uk: did not receive HSTS header
 clickandgo.com: did not receive HSTS header
 clickandshoot.nl: did not receive HSTS header
 clickgram.biz: could not connect to host
 clicn.bio: could not connect to host
 clicnbio.com: did not receive HSTS header
 cliftons.com: did not receive HSTS header
+cliniquepariseau.com: could not connect to host
 clintonbloodworth.com: could not connect to host
 clintonbloodworth.io: could not connect to host
 clintwilson.technology: max-age too low: 2592000
 clip.ovh: could not connect to host
 clipped4u.com: could not connect to host
 cloud-project.com: could not connect to host
-cloud.bugatti: could not connect to host
 cloud.wtf: could not connect to host
 cloudapi.vc: could not connect to host
 cloudbleed.info: could not connect to host
 cloudcert.org: did not receive HSTS header
 cloudcy.net: could not connect to host
 clouddesktop.co.nz: could not connect to host
 cloudey.net: did not receive HSTS header
 cloudflare.com: did not receive HSTS header
@@ -1453,17 +1449,16 @@ codeco.pw: could not connect to host
 codecontrollers.de: could not connect to host
 codeforce.io: could not connect to host
 codeforhakodate.org: did not receive HSTS header
 codelayer.ca: could not connect to host
 codelitmus.com: did not receive HSTS header
 codelove.de: did not receive HSTS header
 codemonkeyrawks.net: could not connect to host
 codepoet.de: could not connect to host
-codepult.com: could not connect to host
 codepx.com: did not receive HSTS header
 codercross.com: could not connect to host
 codes.pk: did not receive HSTS header
 codewiththepros.org: could not connect to host
 codymoniz.com: did not receive HSTS header
 coerthas.com: could not connect to host
 coffeeetc.co.uk: max-age too low: 7776000
 coffeestrategies.com: max-age too low: 5184000
@@ -1503,20 +1498,19 @@ compiledworks.com: could not connect to 
 completionist.audio: could not connect to host
 complymd.com: did not receive HSTS header
 compraneta.com: did not receive HSTS header
 comprehensiveihc.com: could not connect to host
 compucorner.com.mx: could not connect to host
 computeremergency.com.au: did not receive HSTS header
 computersystems.guru: did not receive HSTS header
 computertal.de: could not connect to host
-conaudisa.com: could not connect to host
+conaudisa.com: did not receive HSTS header
 conception.sk: could not connect to host
 concord-group.co.jp: did not receive HSTS header
-confidential.network: did not receive HSTS header
 confirm365.com: could not connect to host
 conformal.com: could not connect to host
 cong5.net: max-age too low: 0
 congz.me: could not connect to host
 conjugacao.com.br: did not receive HSTS header
 connect.ua: could not connect to host
 connected-verhuurservice.nl: did not receive HSTS header
 connectfss.com: could not connect to host
@@ -1536,16 +1530,17 @@ controleer-maar-een-ander.nl: could not 
 convert.zone: did not receive HSTS header
 cooink.net: could not connect to host
 coolaj86.com: did not receive HSTS header
 coolchevy.org.ua: did not receive HSTS header
 coole-meister.de: could not connect to host
 coolrc.me: could not connect to host
 cooxa.com: did not receive HSTS header
 copshop.com.br: did not receive HSTS header
+copytrack.com: could not connect to host
 cor-ser.es: could not connect to host
 coralproject.net: did not receive HSTS header
 coralrosado.com.br: did not receive HSTS header
 corderoscleaning.com: did not receive HSTS header
 cordial-restaurant.com: did not receive HSTS header
 core.mx: could not connect to host
 core4system.de: could not connect to host
 corecodec.com: could not connect to host
@@ -1555,19 +1550,19 @@ corkyoga.site: could not connect to host
 cormactagging.ie: could not connect to host
 cormilu.com.br: did not receive HSTS header
 corozanu.ro: did not receive HSTS header
 corporateencryption.com: could not connect to host
 correctpaardbatterijnietje.nl: did not receive HSTS header
 corruption-mc.net: could not connect to host
 corruption-rsps.net: could not connect to host
 corruption-server.net: could not connect to host
-costa-rica-reisen.de: did not receive HSTS header
 costow.club: could not connect to host
 count.sh: could not connect to host
+countryoutlaws.ca: could not connect to host
 couponcodeq.com: did not receive HSTS header
 couragewhispers.ca: could not connect to host