Merge mozilla-central to autoland. a=merge CLOSED TREE
authorGurzau Raul <rgurzau@mozilla.com>
Thu, 25 Apr 2019 19:30:38 +0300
changeset 471340 f4002576fa1684fb028432840e6b39ae6a94ca5a
parent 471339 6dea6fe4571e9d219b49a5e4cfbfefd87b7299f3 (current diff)
parent 471285 07efc6e32c8729fc9ef50e4e955beca58ff91d48 (diff)
child 471341 a5b40abdd0650e724b1dedd55e231778065af2ac
push id112913
push useropoprus@mozilla.com
push dateThu, 25 Apr 2019 22:21:16 +0000
treeherdermozilla-inbound@5279ac14ae48 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/src/bindings.rs
testing/web-platform/meta/html/browsers/the-window-object/window-open-noreferrer.html.ini
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3308,16 +3308,17 @@ dependencies = [
  "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "nsstring 0.1.0",
+ "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender 0.60.0",
 ]
 
 [[package]]
 name = "webrender_build"
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5744,18 +5744,23 @@ nsBrowserAccess.prototype = {
     if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
       if (isExternal &&
           Services.prefs.prefHasUserValue("browser.link.open_newwindow.override.external"))
         aWhere = Services.prefs.getIntPref("browser.link.open_newwindow.override.external");
       else
         aWhere = Services.prefs.getIntPref("browser.link.open_newwindow");
     }
 
-    let referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true,
-      aOpener ? makeURI(aOpener.location.href) : null);
+    let referrerInfo;
+    if (aFlags & Ci.nsIBrowserDOMWindow.OPEN_NO_REFERRER) {
+      referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, false, null);
+    } else {
+      referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true,
+        aOpener ? makeURI(aOpener.location.href) : null);
+    }
     if (aOpener && aOpener.document) {
       referrerInfo.referrerPolicy = aOpener.document.referrerPolicy;
     }
     let isPrivate = aOpener
                   ? PrivateBrowsingUtils.isContentWindowPrivate(aOpener)
                   : PrivateBrowsingUtils.isWindowPrivate(window);
 
     switch (aWhere) {
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -1361,17 +1361,17 @@ CustomizeMode.prototype = {
     let currentTheme = themes.find(theme => theme.isActive);
 
     // Move the current theme (if any) and the light/dark themes to the start:
     let importantThemes = new Set([DEFAULT_THEME_ID, LIGHT_THEME_ID, DARK_THEME_ID]);
     if (currentTheme) {
       importantThemes.add(currentTheme.id);
     }
 
-    themes.sort((a, b) => importantThemes.has(b) - importantThemes.has(a));
+    themes.sort((a, b) => importantThemes.has(a.id) - importantThemes.has(b.id));
 
     if (themes.length > MAX_THEME_COUNT)
       themes.length = MAX_THEME_COUNT;
 
     let footer = doc.getElementById("customization-lwtheme-menu-footer");
     let panel = footer.parentNode;
     for (let theme of themes) {
       let button = buildToolbarButton(theme);
--- a/devtools/client/responsive.html/browser/tunnel.js
+++ b/devtools/client/responsive.html/browser/tunnel.js
@@ -258,23 +258,26 @@ function tunnelToInnerBrowser(outer, inn
       //   * Specific target names (everything treated as _blank)
       //   * Window features
       //   * window.opener
       // These things are deferred for now, since content which does depend on them seems
       // outside the main focus of RDM.
       const { detail } = event;
       event.preventDefault();
       const uri = Services.io.newURI(detail.url);
+      let flags = Ci.nsIBrowserDOMWindow.OPEN_NEWTAB;
+      if (detail.forceNoReferrer) {
+        flags |= Ci.nsIBrowserDOMWindow.OPEN_NO_REFERRER;
+      }
       // This API is used mainly because it's near the path used for <a target/> with
       // regular browser tabs (which calls `openURIInFrame`).  The more elaborate APIs
       // that support openers, window features, etc. didn't seem callable from JS and / or
       // this event doesn't give enough info to use them.
       browserWindow.browserDOMWindow
-        .openURI(uri, null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
-                 Ci.nsIBrowserDOMWindow.OPEN_NEW,
+        .openURI(uri, null, flags, Ci.nsIBrowserDOMWindow.OPEN_NEW,
                  outer.contentPrincipal);
     },
 
     handleModalPromptEvent({ detail }) {
       // Relay window.alert(), window.prompt() and window.confirm() dialogs through the
       // outer window and make sure the return value is passed back to the inner window.
       // When this event handler is called, the inner iframe is spinning in a nested event
       // loop waiting to be unblocked.
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -7066,26 +7066,34 @@ nsresult nsGlobalWindowOuter::OpenIntern
     // -- see nsIWindowWatcher.idl
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   NS_ASSERTION(mDocShell, "Must have docshell here");
 
   nsAutoCString options;
   bool forceNoOpener = aForceNoOpener;
+  bool forceNoReferrer = false;
   // Unlike other window flags, "noopener" comes from splitting on commas with
   // HTML whitespace trimming...
   nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
       aOptions, ',');
   while (tok.hasMoreTokens()) {
     auto nextTok = tok.nextToken();
     if (nextTok.EqualsLiteral("noopener")) {
       forceNoOpener = true;
       continue;
     }
+    if (StaticPrefs::dom_window_open_noreferrer_enabled() &&
+        nextTok.LowerCaseEqualsLiteral("noreferrer")) {
+      forceNoReferrer = true;
+      // noreferrer implies noopener
+      forceNoOpener = true;
+      continue;
+    }
     // Want to create a copy of the options without 'noopener' because having
     // 'noopener' in the options affects other window features.
     if (!options.IsEmpty()) {
       options.Append(',');
     }
     AppendUTF16toUTF8(nextTok, options);
   }
 
@@ -7181,17 +7189,17 @@ nsresult nsGlobalWindowOuter::OpenIntern
     nsAutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
 
     if (!aCalledNoScript) {
       // We asserted at the top of this function that aNavigate is true for
       // !aCalledNoScript.
       rv = pwwatch->OpenWindow2(
           this, url.IsVoid() ? nullptr : url.get(), name_ptr, options_ptr,
           /* aCalledFromScript = */ true, aDialog, aNavigate, argv,
-          isPopupSpamWindow, forceNoOpener, aLoadState,
+          isPopupSpamWindow, forceNoOpener, forceNoReferrer, aLoadState,
           getter_AddRefs(domReturn));
     } else {
       // Force a system caller here so that the window watcher won't screw us
       // up.  We do NOT want this case looking at the JS context on the stack
       // when searching.  Compare comments on
       // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow.
 
       // Note: Because nsWindowWatcher is so broken, it's actually important
@@ -7201,17 +7209,17 @@ nsresult nsGlobalWindowOuter::OpenIntern
       Maybe<AutoNoJSAPI> nojsapi;
       if (!aContentModal) {
         nojsapi.emplace();
       }
 
       rv = pwwatch->OpenWindow2(
           this, url.IsVoid() ? nullptr : url.get(), name_ptr, options_ptr,
           /* aCalledFromScript = */ false, aDialog, aNavigate, aExtraArgument,
-          isPopupSpamWindow, forceNoOpener, aLoadState,
+          isPopupSpamWindow, forceNoOpener, forceNoReferrer, aLoadState,
           getter_AddRefs(domReturn));
     }
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // success!
 
--- a/dom/browser-element/BrowserElementParent.cpp
+++ b/dom/browser-element/BrowserElementParent.cpp
@@ -109,32 +109,34 @@ namespace mozilla {
  * was accepted by the embedder.
  */
 /*static*/
 BrowserElementParent::OpenWindowResult
 BrowserElementParent::DispatchOpenWindowEvent(Element* aOpenerFrameElement,
                                               Element* aPopupFrameElement,
                                               const nsAString& aURL,
                                               const nsAString& aName,
+                                              bool aForceNoReferrer,
                                               const nsAString& aFeatures) {
   // Dispatch a CustomEvent at aOpenerFrameElement with a detail object
   // (OpenWindowEventDetail) containing aPopupFrameElement, aURL, aName, and
   // aFeatures.
 
   // Create the event's detail object.
   OpenWindowEventDetail detail;
   if (aURL.IsEmpty()) {
     // URL should never be empty. Assign about:blank as default.
     detail.mUrl = NS_LITERAL_STRING("about:blank");
   } else {
     detail.mUrl = aURL;
   }
   detail.mName = aName;
   detail.mFeatures = aFeatures;
   detail.mFrameElement = aPopupFrameElement;
+  detail.mForceNoReferrer = aForceNoReferrer;
 
   nsIGlobalObject* sgo = aPopupFrameElement->OwnerDoc()->GetScopeObject();
   if (!sgo) {
     return BrowserElementParent::OPEN_WINDOW_IGNORED;
   }
 
   AutoJSAPI jsapi;
   if (!jsapi.Init(sgo)) {
@@ -168,17 +170,18 @@ BrowserElementParent::DispatchOpenWindow
   }
 
   return BrowserElementParent::OPEN_WINDOW_IGNORED;
 }
 
 /*static*/
 BrowserElementParent::OpenWindowResult BrowserElementParent::OpenWindowOOP(
     BrowserParent* aOpenerBrowserParent, BrowserParent* aPopupBrowserParent,
-    const nsAString& aURL, const nsAString& aName, const nsAString& aFeatures) {
+    const nsAString& aURL, const nsAString& aName, bool aForceNoReferrer,
+    const nsAString& aFeatures) {
   // Create an iframe owned by the same document which owns openerFrameElement.
   nsCOMPtr<Element> openerFrameElement =
       aOpenerBrowserParent->GetOwnerElement();
   NS_ENSURE_TRUE(openerFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
   RefPtr<HTMLIFrameElement> popupFrameElement =
       CreateIframe(openerFrameElement, aName, /* aRemote = */ true);
 
   // Normally an <iframe> element will try to create a frameLoader when the
@@ -188,18 +191,19 @@ BrowserElementParent::OpenWindowResult B
   // we've verified that the popup has gone through successfully.  If the popup
   // is "blocked" by the embedder, we don't want to load the popup's url.
   //
   // Therefore we call DisallowCreateFrameLoader() on the element and call
   // AllowCreateFrameLoader() only after we've verified that the popup was
   // allowed.
   popupFrameElement->DisallowCreateFrameLoader();
 
-  OpenWindowResult opened = DispatchOpenWindowEvent(
-      openerFrameElement, popupFrameElement, aURL, aName, aFeatures);
+  OpenWindowResult opened =
+      DispatchOpenWindowEvent(openerFrameElement, popupFrameElement, aURL,
+                              aName, aForceNoReferrer, aFeatures);
 
   if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
     return opened;
   }
 
   // The popup was not blocked, so hook up the frame element and the popup tab
   // parent, and return success.
   aPopupBrowserParent->SetOwnerElement(popupFrameElement);
@@ -245,17 +249,17 @@ BrowserElementParent::OpenWindowInProces
     ErrorResult res;
     popupFrameElement->PresetOpenerWindow(WindowProxyHolder(aOpenerWindow),
                                           res);
     MOZ_ASSERT(!res.Failed());
   }
 
   OpenWindowResult opened = DispatchOpenWindowEvent(
       openerFrameElement, popupFrameElement, NS_ConvertUTF8toUTF16(spec), aName,
-      NS_ConvertUTF8toUTF16(aFeatures));
+      false, NS_ConvertUTF8toUTF16(aFeatures));
 
   if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
     return opened;
   }
 
   // Return popupFrameElement's window.
   RefPtr<nsFrameLoader> frameLoader = popupFrameElement->GetFrameLoader();
   NS_ENSURE_TRUE(frameLoader, BrowserElementParent::OPEN_WINDOW_IGNORED);
--- a/dom/browser-element/BrowserElementParent.h
+++ b/dom/browser-element/BrowserElementParent.h
@@ -85,17 +85,18 @@ class BrowserElementParent {
    * will live.
    * @return an OpenWindowresult that describes whether the embedder added the
    *         frame to a document and whether it called preventDefault to prevent
    *         the platform from handling the open request.
    */
   static OpenWindowResult OpenWindowOOP(
       dom::BrowserParent* aOpenerBrowserParent,
       dom::BrowserParent* aPopupBrowserParent, const nsAString& aURL,
-      const nsAString& aName, const nsAString& aFeatures);
+      const nsAString& aName, bool aForceNoReferrer,
+      const nsAString& aFeatures);
 
   /**
    * Handle a window.open call from an in-process <iframe mozbrowser>.
    *
    * (These parameter types are silly, but they match what our caller has in
    * hand.  Feel free to add an override, if they are inconvenient to you.)
    *
    * @param aURI the URI the new window should load.  May be null.
@@ -106,15 +107,15 @@ class BrowserElementParent {
   static OpenWindowResult OpenWindowInProcess(
       mozilla::dom::BrowsingContext* aOpenerWindow, nsIURI* aURI,
       const nsAString& aName, const nsACString& aFeatures, bool aForceNoOpener,
       mozIDOMWindowProxy** aReturnWindow);
 
  private:
   static OpenWindowResult DispatchOpenWindowEvent(
       dom::Element* aOpenerFrameElement, dom::Element* aPopupFrameElement,
-      const nsAString& aURL, const nsAString& aName,
+      const nsAString& aURL, const nsAString& aName, bool aForceNoReferrer,
       const nsAString& aFeatures);
 };
 
 }  // namespace mozilla
 
 #endif
--- a/dom/clients/manager/ClientOpenWindowUtils.cpp
+++ b/dom/clients/manager/ClientOpenWindowUtils.cpp
@@ -236,16 +236,17 @@ nsresult OpenWindow(const ClientOpenWind
     rv = pwwatch->OpenWindow2(
         nullptr, spec.get(), nullptr, nullptr, false, false, true, nullptr,
         // Not a spammy popup; we got permission, we swear!
         /* aIsPopupSpam = */ false,
         // Don't force noopener.  We're not passing in an
         // opener anyway, and we _do_ want the returned
         // window.
         /* aForceNoOpener = */ false,
+        /* aForceNoReferrer = */ false,
         /* aLoadInfp = */ nullptr, getter_AddRefs(newWindow));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     nsCOMPtr<nsPIDOMWindowOuter> pwindow = nsPIDOMWindowOuter::From(newWindow);
     pwindow.forget(aWindow);
     MOZ_DIAGNOSTIC_ASSERT(*aWindow);
     return NS_OK;
--- a/dom/interfaces/base/nsIBrowserDOMWindow.idl
+++ b/dom/interfaces/base/nsIBrowserDOMWindow.idl
@@ -91,16 +91,22 @@ interface nsIBrowserDOMWindow : nsISuppo
   const long OPEN_EXTERNAL      = 0x1;
 
   /**
    * Don't set the window.opener property on the window which is being opened.
    */
   const long OPEN_NO_OPENER     = 0x4;
 
   /**
+   * Don't set the referrer on the navigation inside the window which is
+   * being opened.
+   */
+  const long OPEN_NO_REFERRER   = 0x8;
+
+  /**
    * Create the content window for the given URI.
 
    * @param aURI the URI to be opened in the window (can be null).
    * @param aWhere see possible values described above.
    * @param aOpener window requesting the creation (can be null).
    * @param aFlags flags which control the behavior of the load. The
    *               OPEN_EXTERNAL/OPEN_NEW flag is only used when
    *               aWhere == OPEN_DEFAULTWINDOW.
--- a/dom/ipc/BrowserChild.cpp
+++ b/dom/ipc/BrowserChild.cpp
@@ -903,17 +903,17 @@ BrowserChild::GetInterface(const nsIID& 
   return QueryInterface(aIID, aSink);
 }
 
 NS_IMETHODIMP
 BrowserChild::ProvideWindow(mozIDOMWindowProxy* aParent, uint32_t aChromeFlags,
                             bool aCalledFromJS, bool aPositionSpecified,
                             bool aSizeSpecified, nsIURI* aURI,
                             const nsAString& aName, const nsACString& aFeatures,
-                            bool aForceNoOpener,
+                            bool aForceNoOpener, bool aForceNoReferrer,
                             nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
                             mozIDOMWindowProxy** aReturn) {
   *aReturn = nullptr;
 
   // If aParent is inside an <iframe mozbrowser> and this isn't a request to
   // open a modal-type window, we're going to create a new <iframe mozbrowser>
   // and return its window here.
   nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
@@ -938,18 +938,18 @@ BrowserChild::ProvideWindow(mozIDOMWindo
   }
 
   // Note that ProvideWindowCommon may return NS_ERROR_ABORT if the
   // open window call was canceled.  It's important that we pass this error
   // code back to our caller.
   ContentChild* cc = ContentChild::GetSingleton();
   return cc->ProvideWindowCommon(
       this, aParent, iframeMoz, aChromeFlags, aCalledFromJS, aPositionSpecified,
-      aSizeSpecified, aURI, aName, aFeatures, aForceNoOpener, aLoadState,
-      aWindowIsNew, aReturn);
+      aSizeSpecified, aURI, aName, aFeatures, aForceNoOpener, aForceNoReferrer,
+      aLoadState, aWindowIsNew, aReturn);
 }
 
 void BrowserChild::DestroyWindow() {
   if (mBrowsingContext) {
     mBrowsingContext = nullptr;
   }
 
   if (mStatusFilter) {
--- a/dom/ipc/BrowserParent.cpp
+++ b/dom/ipc/BrowserParent.cpp
@@ -2878,24 +2878,26 @@ void BrowserParent::ApzAwareEventRouting
     if (aOutApzResponse) {
       *aOutApzResponse = nsEventStatus_eIgnore;
     }
   }
 }
 
 mozilla::ipc::IPCResult BrowserParent::RecvBrowserFrameOpenWindow(
     PBrowserParent* aOpener, const nsString& aURL, const nsString& aName,
-    const nsString& aFeatures, BrowserFrameOpenWindowResolver&& aResolve) {
+    bool aForceNoReferrer, const nsString& aFeatures,
+    BrowserFrameOpenWindowResolver&& aResolve) {
   CreatedWindowInfo cwi;
   cwi.rv() = NS_OK;
   cwi.maxTouchPoints() = 0;
 
   BrowserElementParent::OpenWindowResult opened =
       BrowserElementParent::OpenWindowOOP(BrowserParent::GetFrom(aOpener), this,
-                                          aURL, aName, aFeatures);
+                                          aURL, aName, aForceNoReferrer,
+                                          aFeatures);
   cwi.windowOpened() = (opened == BrowserElementParent::OPEN_WINDOW_ADDED);
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
     cwi.maxTouchPoints() = widget->GetMaxTouchPoints();
     cwi.dimensions() = GetDimensionInfo();
   }
 
   // Resolve the request with the information we collected.
--- a/dom/ipc/BrowserParent.h
+++ b/dom/ipc/BrowserParent.h
@@ -188,17 +188,18 @@ class BrowserParent final : public PBrow
   void ReconstructWebProgressAndRequest(
       nsIWebProgress* aManager, const Maybe<WebProgressData>& aWebProgressData,
       const RequestData& aRequestData,
       nsCOMPtr<nsIWebProgress>& aOutWebProgress,
       nsCOMPtr<nsIRequest>& aOutRequest);
 
   mozilla::ipc::IPCResult RecvBrowserFrameOpenWindow(
       PBrowserParent* aOpener, const nsString& aURL, const nsString& aName,
-      const nsString& aFeatures, BrowserFrameOpenWindowResolver&& aResolve);
+      bool aForceNoReferrer, const nsString& aFeatures,
+      BrowserFrameOpenWindowResolver&& aResolve);
 
   mozilla::ipc::IPCResult RecvSyncMessage(
       const nsString& aMessage, const ClonedMessageData& aData,
       InfallibleTArray<CpowEntry>&& aCpows, const IPC::Principal& aPrincipal,
       nsTArray<ipc::StructuredCloneData>* aRetVal);
 
   mozilla::ipc::IPCResult RecvRpcMessage(
       const nsString& aMessage, const ClonedMessageData& aData,
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -749,44 +749,48 @@ void ContentChild::SetProcessName(const 
 #endif
 }
 
 NS_IMETHODIMP
 ContentChild::ProvideWindow(mozIDOMWindowProxy* aParent, uint32_t aChromeFlags,
                             bool aCalledFromJS, bool aPositionSpecified,
                             bool aSizeSpecified, nsIURI* aURI,
                             const nsAString& aName, const nsACString& aFeatures,
-                            bool aForceNoOpener,
+                            bool aForceNoOpener, bool aForceNoReferrer,
                             nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
                             mozIDOMWindowProxy** aReturn) {
-  return ProvideWindowCommon(nullptr, aParent, false, aChromeFlags,
-                             aCalledFromJS, aPositionSpecified, aSizeSpecified,
-                             aURI, aName, aFeatures, aForceNoOpener, aLoadState,
-                             aWindowIsNew, aReturn);
+  return ProvideWindowCommon(
+      nullptr, aParent, false, aChromeFlags, aCalledFromJS, aPositionSpecified,
+      aSizeSpecified, aURI, aName, aFeatures, aForceNoOpener, aForceNoReferrer,
+      aLoadState, aWindowIsNew, aReturn);
 }
 
 static nsresult GetCreateWindowParams(mozIDOMWindowProxy* aParent,
                                       nsDocShellLoadState* aLoadState,
-                                      float* aFullZoom,
+                                      bool aForceNoReferrer, float* aFullZoom,
                                       nsIReferrerInfo** aReferrerInfo,
                                       nsIPrincipal** aTriggeringPrincipal,
                                       nsIContentSecurityPolicy** aCsp) {
   *aFullZoom = 1.0f;
   if (!aTriggeringPrincipal || !aCsp) {
     NS_ERROR("aTriggeringPrincipal || aCsp is null");
     return NS_ERROR_FAILURE;
   }
 
   if (!aReferrerInfo) {
     NS_ERROR("aReferrerInfo is null");
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIReferrerInfo> referrerInfo;
-  if (aLoadState) {
+  if (aForceNoReferrer) {
+    referrerInfo = new ReferrerInfo(
+        nullptr, mozilla::net::ReferrerPolicy::RP_Unset, false);
+  }
+  if (aLoadState && !referrerInfo) {
     referrerInfo = aLoadState->GetReferrerInfo();
   }
 
   auto* opener = nsPIDOMWindowOuter::From(aParent);
   if (!opener) {
     nsCOMPtr<nsIPrincipal> nullPrincipal =
         NullPrincipal::CreateWithoutOriginAttributes();
     if (!referrerInfo) {
@@ -837,17 +841,17 @@ static nsresult GetCreateWindowParams(mo
 
   return NS_OK;
 }
 
 nsresult ContentChild::ProvideWindowCommon(
     BrowserChild* aTabOpener, mozIDOMWindowProxy* aParent, bool aIframeMoz,
     uint32_t aChromeFlags, bool aCalledFromJS, bool aPositionSpecified,
     bool aSizeSpecified, nsIURI* aURI, const nsAString& aName,
-    const nsACString& aFeatures, bool aForceNoOpener,
+    const nsACString& aFeatures, bool aForceNoOpener, bool aForceNoReferrer,
     nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
     mozIDOMWindowProxy** aReturn) {
   *aReturn = nullptr;
 
   nsAutoPtr<IPCTabContext> ipcContext;
   TabId openerTabId = TabId(0);
   nsAutoCString features(aFeatures);
   nsAutoString name(aName);
@@ -883,19 +887,20 @@ nsresult ContentChild::ProvideWindowComm
 
   // If we're in a content process and we have noopener set, there's no reason
   // to load in our process, so let's load it elsewhere!
   if (loadInDifferentProcess) {
     float fullZoom;
     nsCOMPtr<nsIPrincipal> triggeringPrincipal;
     nsCOMPtr<nsIContentSecurityPolicy> csp;
     nsCOMPtr<nsIReferrerInfo> referrerInfo;
-    rv = GetCreateWindowParams(
-        aParent, aLoadState, &fullZoom, getter_AddRefs(referrerInfo),
-        getter_AddRefs(triggeringPrincipal), getter_AddRefs(csp));
+    rv = GetCreateWindowParams(aParent, aLoadState, aForceNoReferrer, &fullZoom,
+                               getter_AddRefs(referrerInfo),
+                               getter_AddRefs(triggeringPrincipal),
+                               getter_AddRefs(csp));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     Maybe<URIParams> uriToLoad;
     SerializeURI(aURI, uriToLoad);
 
     if (name.LowerCaseEqualsLiteral("_blank")) {
@@ -1101,27 +1106,28 @@ nsresult ContentChild::ProvideWindowComm
       // We can't actually send a nullptr up as the URI, since IPDL doesn't let
       // us send nullptr's for primitives. We indicate that the nsString for the
       // URI should be converted to a nullptr by voiding the string.
       url.SetIsVoid(true);
     }
 
     // NOTE: BrowserFrameOpenWindowPromise is the same type as
     // CreateWindowPromise, and this code depends on that fact.
-    newChild->SendBrowserFrameOpenWindow(aTabOpener, NS_ConvertUTF8toUTF16(url),
-                                         name, NS_ConvertUTF8toUTF16(features),
-                                         std::move(resolve), std::move(reject));
+    newChild->SendBrowserFrameOpenWindow(
+        aTabOpener, NS_ConvertUTF8toUTF16(url), name, aForceNoReferrer,
+        NS_ConvertUTF8toUTF16(features), std::move(resolve), std::move(reject));
   } else {
     float fullZoom;
     nsCOMPtr<nsIPrincipal> triggeringPrincipal;
     nsCOMPtr<nsIContentSecurityPolicy> csp;
     nsCOMPtr<nsIReferrerInfo> referrerInfo;
-    rv = GetCreateWindowParams(
-        aParent, aLoadState, &fullZoom, getter_AddRefs(referrerInfo),
-        getter_AddRefs(triggeringPrincipal), getter_AddRefs(csp));
+    rv = GetCreateWindowParams(aParent, aLoadState, aForceNoReferrer, &fullZoom,
+                               getter_AddRefs(referrerInfo),
+                               getter_AddRefs(triggeringPrincipal),
+                               getter_AddRefs(csp));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     Maybe<URIParams> uriToLoad;
     if (aURI) {
       SerializeURI(aURI, uriToLoad);
     }
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -104,25 +104,23 @@ class ContentChild final : public PConte
     nsCString buildID;
     nsCString name;
     nsCString UAName;
     nsCString ID;
     nsCString vendor;
     nsCString sourceURL;
   };
 
-  nsresult ProvideWindowCommon(BrowserChild* aTabOpener,
-                               mozIDOMWindowProxy* aOpener, bool aIframeMoz,
-                               uint32_t aChromeFlags, bool aCalledFromJS,
-                               bool aPositionSpecified, bool aSizeSpecified,
-                               nsIURI* aURI, const nsAString& aName,
-                               const nsACString& aFeatures, bool aForceNoOpener,
-                               nsDocShellLoadState* aLoadState,
-                               bool* aWindowIsNew,
-                               mozIDOMWindowProxy** aReturn);
+  nsresult ProvideWindowCommon(
+      BrowserChild* aTabOpener, mozIDOMWindowProxy* aParent, bool aIframeMoz,
+      uint32_t aChromeFlags, bool aCalledFromJS, bool aPositionSpecified,
+      bool aSizeSpecified, nsIURI* aURI, const nsAString& aName,
+      const nsACString& aFeatures, bool aForceNoOpener, bool aForceNoReferrer,
+      nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
+      mozIDOMWindowProxy** aReturn);
 
   bool Init(MessageLoop* aIOLoop, base::ProcessId aParentPid,
             const char* aParentBuildID, IPC::Channel* aChannel,
             uint64_t aChildID, bool aIsForBrowser);
 
   void InitXPCOM(const XPCOMInitData& aXPCOMInit,
                  const mozilla::dom::ipc::StructuredCloneData& aInitialData);
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -449,17 +449,18 @@ parent:
      *
      * The parent process gets a chance to accept or reject the window.open
      * call, and windowOpened is set to true if we ended up going through with
      * the window.open.
      *
      * @param opener the PBrowser whose content called window.open.
      */
     async BrowserFrameOpenWindow(PBrowser opener,
-                                 nsString aURL, nsString aName, nsString aFeatures)
+                                 nsString aURL, nsString aName,
+                                 bool aForceNoReferrer, nsString aFeatures)
         returns (CreatedWindowInfo window);
 
     /**
      * Tells the containing widget whether the given input block results in a
      * swipe. Should be called in response to a WidgetWheelEvent that has
      * mFlags.mCanTriggerSwipe set on it.
      */
     async RespondStartSwipeEvent(uint64_t aInputBlockId, bool aStartSwipe);
--- a/dom/smil/SMILCompositor.h
+++ b/dom/smil/SMILCompositor.h
@@ -28,17 +28,17 @@ namespace mozilla {
 class SMILCompositor : public PLDHashEntryHdr {
  public:
   typedef SMILTargetIdentifier KeyType;
   typedef const KeyType& KeyTypeRef;
   typedef const KeyType* KeyTypePointer;
 
   explicit SMILCompositor(KeyTypePointer aKey)
       : mKey(*aKey), mForceCompositing(false) {}
-  SMILCompositor(SMILCompositor&& toMove)
+  SMILCompositor(SMILCompositor&& toMove) noexcept
       : PLDHashEntryHdr(std::move(toMove)),
         mKey(std::move(toMove.mKey)),
         mAnimationFunctions(std::move(toMove.mAnimationFunctions)),
         mForceCompositing(false) {}
 
   // PLDHashEntryHdr methods
   KeyTypeRef GetKey() const { return mKey; }
   bool KeyEquals(KeyTypePointer aKey) const;
--- a/dom/smil/SMILValue.cpp
+++ b/dom/smil/SMILValue.cpp
@@ -37,26 +37,26 @@ const SMILValue& SMILValue::operator=(co
   }
 
   mType->Assign(*this, aVal);
 
   return *this;
 }
 
 // Move constructor / reassignment operator:
-SMILValue::SMILValue(SMILValue&& aVal)
+SMILValue::SMILValue(SMILValue&& aVal) noexcept
     : mU(aVal.mU),  // Copying union is only OK because we clear aVal.mType
                     // below.
       mType(aVal.mType) {
   // Leave aVal with a null type, so that it's safely destructible (and won't
   // mess with anything referenced by its union, which we've copied).
   aVal.mType = SMILNullType::Singleton();
 }
 
-SMILValue& SMILValue::operator=(SMILValue&& aVal) {
+SMILValue& SMILValue::operator=(SMILValue&& aVal) noexcept {
   if (!IsNull()) {
     // Clean up any data we're currently tracking.
     DestroyAndCheckPostcondition();
   }
 
   // Copy the union (which could include a pointer to external memory) & mType:
   mU = aVal.mU;
   mType = aVal.mType;
--- a/dom/smil/SMILValue.h
+++ b/dom/smil/SMILValue.h
@@ -27,18 +27,18 @@ class SMILValue {
   explicit SMILValue(const SMILType* aType);
   SMILValue(const SMILValue& aVal);
 
   ~SMILValue() { mType->Destroy(*this); }
 
   const SMILValue& operator=(const SMILValue& aVal);
 
   // Move constructor / reassignment operator:
-  SMILValue(SMILValue&& aVal);
-  SMILValue& operator=(SMILValue&& aVal);
+  SMILValue(SMILValue&& aVal) noexcept;
+  SMILValue& operator=(SMILValue&& aVal) noexcept;
 
   // Equality operators. These are allowed to be conservative (return false
   // more than you'd expect) - see comment above SMILType::IsEqual.
   bool operator==(const SMILValue& aVal) const;
   bool operator!=(const SMILValue& aVal) const { return !(*this == aVal); }
 
   bool IsNull() const { return (mType == SMILNullType::Singleton()); }
 
--- a/dom/svg/SVGViewportElement.h
+++ b/dom/svg/SVGViewportElement.h
@@ -42,17 +42,18 @@ class svgFloatSize {
   float height;
 };
 
 class SVGViewportElement : public SVGGraphicsElement {
   friend class ::nsSVGOuterSVGFrame;
   friend class ::nsSVGViewportFrame;
 
  protected:
-  SVGViewportElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
+  explicit SVGViewportElement(
+      already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
   ~SVGViewportElement() = default;
 
  public:
   // nsIContent interface
   NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
 
   // SVGElement specializations:
   virtual gfxMatrix PrependLocalTransformsTo(
--- a/dom/tests/browser/browser_noopener.js
+++ b/dom/tests/browser/browser_noopener.js
@@ -5,21 +5,21 @@ const TESTS = [
   {id: "#test3", name: "", opener: false, newWindow: false},
 
   {id: "#test4", name: "uniquename1", opener: true, newWindow: false},
   {id: "#test5", name: "uniquename2", opener: false, newWindow: false},
   {id: "#test6", name: "uniquename3", opener: false, newWindow: false},
 
   {id: "#test7", name: "", opener: true, newWindow: false},
   {id: "#test8", name: "", opener: false, newWindow: false},
-  {id: "#test9", name: "", opener: true, newWindow: true},
+  {id: "#test9", name: "", opener: false, newWindow: false},
 
   {id: "#test10", name: "uniquename1", opener: true, newWindow: false},
   {id: "#test11", name: "uniquename2", opener: false, newWindow: false},
-  {id: "#test12", name: "uniquename3", opener: true, newWindow: true},
+  {id: "#test12", name: "uniquename3", opener: false, newWindow: false},
 ];
 
 const TEST_URL = "http://mochi.test:8888/browser/dom/tests/browser/test_noopener_source.html";
 const TARGET_URL = "http://mochi.test:8888/browser/dom/tests/browser/test_noopener_target.html";
 
 const OPEN_NEWWINDOW_PREF = "browser.link.open_newwindow";
 const OPEN_NEWWINDOW = 2;
 const OPEN_NEWTAB = 3;
@@ -94,16 +94,20 @@ async function doAllTests() {
   // Non-private window with container
   await doTests(false, true);
 }
 
 // This test takes a really long time, especially in debug builds, as it is
 // constant starting and stopping processes, and opens a new window ~144 times.
 requestLongerTimeout(25);
 
+add_task(async function prepare() {
+  await SpecialPowers.pushPrefEnv({set: [["dom.window.open.noreferrer.enabled", true]]});
+});
+
 add_task(async function newtab_sameproc() {
   await SpecialPowers.pushPrefEnv({set: [[OPEN_NEWWINDOW_PREF, OPEN_NEWTAB],
                                          [NOOPENER_NEWPROC_PREF, false]]});
   await doAllTests();
 });
 
 add_task(async function newtab_newproc() {
   await SpecialPowers.pushPrefEnv({set: [[OPEN_NEWWINDOW_PREF, OPEN_NEWTAB],
--- a/dom/webidl/BrowserElementDictionaries.webidl
+++ b/dom/webidl/BrowserElementDictionaries.webidl
@@ -7,14 +7,15 @@
  * liability, trademark and document use rules apply.
  */
 
 dictionary OpenWindowEventDetail {
   DOMString url = "";
   DOMString name = "";
   DOMString features = "";
   Node? frameElement = null;
+  boolean forceNoReferrer = false;
 };
 
 dictionary DOMWindowResizeEventDetail {
   long width = 0;
   long height = 0;
 };
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -1,16 +1,17 @@
 [package]
 name = "webrender_bindings"
 version = "0.1.0"
 authors = ["The Mozilla Project Developers"]
 license = "MPL-2.0"
 
 [dependencies]
 rayon = "1"
+num_cpus = "1.7.0"
 thread_profiler = "0.1.1"
 euclid = { version = "0.19.4", features = ["serde"] }
 app_units = "0.7"
 gleam = "0.6.14"
 log = "0.4"
 nsstring = { path = "../../xpcom/rust/nsstring" }
 bincode = "1.0"
 uuid = { version = "0.6", features = ["v4"] }
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/ipc/ByteBuf.h"
 #include "mozilla/webrender/RendererOGL.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/webrender/RenderCompositor.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "mozilla/layers/SynchronousTask.h"
 #include "TextDrawTarget.h"
+#include "malloc_decls.h"
 
 // clang-format off
 #define WRDL_LOG(...)
 //#define WRDL_LOG(...) printf_stderr("WRDL(%p): " __VA_ARGS__)
 //#define WRDL_LOG(...) if (XRE_IsContentProcess()) printf_stderr("WRDL(%p): " __VA_ARGS__)
 // clang-format on
 
 namespace mozilla {
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -26,16 +26,17 @@ use webrender::{
     PipelineInfo, ProfilerHooks, ReadPixelsFormat, Renderer, RendererOptions, RendererStats,
     SceneBuilderHooks, ShaderPrecacheFlags, Shaders, ThreadListener, UploadMethod, VertexUsageHint,
     WrShaders, set_profiler_hooks,
 };
 use thread_profiler::register_thread_with_profiler;
 use moz2d_renderer::Moz2dBlobImageHandler;
 use program_cache::{WrProgramCache, remove_disk_cache};
 use rayon;
+use num_cpus;
 use euclid::SideOffsets2D;
 use nsstring::nsAString;
 
 #[cfg(target_os = "macos")]
 use core_foundation::string::CFString;
 #[cfg(target_os = "macos")]
 use core_graphics::font::CGFont;
 
@@ -1011,18 +1012,24 @@ impl ThreadListener for GeckoProfilerThr
         }
     }
 }
 
 pub struct WrThreadPool(Arc<rayon::ThreadPool>);
 
 #[no_mangle]
 pub unsafe extern "C" fn wr_thread_pool_new() -> *mut WrThreadPool {
+    // Clamp the number of workers between 1 and 8. We get diminishing returns
+    // with high worker counts and extra overhead because of rayon and font
+    // management.
+    let num_threads = num_cpus::get().max(2).min(8);
+
     let worker = rayon::ThreadPoolBuilder::new()
         .thread_name(|idx|{ format!("WRWorker#{}", idx) })
+        .num_threads(num_threads)
         .start_handler(|idx| {
             let name = format!("WRWorker#{}", idx);
             register_thread_with_profiler(name.clone());
             gecko_profiler_register_thread(CString::new(name).unwrap().as_ptr());
         })
         .exit_handler(|_idx| {
             gecko_profiler_unregister_thread();
         })
--- a/gfx/webrender_bindings/src/lib.rs
+++ b/gfx/webrender_bindings/src/lib.rs
@@ -5,16 +5,17 @@
 #![deny(warnings)]
 
 extern crate webrender;
 extern crate euclid;
 extern crate app_units;
 extern crate gleam;
 extern crate nsstring;
 extern crate rayon;
+extern crate num_cpus;
 extern crate thread_profiler;
 extern crate bincode;
 extern crate uuid;
 extern crate fxhash;
 
 #[macro_use]
 extern crate log;
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/gc/stackmaps4-params-n-locals.js
@@ -0,0 +1,145 @@
+// |jit-test| skip-if: !wasmReftypesEnabled()
+
+// A stress test for stackmap creation as it relates to params and locals.
+
+function Stuff(n) {
+    this.n = n;
+}
+function allocate(n) {
+    const res = new Stuff(n);
+    // The webassembly loop below will provide us with 254 as an arg after not
+    // very long.
+    if (n == 254) {
+        gc();
+    }
+    return res;
+}
+
+function visit(aStuff) {
+    return aStuff.n;
+}
+
+let bin = wasmTextToBinary(
+`
+(module
+  (import $allocate "" "allocate" (func (result anyref) (param i32)))
+  (import $visit "" "visit" (func (result i32) (param anyref)))
+
+  ;; A function with many params and locals, most of which are ref-typed.
+  ;; The purpose of having so many is to defeat any reasonable attempt at
+  ;; allocating them all in registers.  The asymmetrically-placed i32s are
+  ;; an attempt to expose any misalignment or inversion of the stack layout
+  ;; vs what the stackmap claims the layout to be.
+
+  (func $manyParamsAndLocals (export "manyParamsAndLocals")
+    (result i32)
+    (param $p1 anyref) (param $p2 i32)    (param $p3 anyref)
+    (param $p4 anyref) (param $p5 anyref) (param $p6 anyref)
+    (param $p7 anyref) (param $p8 anyref) (param $p9 i32)
+    (local $l1 anyref) (local $l2 anyref) (local $l3 anyref)
+    (local $l4 i32)    (local $l5 anyref) (local $l6 i32)
+    (local $l7 anyref) (local $l8 anyref) (local $l9 anyref)
+
+    (local $i i32)
+    (local $runningTotal i32)
+
+    ;; Bind some objects to l1 .. l9.  The JS harness will already
+    ;; have done the same for p1 .. p9.
+    (local.set $l1 (call $allocate (i32.const 1)))
+    (local.set $l2 (call $allocate (i32.const 3)))
+    (local.set $l3 (call $allocate (i32.const 5)))
+    (local.set $l4 (i32.const 7))
+    (local.set $l5 (call $allocate (i32.const 9)))
+    (local.set $l6 (i32.const 11))
+    (local.set $l7 (call $allocate (i32.const 13)))
+    (local.set $l8 (call $allocate (i32.const 15)))
+    (local.set $l9 (call $allocate (i32.const 17)))
+
+    ;; Now loop, allocating as we go, and forcing GC every 256 iterations.
+    ;; Also in each iteration, visit all the locals and params, in the hope
+    ;; of exposing any cases where they are not held live across GC.
+    (loop $CONT
+      ;; Allocate, and hold on to the resulting value, so that Ion can't
+      ;; delete the allocation.
+      (local.set $l9 (call $allocate (i32.and (local.get $i) (i32.const 255))))
+
+      ;; Visit all params and locals
+
+      local.get $runningTotal
+
+      (call $visit (local.get $p1))
+      i32.add
+      local.get $p2
+      i32.add
+      (call $visit (local.get $p3))
+      i32.add
+      (call $visit (local.get $p4))
+      i32.add
+      (call $visit (local.get $p5))
+      i32.add
+      (call $visit (local.get $p6))
+      i32.add
+      (call $visit (local.get $p7))
+      i32.add
+      (call $visit (local.get $p8))
+      i32.add
+      local.get $p9
+      i32.add
+
+      (call $visit (local.get $l1))
+      i32.add
+      (call $visit (local.get $l2))
+      i32.add
+      (call $visit (local.get $l3))
+      i32.add
+      local.get $l4
+      i32.add
+      (call $visit (local.get $l5))
+      i32.add
+      local.get $l6
+      i32.add
+      (call $visit (local.get $l7))
+      i32.add
+      (call $visit (local.get $l8))
+      i32.add
+      (call $visit (local.get $l9))
+      i32.add
+
+      local.set $runningTotal
+
+      (local.set $i (i32.add (local.get $i) (i32.const 1)))
+      (br_if $CONT (i32.lt_s (local.get $i) (i32.const 10000)))
+    ) ;; loop
+
+    local.get $runningTotal
+  ) ;; func
+)
+`);
+
+let mod = new WebAssembly.Module(bin);
+let ins = new WebAssembly.Instance(mod, {"":{allocate, visit}});
+
+let p1 = allocate(97);
+let p2 = 93;
+let p3 = allocate(91);
+let p4 = allocate(83);
+let p5 = allocate(79);
+let p6 = allocate(73);
+let p7 = allocate(71);
+let p8 = allocate(67);
+let p9 = 61;
+
+let res = ins.exports.manyParamsAndLocals(p1, p2, p3, p4, p5, p6, p7, p8, p9);
+
+// Check that GC didn't mess up p1 .. p9
+res += visit(p1);
+res += p2;
+res += visit(p3);
+res += visit(p4);
+res += visit(p5);
+res += visit(p6);
+res += visit(p7);
+res += visit(p8);
+res += p9;
+
+assertEq(res, 9063795);
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -4151,35 +4151,47 @@ class BaseCompiler final : public BaseCo
 
     masm.reserveStack(reservedBytes);
     fr.onFixedStackAllocated();
     if (!stackMapGenerator_.machineStackTracker.pushNonGCPointers(
             reservedBytes / sizeof(void*))) {
       return false;
     }
 
+    // Locals are stack allocated.  Mark ref-typed ones in the stackmap
+    // accordingly.
+    for (const Local& l : localInfo_) {
+      if (l.type == MIRType::RefOrNull) {
+        uint32_t offs = fr.localOffset(l);
+        MOZ_ASSERT(0 == (offs % sizeof(void*)));
+        stackMapGenerator_.machineStackTracker
+                          .setGCPointer(offs / sizeof(void*));
+      }
+    }
+
     // Copy arguments from registers to stack.
     for (ABIArgIter<const ValTypeVector> i(args); !i.done(); i++) {
       if (!i->argInRegister()) {
         continue;
       }
       Local& l = localInfo_[i.index()];
       switch (i.mirType()) {
         case MIRType::Int32:
           fr.storeLocalI32(RegI32(i->gpr()), l);
           break;
         case MIRType::Int64:
           fr.storeLocalI64(RegI64(i->gpr64()), l);
           break;
         case MIRType::RefOrNull: {
-          uint32_t offs = fr.localOffset(l);
+          DebugOnly<uint32_t> offs = fr.localOffset(l);
           MOZ_ASSERT(0 == (offs % sizeof(void*)));
           fr.storeLocalPtr(RegPtr(i->gpr()), l);
-          stackMapGenerator_.machineStackTracker.setGCPointer(offs /
-                                                              sizeof(void*));
+          // We should have just visited this local in the preceding loop.
+          MOZ_ASSERT(stackMapGenerator_.machineStackTracker
+                                       .isGCPointer(offs / sizeof(void*)));
           break;
         }
         case MIRType::Double:
           fr.storeLocalF64(RegF64(i->fpu()), l);
           break;
         case MIRType::Float32:
           fr.storeLocalF32(RegF32(i->fpu()), l);
           break;
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3407,20 +3407,21 @@ nsBrowserAccess.prototype = {
       } else {
         aWhere = Services.prefs.getIntPref("browser.link.open_newwindow");
       }
     }
 
     Services.io.offline = false;
 
     let referrer;
+    let forceNoReferrer = !!(aFlags & Ci.nsIBrowserDOMWindow.OPEN_NO_REFERRER);
     if (aOpener) {
       try {
         let location = aOpener.location;
-        referrer = Services.io.newURI(location);
+        referrer = forceNoReferrer ? null : Services.io.newURI(location);
       } catch(e) { }
     }
 
     let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
     let pinned = false;
 
     if (aURI && aWhere == Ci.nsIBrowserDOMWindow.OPEN_SWITCHTAB) {
       pinned = true;
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -585,16 +585,24 @@ VARCACHE_PREF(
 )
 
 VARCACHE_PREF(
   "dom.storage_access.auto_grants.delayed",
    dom_storage_access_auto_grants_delayed,
   bool, true
 )
 
+// Enable the "noreferrer" feature argument for window.open()
+VARCACHE_PREF(
+  "dom.window.open.noreferrer.enabled",
+   dom_window_open_noreferrer_enabled,
+  bool, true
+)
+
+
 //---------------------------------------------------------------------------
 // Extension prefs
 //---------------------------------------------------------------------------
 
 #ifdef ANDROID
 // Private browsing opt-in is only supported on Firefox desktop.
 # define PREF_VALUE true
 #else
--- a/remote/doc/Testing.md
+++ b/remote/doc/Testing.md
@@ -1,35 +1,37 @@
 Testing
 =======
 
 The remote agent has unit- and functional tests located under
 `remote/test/{unit,browser}`.
 
-You may run all the tests locally using `mach test` like this:
+You may run all the tests under a particular subfolder like this:
 
-	% ./mach test --setpref "remote.enabled=true" remote/test
-
-The tests are currently not run on try.
+	% ./mach test remote
 
 
 Unit tests
 ----------
 
 Because tests are run in parallel and [xpcshell] itself is quite
 chatty, it can sometimes be useful to run the tests in sequence:
 
-	% ./mach xcpshell-test --setpref "remote.enabled=true" --sequential remote/test/unit/test_Assert.js
+	% ./mach xcpshell-test --sequential remote/test/unit/test_Assert.js
 
-The unit tests will appear as part of the `X` jobs on Treeherder.
+The unit tests will appear as part of the `X` (for _xpcshell_) jobs
+on Treeherder.
 
 [xpcshell]: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Writing_xpcshell-based_unit_tests
 
 
 Functional tests
 ----------------
 
 We also have a set of functional [browser chrome] tests located
 under _remote/test/browser_:
 
-	% ./mach mochitest --setpref "remote.enabled=true" -f browser remote/test/browser/browser_cdp.js
+	% ./mach mochitest -f browser remote/test/browser/browser_cdp.js
+
+The functional tests will appear under the `M` (for _mochitest_)
+category in the `bc` (_browser-chrome_) jobs on Treeherder.
 
 [browser chrome]: https://developer.mozilla.org/en-US/docs/Mozilla/Browser_chrome_tests
--- a/remote/doc/Usage.md
+++ b/remote/doc/Usage.md
@@ -9,23 +9,22 @@ three different programs/components runn
     and retrieve information out of Firefox;
 
   * the __agent__ that the client connects to which is an HTTPD living
     inside Firefox, facilitating communication between clients
     and targets;
 
   * and the __target__, which is the web document being debugging.
 
-The remote agent is not currently part of the default Firefox build.
-To self-service a build with it built in, you should follow the
-[_Building_] steps in the developer documentation.
+The remote agent currently only ships with builds of [Firefox
+Nightly] and is __not enabled by default__.  To enable it, you must
+flip the [`remote.enabled` preference] to true.
 
-To check if your Firefox binary comes with the remote agent built
-in, you can look in its help message for this:
-
+To check if your Firefox binary has the remote agent enabled, you
+can look in its help message for this:
 
 	% ./firefox -h

 	  --remote-debugging-port <port>
 	  --remote-debugger [<host>][:<port>] Start the Firefox remote agent, which is
 	                     a low-level debugging interface based on the CDP protocol.
 	                     Defaults to listen on localhost:9222.

@@ -60,9 +59,10 @@ listen on port 0, the system will atomic
 free port.
 
 Allocating an atomic port can be useful if you want to avoid race
 conditions.  The atomically allocated port will be somewhere in the
 ephemeral port range, which varies depending on your system and
 system configuration, but is always guaranteed to be free thus
 eliminating the risk of binding to a port that is already in use.
 
-[_Building_]: ./Building.html
+[Firefox Nightly]: https://www.mozilla.org/en-GB/firefox/channel/desktop/#nightly
+[`remote.enabled` preference]: ./Prefs.html
--- a/taskcluster/ci/source-test/wpt-manifest.yml
+++ b/taskcluster/ci/source-test/wpt-manifest.yml
@@ -15,17 +15,17 @@ upload:
     index:
         product: source
         job-name: manifest-upload
         rank: build_date
     run:
         using: run-task
         command: >
             cd /builds/worker/checkouts/gecko
-            && ./mach wpt-manifest-update --config testing/web-platform/wptrunner.ini
+            && ./mach wpt-manifest-update --config testing/web-platform/wptrunner.ini --no-download
             && tar -cvzf manifests.tar.gz -C testing/web-platform/ meta/MANIFEST.json mozilla/meta/MANIFEST.json
     worker:
         artifacts:
             - type: file
               path: /builds/worker/checkouts/gecko/manifests.tar.gz
               name: public/manifests.tar.gz
 
         max-run-time: 3600
deleted file mode 100644
--- a/testing/web-platform/meta/html/browsers/the-window-object/window-open-noreferrer.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[window-open-noreferrer.html]
-  [window.open() with "noreferrer" tests]
-    expected: FAIL
-
--- a/toolkit/components/windowcreator/nsIWindowProvider.idl
+++ b/toolkit/components/windowcreator/nsIWindowProvider.idl
@@ -101,11 +101,12 @@ interface nsIWindowProvider : nsISupport
                                    in unsigned long aChromeFlags,
                                    in boolean aCalledFromJS,
                                    in boolean aPositionSpecified,
                                    in boolean aSizeSpecified,
                                    in nsIURI aURI,
                                    in AString aName,
                                    in AUTF8String aFeatures,
                                    in boolean aForceNoOpener,
+                                   in boolean aForceNoReferrer,
                                    in nsDocShellLoadStatePtr aLoadState,
                                    out boolean aWindowIsNew);
 };
--- a/toolkit/components/windowwatcher/nsPIWindowWatcher.idl
+++ b/toolkit/components/windowwatcher/nsPIWindowWatcher.idl
@@ -84,16 +84,17 @@ interface nsPIWindowWatcher : nsISupport
   mozIDOMWindowProxy openWindow2(in mozIDOMWindowProxy aParent, in string aUrl,
                                  in string aName, in string aFeatures,
                                  in boolean aCalledFromScript,
                                  in boolean aDialog,
                                  in boolean aNavigate,
                                  in nsISupports aArgs,
                                  in boolean aIsPopupSpam,
                                  in boolean aForceNoOpener,
+                                 in boolean aForceNoReferrer,
                                  in nsDocShellLoadStatePtr aLoadState);
 
   /**
    * Opens a new window so that the window that aOpeningTab belongs to
    * is set as the parent window. The newly opened window will also
    * inherit load context information from aOpeningTab.
    *
    * @param aOpeningTab
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
@@ -287,16 +287,17 @@ nsWindowWatcher::OpenWindow(mozIDOMWindo
   }
   bool dialog = (argc != 0);
 
   return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
                             /* calledFromJS = */ false, dialog,
                             /* navigate = */ true, argv,
                             /* aIsPopupSpam = */ false,
                             /* aForceNoOpener = */ false,
+                            /* aForceNoReferrer = */ false,
                             /* aLoadState = */ nullptr, aResult);
 }
 
 struct SizeSpec {
   SizeSpec()
       : mLeft(0),
         mTop(0),
         mOuterWidth(0),
@@ -342,16 +343,17 @@ struct SizeSpec {
 };
 
 NS_IMETHODIMP
 nsWindowWatcher::OpenWindow2(mozIDOMWindowProxy* aParent, const char* aUrl,
                              const char* aName, const char* aFeatures,
                              bool aCalledFromScript, bool aDialog,
                              bool aNavigate, nsISupports* aArguments,
                              bool aIsPopupSpam, bool aForceNoOpener,
+                             bool aForceNoReferrer,
                              nsDocShellLoadState* aLoadState,
                              mozIDOMWindowProxy** aResult) {
   nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
 
   uint32_t argc = 0;
   if (argv) {
     argv->GetLength(&argc);
   }
@@ -361,17 +363,18 @@ nsWindowWatcher::OpenWindow2(mozIDOMWind
   // called from script.  Fixing this is bug 779939.
   bool dialog = aDialog;
   if (!aCalledFromScript) {
     dialog = argc > 0;
   }
 
   return OpenWindowInternal(aParent, aUrl, aName, aFeatures, aCalledFromScript,
                             dialog, aNavigate, argv, aIsPopupSpam,
-                            aForceNoOpener, aLoadState, aResult);
+                            aForceNoOpener, aForceNoReferrer, aLoadState,
+                            aResult);
 }
 
 // This static function checks if the aDocShell uses an UserContextId equal to
 // the userContextId of subjectPrincipal, if not null.
 static bool CheckUserContextCompatibility(nsIDocShell* aDocShell) {
   MOZ_ASSERT(aDocShell);
 
   uint32_t userContextId =
@@ -570,17 +573,20 @@ nsWindowWatcher::OpenWindowWithRemoteTab
   newBrowserParent.forget(aResult);
   return NS_OK;
 }
 
 nsresult nsWindowWatcher::OpenWindowInternal(
     mozIDOMWindowProxy* aParent, const char* aUrl, const char* aName,
     const char* aFeatures, bool aCalledFromJS, bool aDialog, bool aNavigate,
     nsIArray* aArgv, bool aIsPopupSpam, bool aForceNoOpener,
-    nsDocShellLoadState* aLoadState, mozIDOMWindowProxy** aResult) {
+    bool aForceNoReferrer, nsDocShellLoadState* aLoadState,
+    mozIDOMWindowProxy** aResult) {
+  MOZ_ASSERT_IF(aForceNoReferrer, aForceNoOpener);
+
   nsresult rv = NS_OK;
   bool isNewToplevelWindow = false;
   bool windowIsNew = false;
   bool windowNeedsName = false;
   bool windowIsModal = false;
   bool uriToLoadIsChrome = false;
   bool windowIsModalContentDialog = false;
 
@@ -753,17 +759,18 @@ nsresult nsWindowWatcher::OpenWindowInte
         provider = nsContentUtils::GetWindowProviderForContentProcess();
       }
 
       if (provider) {
         nsCOMPtr<mozIDOMWindowProxy> newWindow;
         rv = provider->ProvideWindow(
             aParent, chromeFlags, aCalledFromJS, sizeSpec.PositionSpecified(),
             sizeSpec.SizeSpecified(), uriToLoad, name, features, aForceNoOpener,
-            aLoadState, &windowIsNew, getter_AddRefs(newWindow));
+            aForceNoReferrer, aLoadState, &windowIsNew,
+            getter_AddRefs(newWindow));
 
         if (NS_SUCCEEDED(rv)) {
           GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
           if (windowIsNew && newDocShellItem) {
             // Make sure to stop any loads happening in this window that the
             // window provider might have started.  Otherwise if our caller
             // manipulates the window it just opened and then the load
             // completes their stuff will get blown away.
@@ -1071,30 +1078,32 @@ nsresult nsWindowWatcher::OpenWindowInte
     if (subjectPrincipal) {
       loadState->SetTriggeringPrincipal(subjectPrincipal);
     }
 #ifndef ANDROID
     MOZ_ASSERT(subjectPrincipal,
                "nsWindowWatcher: triggeringPrincipal required");
 #endif
 
-    /* use the URL from the *extant* document, if any. The usual accessor
-       GetDocument will synchronously create an about:blank document if
-       it has no better answer, and we only care about a real document.
-       Also using GetDocument to force document creation seems to
-       screw up focus in the hidden window; see bug 36016.
-    */
-    RefPtr<Document> doc = GetEntryDocument();
-    if (!doc && parentWindow) {
-      doc = parentWindow->GetExtantDoc();
-    }
-    if (doc) {
-      nsCOMPtr<nsIReferrerInfo> referrerInfo =
-          new ReferrerInfo(doc->GetDocumentURI(), doc->GetReferrerPolicy());
-      loadState->SetReferrerInfo(referrerInfo);
+    if (!aForceNoReferrer) {
+      /* use the URL from the *extant* document, if any. The usual accessor
+         GetDocument will synchronously create an about:blank document if
+         it has no better answer, and we only care about a real document.
+         Also using GetDocument to force document creation seems to
+         screw up focus in the hidden window; see bug 36016.
+      */
+      RefPtr<Document> doc = GetEntryDocument();
+      if (!doc && parentWindow) {
+        doc = parentWindow->GetExtantDoc();
+      }
+      if (doc) {
+        nsCOMPtr<nsIReferrerInfo> referrerInfo =
+            new ReferrerInfo(doc->GetDocumentURI(), doc->GetReferrerPolicy());
+        loadState->SetReferrerInfo(referrerInfo);
+      }
     }
   }
 
   // Currently we query the CSP from the principal of the inner window.
   // After Bug 965637 we can query the CSP directly from the inner window.
   // Further, if the JS context is null, then the subjectPrincipal falls
   // back to being the SystemPrincipal (see above) and the SystemPrincipal
   // can currently not hold a CSP. We use the same semantics here.
--- a/toolkit/components/windowwatcher/nsWindowWatcher.h
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.h
@@ -79,17 +79,17 @@ class nsWindowWatcher : public nsIWindow
                                           mozIDOMWindowProxy* aCurrentWindow);
 
   // Just like OpenWindowJS, but knows whether it got called via OpenWindowJS
   // (which means called from script) or called via OpenWindow.
   nsresult OpenWindowInternal(mozIDOMWindowProxy* aParent, const char* aUrl,
                               const char* aName, const char* aFeatures,
                               bool aCalledFromJS, bool aDialog, bool aNavigate,
                               nsIArray* aArgv, bool aIsPopupSpam,
-                              bool aForceNoOpener,
+                              bool aForceNoOpener, bool aForceNoReferrer,
                               nsDocShellLoadState* aLoadState,
                               mozIDOMWindowProxy** aResult);
 
   static nsresult URIfromURL(const char* aURL, mozIDOMWindowProxy* aParent,
                              nsIURI** aURI);
 
   static uint32_t CalculateChromeFlagsForChild(const nsACString& aFeaturesStr);
 
--- a/xpfe/appshell/nsContentTreeOwner.cpp
+++ b/xpfe/appshell/nsContentTreeOwner.cpp
@@ -698,17 +698,17 @@ NS_IMETHODIMP nsContentTreeOwner::SetTit
 //*****************************************************************************
 // nsContentTreeOwner: nsIWindowProvider
 //*****************************************************************************
 NS_IMETHODIMP
 nsContentTreeOwner::ProvideWindow(
     mozIDOMWindowProxy* aParent, uint32_t aChromeFlags, bool aCalledFromJS,
     bool aPositionSpecified, bool aSizeSpecified, nsIURI* aURI,
     const nsAString& aName, const nsACString& aFeatures, bool aForceNoOpener,
-    nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
+    bool aForceNoReferrer, nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
     mozIDOMWindowProxy** aReturn) {
   NS_ENSURE_ARG_POINTER(aParent);
 
   auto* parentWin = nsPIDOMWindowOuter::From(aParent);
   dom::BrowsingContext* parent =
       parentWin ? parentWin->GetBrowsingContext() : nullptr;
 
   *aReturn = nullptr;
@@ -792,16 +792,19 @@ nsContentTreeOwner::ProvideWindow(
 
   {
     dom::AutoNoJSAPI nojsapi;
 
     uint32_t flags = nsIBrowserDOMWindow::OPEN_NEW;
     if (aForceNoOpener) {
       flags |= nsIBrowserDOMWindow::OPEN_NO_OPENER;
     }
+    if (aForceNoReferrer) {
+      flags |= nsIBrowserDOMWindow::OPEN_NO_REFERRER;
+    }
 
     // Get a new rendering area from the browserDOMWin.
     // Since we are not loading any URI, we follow the principle of least
     // privilege and use a nullPrincipal as the triggeringPrincipal.
     //
     // This method handles setting the opener for us, so we don't need to set it
     // ourselves.
     RefPtr<NullPrincipal> nullPrincipal =