Bug 1303196 - Part 7: Updates to apply correctly after noopener changes, r=smaug
authorMichael Layzell <michael@thelayzells.com>
Thu, 27 Oct 2016 12:37:44 -0400
changeset 319866 74ef5b08ae25
parent 319865 fc29cabb300b
child 319867 2a31079dae25
push id30882
push userryanvm@gmail.com
push dateSat, 29 Oct 2016 13:12:06 +0000
treeherdermozilla-central@16cdd6273c48 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1303196
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1303196 - Part 7: Updates to apply correctly after noopener changes, r=smaug MozReview-Commit-ID: 25Ew2PLpP5c
browser/base/content/browser.js
devtools/client/responsive.html/browser/tunnel.js
dom/base/nsHostObjectURI.cpp
dom/browser-element/BrowserElementParent.cpp
dom/browser-element/BrowserElementParent.h
dom/interfaces/base/nsIBrowserDOMWindow.idl
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/TabChild.cpp
embedding/components/windowwatcher/nsWindowWatcher.cpp
embedding/nsIWindowProvider.idl
mobile/android/chrome/content/browser.js
uriloader/exthandler/nsWebHandlerApp.js
xpfe/appshell/nsContentTreeOwner.cpp
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4938,27 +4938,27 @@ nsBrowserAccess.prototype = {
     let browser = win.gBrowser.getBrowserForTab(tab);
 
     if (needToFocusWin || (!loadInBackground && aIsExternal))
       win.focus();
 
     return browser;
   },
 
-  openURI: function (aURI, aOpener, aWhere, aContext) {
+  openURI: function (aURI, aOpener, aWhere, aFlags) {
     // This function should only ever be called if we're opening a URI
     // from a non-remote browser window (via nsContentTreeOwner).
     if (aOpener && Cu.isCrossProcessWrapper(aOpener)) {
       Cu.reportError("nsBrowserAccess.openURI was passed a CPOW for aOpener. " +
                      "openURI should only ever be called from non-remote browsers.");
       throw Cr.NS_ERROR_FAILURE;
     }
 
     var newWindow = null;
-    var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
+    var isExternal = !!(aFlags & Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
     if (aOpener && isExternal) {
       Cu.reportError("nsBrowserAccess.openURI did not expect an opener to be " +
                      "passed if the context is OPEN_EXTERNAL.");
       throw Cr.NS_ERROR_FAILURE;
     }
 
     if (isExternal && aURI && aURI.schemeIs("chrome")) {
@@ -5002,20 +5002,21 @@ nsBrowserAccess.prototype = {
         // this means forcing the newly opened browser to be non-remote so that
         // we can hand back the nsIDOMWindow. The XULBrowserWindow.shouldLoadURI
         // will do the job of shuttling off the newly opened browser to run in
         // the right process once it starts loading a URI.
         let forceNotRemote = !!aOpener;
         let userContextId = aOpener && aOpener.document
                               ? aOpener.document.nodePrincipal.originAttributes.userContextId
                               : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
+        let openerWindow = (aFlags & Ci.nsIBrowserDOMWindow.OPEN_NO_OPENER) ? null : aOpener;
         let browser = this._openURIInNewTab(aURI, referrer, referrerPolicy,
                                             isPrivate, isExternal,
                                             forceNotRemote, userContextId,
-                                            aOpener);
+                                            openerWindow);
         if (browser)
           newWindow = browser.contentWindow;
         break;
       default : // OPEN_CURRENTWINDOW or an illegal value
         newWindow = content;
         if (aURI) {
           let loadflags = isExternal ?
                             Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
@@ -5027,23 +5028,23 @@ nsBrowserAccess.prototype = {
                                     });
         }
         if (!gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"))
           window.focus();
     }
     return newWindow;
   },
 
-  openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aContext) {
+  openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aFlags) {
     if (aWhere != Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
       dump("Error: openURIInFrame can only open in new tabs");
       return null;
     }
 
-    var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
+    var isExternal = !!(aFlags & Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
     var userContextId = aParams.openerOriginAttributes &&
                         ("userContextId" in aParams.openerOriginAttributes)
                           ? aParams.openerOriginAttributes.userContextId
                           : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID
 
     let browser = this._openURIInNewTab(aURI, aParams.referrer,
                                         aParams.referrerPolicy,
--- a/devtools/client/responsive.html/browser/tunnel.js
+++ b/devtools/client/responsive.html/browser/tunnel.js
@@ -226,18 +226,18 @@ function tunnelToInnerBrowser(outer, inn
       let { detail } = event;
       event.preventDefault();
       let uri = Services.io.newURI(detail.url, null, null);
       // 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_NEWTAB);
+        .openURI(uri, null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
+                 Ci.nsIBrowserDOMWindow.OPEN_NEW);
     },
 
     stop() {
       let tab = gBrowser.getTabForBrowser(outer);
       let filteredProgressListener = gBrowser._tabFilters.get(tab);
 
       // The browser's state has changed over time while the tunnel was active.  Push the
       // the current state down to the inner browser, so that it follows the content in
--- a/dom/base/nsHostObjectURI.cpp
+++ b/dom/base/nsHostObjectURI.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsHostObjectURI.h"
 
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
+#include "nsHostObjectProtocolHandler.h"
 
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/URIUtils.h"
 
 static NS_DEFINE_CID(kHOSTOBJECTURICID, NS_HOSTOBJECTURI_CID);
 
 static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
                      NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
--- a/dom/browser-element/BrowserElementParent.cpp
+++ b/dom/browser-element/BrowserElementParent.cpp
@@ -251,16 +251,17 @@ BrowserElementParent::OpenWindowOOP(TabP
 }
 
 /* static */
 BrowserElementParent::OpenWindowResult
 BrowserElementParent::OpenWindowInProcess(nsPIDOMWindowOuter* aOpenerWindow,
                                           nsIURI* aURI,
                                           const nsAString& aName,
                                           const nsACString& aFeatures,
+                                          bool aForceNoOpener,
                                           mozIDOMWindowProxy** aReturnWindow)
 {
   *aReturnWindow = nullptr;
 
   // If we call window.open from an <iframe> inside an <iframe mozbrowser>,
   // it's as though the top-level document inside the <iframe mozbrowser>
   // called window.open.  (Indeed, in the OOP case, the inner <iframe> lives
   // out-of-process, so we couldn't touch it if we tried.)
@@ -278,19 +279,21 @@ BrowserElementParent::OpenWindowInProces
     CreateIframe(openerFrameElement, aName, /* aRemote = */ false);
   NS_ENSURE_TRUE(popupFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
 
   nsAutoCString spec;
   if (aURI) {
     aURI->GetSpec(spec);
   }
 
-  ErrorResult res;
-  popupFrameElement->PresetOpenerWindow(aOpenerWindow, res);
-  MOZ_ASSERT(!res.Failed());
+  if (!aForceNoOpener) {
+    ErrorResult res;
+    popupFrameElement->PresetOpenerWindow(aOpenerWindow, res);
+    MOZ_ASSERT(!res.Failed());
+  }
 
   OpenWindowResult opened =
     DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
                             NS_ConvertUTF8toUTF16(spec),
                             aName,
                             NS_ConvertUTF8toUTF16(aFeatures));
 
   if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
--- a/dom/browser-element/BrowserElementParent.h
+++ b/dom/browser-element/BrowserElementParent.h
@@ -113,16 +113,17 @@ public:
    *         frame to a document or whether they called preventDefault to prevent
    *         the platform from handling the open request
    */
   static OpenWindowResult
   OpenWindowInProcess(nsPIDOMWindowOuter* 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,
--- a/dom/interfaces/base/nsIBrowserDOMWindow.idl
+++ b/dom/interfaces/base/nsIBrowserDOMWindow.idl
@@ -57,51 +57,64 @@ interface nsIBrowserDOMWindow : nsISuppo
   const short OPEN_NEWTAB        = 3;
   /**
    * Open in an existing content tab based on the URI. If a match can't be
    * found, revert to OPEN_NEWTAB behavior.
    */
   const short OPEN_SWITCHTAB     = 4;
 
   /**
-   * Values for openURI's aContext parameter.  These affect the behavior of
-   * OPEN_DEFAULTWINDOW.
+   * Values for openURI's aFlags parameter. This is a bitflags field.
+   *
+   * The 0x1 bit decides the behavior of OPEN_DEFAULTWINDOW, and the 0x4 bit
+   * controls whether or not to set the window.opener property on the newly
+   * opened window.
+   *
+   * NOTE: The 0x2 bit is ignored for backwards compatibility with addons, as
+   * OPEN_NEW used to have the value 2. The values 0 and 2 are treated
+   * the same way internally.
    */
   /**
+   * internal open new window
+   */
+  const long OPEN_NEW           = 0x0;
+  /**
    * external link (load request from another application, xremote, etc).
    */
-  const short OPEN_EXTERNAL      = 1;
+  const long OPEN_EXTERNAL      = 0x1;
+
   /**
-   * internal open new window
+   * Don't set the window.opener property on the window which is being opened
    */
-  const short OPEN_NEW           = 2; 
+  const long OPEN_NO_OPENER     = 0x4;
 
   /**
    * Load a URI
 
    * @param aURI the URI to open. null is allowed.  If null is passed in, no
    *             load will be done, though the window the load would have
    *             happened in will be returned.
    * @param aWhere see possible values described above.
    * @param aOpener window requesting the open (can be null).
-   * @param aContext the context in which the URI is being opened. This
-   *                 is used only when aWhere == OPEN_DEFAULTWINDOW.
+   * @param aFlags flags which control the behavior of the load. The
+   *               OPEN_EXTERNAL/OPEN_NEW flag is only used when
+   *               aWhere == OPEN_DEFAULTWINDOW.
    * @return the window into which the URI was opened.
   */
   mozIDOMWindowProxy
   openURI(in nsIURI aURI, in mozIDOMWindowProxy aOpener,
-          in short aWhere, in short aContext);
+          in short aWhere, in long aFlags);
 
   /**
    * As above, but return the nsIFrameLoaderOwner for the new window.
    // XXXbz is this the right API?
    // See bug 537428
    */
   nsIFrameLoaderOwner openURIInFrame(in nsIURI aURI, in nsIOpenURIInFrameParams params,
-                                     in short aWhere, in short aContext);
+                                     in short aWhere, in long aFlags);
 
   /**
    * @param  aWindow the window to test.
    * @return whether the window is the main content window for any
    *         currently open tab in this toplevel browser window.
    */
   boolean isTabContentWindow(in nsIDOMWindow aWindow);
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -657,36 +657,38 @@ 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* aWindowIsNew,
                             mozIDOMWindowProxy** aReturn)
 {
   return ProvideWindowCommon(nullptr, aParent, false, aChromeFlags,
                              aCalledFromJS, aPositionSpecified,
                              aSizeSpecified, aURI, aName, aFeatures,
-                             aWindowIsNew, aReturn);
+                             aForceNoOpener, aWindowIsNew, aReturn);
 }
 
 nsresult
 ContentChild::ProvideWindowCommon(TabChild* 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* aWindowIsNew,
                                   mozIDOMWindowProxy** aReturn)
 {
   *aReturn = nullptr;
 
   nsAutoPtr<IPCTabContext> ipcContext;
   TabId openerTabId = TabId(0);
 
@@ -826,17 +828,17 @@ ContentChild::ProvideWindowCommon(TabChi
                         aTabOpener->mDefaultScale);
   }
 
   // Set the opener window for this window before we start loading the document
   // inside of it. We have to do this before loading the remote scripts, because
   // they can poke at the document and cause the nsDocument to be created before
   // the openerwindow
   nsCOMPtr<mozIDOMWindowProxy> windowProxy = do_GetInterface(newChild->WebNavigation());
-  if (windowProxy && aParent) {
+  if (!aForceNoOpener && windowProxy && aParent) {
     nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(windowProxy);
     nsPIDOMWindowOuter* parent = nsPIDOMWindowOuter::From(aParent);
     outer->SetOpenerWindow(parent, *aWindowIsNew);
   }
 
   // Unfortunately we don't get a window unless we've shown the frame.  That's
   // pretty bogus; see bug 763602.
   newChild->DoFakeShow(textureFactoryIdentifier, layersId, renderFrame,
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -83,16 +83,17 @@ public:
                       bool aIframeMoz,
                       uint32_t aChromeFlags,
                       bool aCalledFromJS,
                       bool aPositionSpecified,
                       bool aSizeSpecified,
                       nsIURI* aURI,
                       const nsAString& aName,
                       const nsACString& aFeatures,
+                      bool aForceNoOpener,
                       bool* aWindowIsNew,
                       mozIDOMWindowProxy** aReturn);
 
   bool Init(MessageLoop* aIOLoop,
             base::ProcessId aParentPid,
             IPC::Channel* aChannel);
 
   void InitProcessAttributes();
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1164,18 +1164,18 @@ TabChild::GetInterface(const nsIID & aII
 }
 
 NS_IMETHODIMP
 TabChild::ProvideWindow(mozIDOMWindowProxy* aParent,
                         uint32_t aChromeFlags,
                         bool aCalledFromJS,
                         bool aPositionSpecified, bool aSizeSpecified,
                         nsIURI* aURI, const nsAString& aName,
-                        const nsACString& aFeatures, bool* aWindowIsNew,
-                        mozIDOMWindowProxy** aReturn)
+                        const nsACString& aFeatures, bool aForceNoOpener,
+                        bool* aWindowIsNew, mozIDOMWindowProxy** aReturn)
 {
     *aReturn = nullptr;
 
     // If aParent is inside an <iframe mozbrowser> or <iframe mozapp> and this
     // isn't a request to open a modal-type window, we're going to create a new
     // <iframe mozbrowser/mozapp> and return its window here.
     nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
     bool iframeMoz = (docshell && docshell->GetIsInMozBrowserOrApp() &&
@@ -1207,16 +1207,17 @@ TabChild::ProvideWindow(mozIDOMWindowPro
                                    iframeMoz,
                                    aChromeFlags,
                                    aCalledFromJS,
                                    aPositionSpecified,
                                    aSizeSpecified,
                                    aURI,
                                    aName,
                                    aFeatures,
+                                   aForceNoOpener,
                                    aWindowIsNew,
                                    aReturn);
 }
 
 void
 TabChild::DestroyWindow()
 {
     nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
--- a/embedding/components/windowwatcher/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp
@@ -878,18 +878,18 @@ nsWindowWatcher::OpenWindowInternal(mozI
         provider = nsContentUtils::GetWindowProviderForContentProcess();
       }
 
       if (provider) {
         nsCOMPtr<mozIDOMWindowProxy> newWindow;
         rv = provider->ProvideWindow(aParent, chromeFlags, aCalledFromJS,
                                      sizeSpec.PositionSpecified(),
                                      sizeSpec.SizeSpecified(),
-                                     uriToLoad, name, features, &windowIsNew,
-                                     getter_AddRefs(newWindow));
+                                     uriToLoad, name, features, aForceNoOpener,
+                                     &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.
@@ -993,31 +993,32 @@ nsWindowWatcher::OpenWindowInternal(mozI
           popupConditions = !isCallerChrome;
         }
 
         if (popupConditions) {
           contextFlags |=
             nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT;
         }
 
+        mozIDOMWindowProxy* openerWindow = aForceNoOpener ? nullptr : aParent;
         rv = CreateChromeWindow(features, parentChrome, chromeFlags, contextFlags,
-                                nullptr, aParent, getter_AddRefs(newChrome));
+                                nullptr, openerWindow, getter_AddRefs(newChrome));
 
       } else {
         rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags,
                                                 getter_AddRefs(newChrome));
       }
 
       if (newChrome) {
         nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(newChrome);
         if (xulWin) {
           nsCOMPtr<nsIXULBrowserWindow> xulBrowserWin;
           xulWin->GetXULBrowserWindow(getter_AddRefs(xulBrowserWin));
           if (xulBrowserWin) {
-            nsPIDOMWindowOuter* openerWindow = aForceNoOpener ? nullptr : parentWindow;
+            nsPIDOMWindowOuter* openerWindow = aForceNoOpener ? nullptr : parentWindow.get();
             xulBrowserWin->ForceInitialBrowserNonRemote(openerWindow);
           }
         }
         /* It might be a chrome nsXULWindow, in which case it won't have
             an nsIDOMWindow (primary content shell). But in that case, it'll
             be able to hand over an nsIDocShellTreeItem directly. */
         nsCOMPtr<nsPIDOMWindowOuter> newWindow(do_GetInterface(newChrome));
         if (newWindow) {
@@ -2142,17 +2143,17 @@ nsWindowWatcher::ReadyOpenedDocShellItem
 
   NS_ENSURE_ARG(aOpenedWindow);
 
   *aOpenedWindow = 0;
   nsCOMPtr<nsPIDOMWindowOuter> piOpenedWindow = aOpenedItem->GetWindow();
   if (piOpenedWindow) {
     if (!aForceNoOpener) {
       piOpenedWindow->SetOpenerWindow(aParent, aWindowIsNew); // damnit
-    } else if (aParent) {
+    } else if (aParent && aParent != piOpenedWindow) {
       MOZ_ASSERT(nsGlobalWindow::Cast(piOpenedWindow)->TabGroup() !=
                  nsGlobalWindow::Cast(aParent)->TabGroup(),
                  "If we're forcing no opener, they should be in different tab groups");
     }
 
     if (aWindowIsNew) {
 #ifdef DEBUG
       // Assert that we're not loading things right now.  If we are, when
--- a/embedding/nsIWindowProvider.idl
+++ b/embedding/nsIWindowProvider.idl
@@ -93,10 +93,11 @@ interface nsIWindowProvider : nsISupport
   mozIDOMWindowProxy provideWindow(in mozIDOMWindowProxy aParent,
                                    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,
                                    out boolean aWindowIsNew);
 };
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3225,31 +3225,29 @@ var DesktopUserAgent = {
 
 
 function nsBrowserAccess() {
 }
 
 nsBrowserAccess.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow]),
 
-  _getBrowser: function _getBrowser(aURI, aOpener, aWhere, aContext) {
-    let isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
+  _getBrowser: function _getBrowser(aURI, aOpener, aWhere, aFlags) {
+    let isExternal = !!(aFlags & Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
     if (isExternal && aURI && aURI.schemeIs("chrome"))
       return null;
 
     let loadflags = isExternal ?
                       Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
                       Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
     if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
-      switch (aContext) {
-        case Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL:
-          aWhere = Services.prefs.getIntPref("browser.link.open_external");
-          break;
-        default: // OPEN_NEW or an illegal value
-          aWhere = Services.prefs.getIntPref("browser.link.open_newwindow");
+      if (isExternal) {
+        aWhere = Services.prefs.getIntPref("browser.link.open_external");
+      } else {
+        aWhere = Services.prefs.getIntPref("browser.link.open_newwindow");
       }
     }
 
     Services.io.offline = false;
 
     let referrer;
     if (aOpener) {
       try {
@@ -3287,45 +3285,46 @@ nsBrowserAccess.prototype = {
       if (!isExternal && aOpener) {
         let parent = BrowserApp.getTabForWindow(aOpener.top);
         if (parent) {
           parentId = parent.id;
           isPrivate = PrivateBrowsingUtils.isBrowserPrivate(parent.browser);
         }
       }
 
+      let openerWindow = (aFlags & Ci.nsIBrowserDOMWindow.OPEN_NO_OPENER) ? null : aOpener;
       // BrowserApp.addTab calls loadURIWithFlags with the appropriate params
       let tab = BrowserApp.addTab(aURI ? aURI.spec : "about:blank", { flags: loadflags,
                                                                       referrerURI: referrer,
                                                                       external: isExternal,
                                                                       parentId: parentId,
-                                                                      opener: aOpener,
+                                                                      opener: openerWindow,
                                                                       selected: true,
                                                                       isPrivate: isPrivate,
                                                                       pinned: pinned });
 
       return tab.browser;
     }
 
     // OPEN_CURRENTWINDOW and illegal values
     let browser = BrowserApp.selectedBrowser;
     if (aURI && browser) {
       browser.loadURIWithFlags(aURI.spec, loadflags, referrer, null, null);
     }
 
     return browser;
   },
 
-  openURI: function browser_openURI(aURI, aOpener, aWhere, aContext) {
-    let browser = this._getBrowser(aURI, aOpener, aWhere, aContext);
+  openURI: function browser_openURI(aURI, aOpener, aWhere, aFlags) {
+    let browser = this._getBrowser(aURI, aOpener, aWhere, aFlags);
     return browser ? browser.contentWindow : null;
   },
 
-  openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aContext) {
-    let browser = this._getBrowser(aURI, null, aWhere, aContext);
+  openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aFlags) {
+    let browser = this._getBrowser(aURI, null, aWhere, aFlags);
     return browser ? browser.QueryInterface(Ci.nsIFrameLoaderOwner) : null;
   },
 
   isTabContentWindow: function(aWindow) {
     return BrowserApp.getBrowserForWindow(aWindow) != null;
   },
 
   canClose() {
--- a/uriloader/exthandler/nsWebHandlerApp.js
+++ b/uriloader/exthandler/nsWebHandlerApp.js
@@ -136,17 +136,17 @@ nsWebHandlerApp.prototype = {
     //    command line handler.  
     // c) something else went wrong 
     //
     // it's not clear how one would differentiate between the three cases
     // above, so for now we don't catch the exception
 
     // openURI
     browserDOMWin.openURI(uriToSend,
-                          null, // no window.opener 
+                          null, // no window.opener
                           Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW,
                           Ci.nsIBrowserDOMWindow.OPEN_NEW);
       
     return;
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// nsIWebHandlerApp
--- a/xpfe/appshell/nsContentTreeOwner.cpp
+++ b/xpfe/appshell/nsContentTreeOwner.cpp
@@ -776,16 +776,17 @@ 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,
                                   bool* aWindowIsNew,
                                   mozIDOMWindowProxy** aReturn)
 {
   NS_ENSURE_ARG_POINTER(aParent);
 
   auto* parent = nsPIDOMWindowOuter::From(aParent);
   
   *aReturn = nullptr;
@@ -809,17 +810,17 @@ nsContentTreeOwner::ProvideWindow(mozIDO
   nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
   if (docshell && docshell->GetIsInMozBrowserOrApp() &&
       !(aChromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
                         nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
                         nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
 
     BrowserElementParent::OpenWindowResult opened =
       BrowserElementParent::OpenWindowInProcess(parent, aURI, aName,
-                                                aFeatures, aReturn);
+                                                aFeatures, aForceNoOpener, aReturn);
 
     // If OpenWindowInProcess handled the open (by opening it or blocking the
     // popup), tell our caller not to proceed trying to create a new window
     // through other means.
     if (opened != BrowserElementParent::OPEN_WINDOW_IGNORED) {
       *aWindowIsNew = opened == BrowserElementParent::OPEN_WINDOW_ADDED;
       return *aWindowIsNew ? NS_OK : NS_ERROR_ABORT;
     }
@@ -868,23 +869,29 @@ nsContentTreeOwner::ProvideWindow(mozIDO
     return NS_OK;
   }
 
   *aWindowIsNew = (openLocation != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW);
 
   {
     dom::AutoNoJSAPI nojsapi;
 
+    uint32_t flags = nsIBrowserDOMWindow::OPEN_NEW;
+    if (aForceNoOpener) {
+      flags |= nsIBrowserDOMWindow::OPEN_NO_OPENER;
+    }
+
     // Get a new rendering area from the browserDOMWin.  We don't want
     // to be starting any loads here, so get it with a null URI.
     //
     // This method handles setting the opener for us, so we don't need to set it
     // ourselves.
-    return browserDOMWin->OpenURI(nullptr, aParent, openLocation,
-                                  nsIBrowserDOMWindow::OPEN_NEW, aReturn);
+    return browserDOMWin->OpenURI(nullptr, aParent,
+                                  openLocation,
+                                  flags, aReturn);
   }
 }
 
 //*****************************************************************************
 // nsContentTreeOwner: Accessors
 //*****************************************************************************
 
 #if defined(XP_MACOSX)