Bug 1147911 Part 7: Create browsing context with no opener if URI needs different process. r=smaug
☠☠ backed out by 4c2a78d4a8c3 ☠ ☠
authorBob Owen <bobowencode@gmail.com>
Wed, 23 Nov 2016 13:36:58 +0000
changeset 324086 f8ff074e914525117bee0d236447521efcda3eb3
parent 324085 360c780c0a95e2a85ae2d82065638b4a2ea62299
child 324087 2a7569652090bc2ca88c358b1afba087d8a9d861
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewerssmaug
bugs1147911
milestone53.0a1
Bug 1147911 Part 7: Create browsing context with no opener if URI needs different process. r=smaug This also means window.open returns null in the same circumstance.
browser/base/content/tab-content.js
dom/base/nsFrameLoader.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/ipc/TabChild.cpp
embedding/browser/nsIWebBrowserChrome3.idl
xpfe/appshell/nsContentTreeOwner.cpp
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -617,16 +617,20 @@ var WebBrowserChrome = {
     if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer)) {
       E10SUtils.redirectLoad(aDocShell, aURI, aReferrer);
       return false;
     }
 
     return true;
   },
 
+  shouldLoadURIInThisProcess: function(aURI) {
+    return E10SUtils.shouldLoadURIInThisProcess(aURI);
+  },
+
   // Try to reload the currently active or currently loading page in a new process.
   reloadInFreshProcess: function(aDocShell, aURI, aReferrer) {
     E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, true);
     return true;
   }
 };
 
 if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -42,22 +42,23 @@ namespace mozilla {
 class DocShellOriginAttributes;
 
 namespace dom {
 class ContentParent;
 class PBrowserParent;
 class Promise;
 class TabParent;
 class MutableTabContext;
-} // namespace dom
 
 namespace ipc {
 class StructuredCloneData;
 } // namespace ipc
 
+} // namespace dom
+
 namespace layout {
 class RenderFrameParent;
 } // namespace layout
 } // namespace mozilla
 
 #if defined(MOZ_WIDGET_GTK)
 typedef struct _GtkWidget GtkWidget;
 #endif
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -650,16 +650,53 @@ ContentChild::ProvideWindow(mozIDOMWindo
                             mozIDOMWindowProxy** aReturn)
 {
   return ProvideWindowCommon(nullptr, aParent, false, aChromeFlags,
                              aCalledFromJS, aPositionSpecified,
                              aSizeSpecified, aURI, aName, aFeatures,
                              aForceNoOpener, aWindowIsNew, aReturn);
 }
 
+static nsresult
+GetWindowParamsFromParent(mozIDOMWindowProxy* aParent,
+                          nsACString& aBaseURIString, float* aFullZoom,
+                          DocShellOriginAttributes& aOriginAttributes)
+{
+  *aFullZoom = 1.0f;
+  auto* opener = nsPIDOMWindowOuter::From(aParent);
+  if (!opener) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDocument> doc = opener->GetDoc();
+  nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
+  if (!baseURI) {
+    NS_ERROR("nsIDocument didn't return a base URI");
+    return NS_ERROR_FAILURE;
+  }
+
+  baseURI->GetSpec(aBaseURIString);
+
+  RefPtr<nsDocShell> openerDocShell =
+    static_cast<nsDocShell*>(opener->GetDocShell());
+  if (!openerDocShell) {
+    return NS_OK;
+  }
+
+  aOriginAttributes = openerDocShell->GetOriginAttributes();
+
+  nsCOMPtr<nsIContentViewer> cv;
+  nsresult rv = openerDocShell->GetContentViewer(getter_AddRefs(cv));
+  if (NS_SUCCEEDED(rv) && cv) {
+    cv->GetFullZoom(aFullZoom);
+  }
+
+  return NS_OK;
+}
+
 nsresult
 ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
                                   mozIDOMWindowProxy* aParent,
                                   bool aIframeMoz,
                                   uint32_t aChromeFlags,
                                   bool aCalledFromJS,
                                   bool aPositionSpecified,
                                   bool aSizeSpecified,
@@ -669,18 +706,53 @@ ContentChild::ProvideWindowCommon(TabChi
                                   bool aForceNoOpener,
                                   bool* aWindowIsNew,
                                   mozIDOMWindowProxy** aReturn)
 {
   *aReturn = nullptr;
 
   nsAutoPtr<IPCTabContext> ipcContext;
   TabId openerTabId = TabId(0);
-
+  nsAutoCString features(aFeatures);
+
+  nsresult rv;
   if (aTabOpener) {
+    // Check to see if the target URI can be loaded in this process.
+    // If not create and load it in an unrelated tab/window.
+    nsCOMPtr<nsIWebBrowserChrome3> browserChrome3;
+    rv = aTabOpener->GetWebBrowserChrome(getter_AddRefs(browserChrome3));
+    if (NS_SUCCEEDED(rv) && browserChrome3) {
+      bool shouldLoad;
+      rv = browserChrome3->ShouldLoadURIInThisProcess(aURI, &shouldLoad);
+      if (NS_SUCCEEDED(rv) && !shouldLoad) {
+        nsAutoCString baseURIString;
+        float fullZoom;
+        DocShellOriginAttributes originAttributes;
+        rv = GetWindowParamsFromParent(aParent, baseURIString, &fullZoom,
+                                       originAttributes);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+
+        URIParams uriToLoad;
+        SerializeURI(aURI, uriToLoad);
+        Unused << SendCreateWindowInDifferentProcess(aTabOpener, aChromeFlags,
+                                                     aCalledFromJS,
+                                                     aPositionSpecified,
+                                                     aSizeSpecified,
+                                                     uriToLoad, features,
+                                                     baseURIString,
+                                                     originAttributes, fullZoom);
+
+        // We return NS_ERROR_ABORT, so that the caller knows that we've abandoned
+        // the window open as far as it is concerned.
+        return NS_ERROR_ABORT;
+      }
+    }
+
     PopupIPCTabContext context;
     openerTabId = aTabOpener->GetTabId();
     context.opener() = openerTabId;
     context.isMozBrowserElement() = aTabOpener->IsMozBrowserElement();
     ipcContext = new IPCTabContext(context);
   } else {
     // It's possible to not have a TabChild opener in the case
     // of ServiceWorker::OpenWindow.
@@ -709,17 +781,16 @@ ContentChild::ProvideWindowCommon(TabChi
 
   Unused << SendPBrowserConstructor(
     // We release this ref in DeallocPBrowserChild
     RefPtr<TabChild>(newChild).forget().take(),
     tabId, *ipcContext, aChromeFlags,
     GetID(), IsForBrowser());
 
   nsString name(aName);
-  nsAutoCString features(aFeatures);
   nsTArray<FrameScriptInfo> frameScripts;
   nsCString urlToLoad;
 
   PRenderFrameChild* renderFrame = newChild->SendPRenderFrameConstructor();
   TextureFactoryIdentifier textureFactoryIdentifier;
   uint64_t layersId = 0;
 
   if (aIframeMoz) {
@@ -735,50 +806,30 @@ ContentChild::ProvideWindowCommon(TabChi
     }
 
     newChild->SendBrowserFrameOpenWindow(aTabOpener, renderFrame, NS_ConvertUTF8toUTF16(url),
                                          name, NS_ConvertUTF8toUTF16(features),
                                          aWindowIsNew, &textureFactoryIdentifier,
                                          &layersId);
   } else {
     nsAutoCString baseURIString;
-    if (aTabOpener) {
-      auto* opener = nsPIDOMWindowOuter::From(aParent);
-      nsCOMPtr<nsIDocument> doc = opener->GetDoc();
-      nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
-      if (!baseURI) {
-        NS_ERROR("nsIDocument didn't return a base URI");
-        return NS_ERROR_FAILURE;
-      }
-
-      baseURI->GetSpec(baseURIString);
+    float fullZoom;
+    DocShellOriginAttributes originAttributes;
+    rv = GetWindowParamsFromParent(aParent, baseURIString, &fullZoom,
+                                   originAttributes);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
     }
 
-    auto* opener = nsPIDOMWindowOuter::From(aParent);
-    nsIDocShell* openerShell;
-    RefPtr<nsDocShell> openerDocShell;
-    float fullZoom = 1.0f;
-    if (opener && (openerShell = opener->GetDocShell())) {
-      openerDocShell = static_cast<nsDocShell*>(openerShell);
-      nsCOMPtr<nsIContentViewer> cv;
-      openerDocShell->GetContentViewer(getter_AddRefs(cv));
-      if (cv) {
-        cv->GetFullZoom(&fullZoom);
-      }
-    }
-
-    nsresult rv;
     if (!SendCreateWindow(aTabOpener, newChild, renderFrame,
                           aChromeFlags, aCalledFromJS, aPositionSpecified,
                           aSizeSpecified,
                           features,
                           baseURIString,
-                          openerDocShell
-                            ? openerDocShell->GetOriginAttributes()
-                            : DocShellOriginAttributes(),
+                          originAttributes,
                           fullZoom,
                           &rv,
                           aWindowIsNew,
                           &frameScripts,
                           &urlToLoad,
                           &textureFactoryIdentifier,
                           &layersId)) {
       PRenderFrameChild::Send__delete__(renderFrame);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -102,16 +102,17 @@
 #include "nsCDefaultURIFixup.h"
 #include "nsCExternalHandlerService.h"
 #include "nsCOMPtr.h"
 #include "nsChromeRegistryChrome.h"
 #include "nsConsoleMessage.h"
 #include "nsConsoleService.h"
 #include "nsContentUtils.h"
 #include "nsDebugImpl.h"
+#include "nsFrameLoader.h"
 #include "nsFrameMessageManager.h"
 #include "nsHashPropertyBag.h"
 #include "nsIAlertsService.h"
 #include "nsIClipboard.h"
 #include "nsContentPermissionHelper.h"
 #include "nsICycleCollectorListener.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocument.h"
@@ -4152,16 +4153,140 @@ ContentParent::AllocPWebBrowserPersistDo
 bool
 ContentParent::DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor)
 {
   delete aActor;
   return true;
 }
 
 mozilla::ipc::IPCResult
+ContentParent::CommonCreateWindow(PBrowserParent* aThisTab,
+                                  bool aSetOpener,
+                                  const uint32_t& aChromeFlags,
+                                  const bool& aCalledFromJS,
+                                  const bool& aPositionSpecified,
+                                  const bool& aSizeSpecified,
+                                  nsIURI* aURIToLoad,
+                                  const nsCString& aFeatures,
+                                  const nsCString& aBaseURI,
+                                  const DocShellOriginAttributes& aOpenerOriginAttributes,
+                                  const float& aFullZoom,
+                                  nsresult& aResult,
+                                  nsCOMPtr<nsITabParent>& aNewTabParent,
+                                  bool* aWindowIsNew)
+
+{
+  // The content process should never be in charge of computing whether or
+  // not a window should be private or remote - the parent will do that.
+  const uint32_t badFlags = nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
+                            nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW |
+                            nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME |
+                            nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
+  if (!!(aChromeFlags & badFlags)) {
+    return IPC_FAIL(this, "Forbidden aChromeFlags passed");
+  }
+
+  TabParent* thisTabParent = TabParent::GetFrom(aThisTab);
+  nsCOMPtr<nsIContent> frame;
+  if (thisTabParent) {
+    frame = do_QueryInterface(thisTabParent->GetOwnerElement());
+
+    if (NS_WARN_IF(thisTabParent->IsMozBrowser())) {
+      return IPC_FAIL(this, "aThisTab is not a MozBrowser");
+    }
+  }
+
+  nsCOMPtr<nsPIDOMWindowOuter> outerWin;
+  if (frame) {
+    outerWin = frame->OwnerDoc()->GetWindow();
+
+    // If our chrome window is in the process of closing, don't try to open a
+    // new tab in it.
+    if (outerWin && outerWin->Closed()) {
+      outerWin = nullptr;
+    }
+  }
+
+  nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
+  if (thisTabParent) {
+    browserDOMWin = thisTabParent->GetBrowserDOMWindow();
+  }
+
+  // If we haven't found a chrome window to open in, just use the most recently
+  // opened one.
+  if (!outerWin) {
+    outerWin = nsContentUtils::GetMostRecentNonPBWindow();
+    if (NS_WARN_IF(!outerWin)) {
+      aResult = NS_ERROR_FAILURE;
+      return IPC_OK();
+    }
+
+    nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(outerWin);
+    if (rootChromeWin) {
+      rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
+    }
+  }
+
+  int32_t openLocation = nsWindowWatcher::GetWindowOpenLocation(
+    outerWin, aChromeFlags, aCalledFromJS, aPositionSpecified, aSizeSpecified);
+
+  MOZ_ASSERT(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
+             openLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);
+
+  if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
+    if (NS_WARN_IF(!browserDOMWin)) {
+      aResult = NS_ERROR_ABORT;
+      return IPC_OK();
+    }
+
+    bool isPrivate = false;
+    if (thisTabParent) {
+      nsCOMPtr<nsILoadContext> loadContext = thisTabParent->GetLoadContext();
+      loadContext->GetUsePrivateBrowsing(&isPrivate);
+    }
+
+    nsCOMPtr<nsIOpenURIInFrameParams> params =
+      new nsOpenURIInFrameParams(aOpenerOriginAttributes);
+    params->SetReferrer(NS_ConvertUTF8toUTF16(aBaseURI));
+    params->SetIsPrivate(isPrivate);
+
+    nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner;
+    aResult = browserDOMWin->OpenURIInFrame(aURIToLoad, params, openLocation,
+                                            nsIBrowserDOMWindow::OPEN_NEW,
+                                            getter_AddRefs(frameLoaderOwner));
+    if (NS_SUCCEEDED(aResult) && frameLoaderOwner) {
+      RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
+      if (frameLoader) {
+        frameLoader->GetTabParent(getter_AddRefs(aNewTabParent));
+      }
+    } else {
+      *aWindowIsNew = false;
+    }
+
+    return IPC_OK();
+  }
+
+  nsCOMPtr<nsPIWindowWatcher> pwwatch =
+    do_GetService(NS_WINDOWWATCHER_CONTRACTID, &aResult);
+  if (NS_WARN_IF(NS_FAILED(aResult))) {
+    return IPC_OK();
+  }
+
+  if (aSetOpener && thisTabParent) {
+    aResult = pwwatch->OpenWindowWithTabParent(thisTabParent, aFeatures,
+                                               aCalledFromJS, aFullZoom,
+                                               getter_AddRefs(aNewTabParent));
+  } else {
+    aResult = pwwatch->OpenWindowWithoutParent(getter_AddRefs(aNewTabParent));
+  }
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 ContentParent::RecvCreateWindow(PBrowserParent* aThisTab,
                                 PBrowserParent* aNewTab,
                                 PRenderFrameParent* aRenderFrame,
                                 const uint32_t& aChromeFlags,
                                 const bool& aCalledFromJS,
                                 const bool& aPositionSpecified,
                                 const bool& aSizeSpecified,
                                 const nsCString& aFeatures,
@@ -4174,151 +4299,41 @@ ContentParent::RecvCreateWindow(PBrowser
                                 nsCString* aURLToLoad,
                                 TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                 uint64_t* aLayersId)
 {
   // We always expect to open a new window here. If we don't, it's an error.
   *aWindowIsNew = true;
   *aResult = NS_OK;
 
-  // The content process should never be in charge of computing whether or
-  // not a window should be private or remote - the parent will do that.
-  const uint32_t badFlags =
-        nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW
-      | nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW
-      | nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME
-      | nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
-  if (!!(aChromeFlags & badFlags)) {
-      return IPC_FAIL_NO_REASON(this);
-  }
-
-  TabParent* thisTabParent = nullptr;
-  if (aThisTab) {
-    thisTabParent = TabParent::GetFrom(aThisTab);
-  }
-
-  if (NS_WARN_IF(thisTabParent && thisTabParent->IsMozBrowser())) {
-    return IPC_FAIL_NO_REASON(this);
-  }
-
   TabParent* newTab = TabParent::GetFrom(aNewTab);
   MOZ_ASSERT(newTab);
 
   auto destroyNewTabOnError = MakeScopeExit([&] {
     if (!*aWindowIsNew || NS_FAILED(*aResult)) {
       if (newTab) {
         newTab->Destroy();
       }
     }
   });
 
   // Content has requested that we open this new content window, so
   // we must have an opener.
   newTab->SetHasContentOpener(true);
 
-  nsCOMPtr<nsIContent> frame;
-  if (thisTabParent) {
-    frame = do_QueryInterface(thisTabParent->GetOwnerElement());
-  }
-
-  nsCOMPtr<nsPIDOMWindowOuter> parent;
-  if (frame) {
-    parent = frame->OwnerDoc()->GetWindow();
-
-    // If our chrome window is in the process of closing, don't try to open a
-    // new tab in it.
-    if (parent && parent->Closed()) {
-      parent = nullptr;
-    }
-  }
-
-  nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
-  if (thisTabParent) {
-    browserDOMWin = thisTabParent->GetBrowserDOMWindow();
-  }
-
-  // If we haven't found a chrome window to open in, just use the most recently
-  // opened one.
-  if (!parent) {
-    parent = nsContentUtils::GetMostRecentNonPBWindow();
-    if (NS_WARN_IF(!parent)) {
-      *aResult = NS_ERROR_FAILURE;
-      return IPC_OK();
-    }
-
-    nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(parent);
-    if (rootChromeWin) {
-      rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
-    }
-  }
-
-  int32_t openLocation =
-    nsWindowWatcher::GetWindowOpenLocation(parent, aChromeFlags, aCalledFromJS,
-                                           aPositionSpecified, aSizeSpecified);
-
-  MOZ_ASSERT(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
-             openLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);
-
-  // Opening new tabs is the easy case...
-  if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
-    if (NS_WARN_IF(!browserDOMWin)) {
-      *aResult = NS_ERROR_ABORT;
-      return IPC_OK();
-    }
-
-    bool isPrivate = false;
-    if (thisTabParent) {
-      nsCOMPtr<nsILoadContext> loadContext = thisTabParent->GetLoadContext();
-      loadContext->GetUsePrivateBrowsing(&isPrivate);
-    }
-
-    nsCOMPtr<nsIOpenURIInFrameParams> params =
-      new nsOpenURIInFrameParams(aOpenerOriginAttributes);
-    params->SetReferrer(NS_ConvertUTF8toUTF16(aBaseURI));
-    params->SetIsPrivate(isPrivate);
-
-    TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);
-
-    nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner;
-    browserDOMWin->OpenURIInFrame(nullptr, params,
-                                  openLocation,
-                                  nsIBrowserDOMWindow::OPEN_NEW,
-                                  getter_AddRefs(frameLoaderOwner));
-    if (!frameLoaderOwner) {
-      *aWindowIsNew = false;
-    }
-
-    newTab->SwapFrameScriptsFrom(*aFrameScripts);
-
-    RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame);
-    if (!newTab->SetRenderFrame(rfp) ||
-        !newTab->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) {
-      *aResult = NS_ERROR_FAILURE;
-    }
-
-    return IPC_OK();
-  }
-
   TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);
 
-  nsCOMPtr<nsPIWindowWatcher> pwwatch =
-    do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult);
-
-  if (NS_WARN_IF(NS_FAILED(*aResult))) {
-    return IPC_OK();
-  }
-
   nsCOMPtr<nsITabParent> newRemoteTab;
-  if (!thisTabParent) {
-    // Because we weren't passed an opener tab, the content process has asked us
-    // to open a new window that is unrelated to a pre-existing tab.
-    *aResult = pwwatch->OpenWindowWithoutParent(getter_AddRefs(newRemoteTab));
-  } else {
-    *aResult = pwwatch->OpenWindowWithTabParent(thisTabParent, aFeatures, aCalledFromJS,
-                                                aFullZoom, getter_AddRefs(newRemoteTab));
+  mozilla::ipc::IPCResult ipcResult =
+    CommonCreateWindow(aThisTab, /* aSetOpener = */ true, aChromeFlags,
+                       aCalledFromJS, aPositionSpecified, aSizeSpecified,
+                       nullptr, aFeatures, aBaseURI, aOpenerOriginAttributes,
+                       aFullZoom, *aResult, newRemoteTab, aWindowIsNew);
+  if (!ipcResult) {
+    return ipcResult;
   }
 
   if (NS_WARN_IF(NS_FAILED(*aResult))) {
     return IPC_OK();
   }
 
   MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab);
 
@@ -4329,16 +4344,49 @@ ContentParent::RecvCreateWindow(PBrowser
       !newTab->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) {
     *aResult = NS_ERROR_FAILURE;
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+ContentParent::RecvCreateWindowInDifferentProcess(
+  PBrowserParent* aThisTab,
+  const uint32_t& aChromeFlags,
+  const bool& aCalledFromJS,
+  const bool& aPositionSpecified,
+  const bool& aSizeSpecified,
+  const URIParams& aURIToLoad,
+  const nsCString& aFeatures,
+  const nsCString& aBaseURI,
+  const DocShellOriginAttributes& aOpenerOriginAttributes,
+  const float& aFullZoom)
+{
+  nsCOMPtr<nsITabParent> newRemoteTab;
+  bool windowIsNew;
+  nsCOMPtr<nsIURI> uriToLoad = DeserializeURI(aURIToLoad);
+  nsresult rv;
+  mozilla::ipc::IPCResult ipcResult =
+    CommonCreateWindow(aThisTab, /* aSetOpener = */ false, aChromeFlags,
+                       aCalledFromJS, aPositionSpecified, aSizeSpecified,
+                       uriToLoad, aFeatures, aBaseURI, aOpenerOriginAttributes,
+                       aFullZoom, rv, newRemoteTab, &windowIsNew);
+  if (!ipcResult) {
+    return ipcResult;
+  }
+
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Call to CommonCreateWindow failed.");
+  }
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 ContentParent::RecvProfile(const nsCString& aProfile)
 {
 #ifdef MOZ_ENABLE_PROFILER_SPS
   if (NS_WARN_IF(!mGatherer)) {
     return IPC_OK();
   }
   mProfile = aProfile;
   mGatherer->GatheredOOPProfile();
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -30,16 +30,17 @@
 #include "PermissionMessageUtils.h"
 #include "DriverCrashGuard.h"
 
 #define CHILD_PROCESS_SHUTDOWN_MESSAGE NS_LITERAL_STRING("child-process-shutdown")
 
 class nsConsoleService;
 class nsICycleCollectorLogSink;
 class nsIDumpGCAndCCLogsCallback;
+class nsITabParent;
 class nsITimer;
 class ParentIdleListener;
 class nsIWidget;
 
 namespace mozilla {
 class PRemoteSpellcheckEngineParent;
 #ifdef MOZ_ENABLE_PROFILER_SPS
 class ProfileGatherer;
@@ -478,33 +479,46 @@ public:
 
   virtual bool
   DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) override;
 
   virtual bool HandleWindowsMessages(const Message& aMsg) const override;
 
   void ForkNewProcess(bool aBlocking);
 
-  virtual mozilla::ipc::IPCResult RecvCreateWindow(PBrowserParent* aThisTabParent,
-                                                   PBrowserParent* aOpener,
-                                                   layout::PRenderFrameParent* aRenderFrame,
-                                                   const uint32_t& aChromeFlags,
-                                                   const bool& aCalledFromJS,
-                                                   const bool& aPositionSpecified,
-                                                   const bool& aSizeSpecified,
-                                                   const nsCString& aFeatures,
-                                                   const nsCString& aBaseURI,
-                                                   const DocShellOriginAttributes& aOpenerOriginAttributes,
-                                                   const float& aFullZoom,
-                                                   nsresult* aResult,
-                                                   bool* aWindowIsNew,
-                                                   InfallibleTArray<FrameScriptInfo>* aFrameScripts,
-                                                   nsCString* aURLToLoad,
-                                                   layers::TextureFactoryIdentifier* aTextureFactoryIdentifier,
-                                                   uint64_t* aLayersId) override;
+  virtual mozilla::ipc::IPCResult
+  RecvCreateWindow(PBrowserParent* aThisTabParent,
+                   PBrowserParent* aNewTab,
+                   layout::PRenderFrameParent* aRenderFrame,
+                   const uint32_t& aChromeFlags,
+                   const bool& aCalledFromJS,
+                   const bool& aPositionSpecified,
+                   const bool& aSizeSpecified,
+                   const nsCString& aFeatures,
+                   const nsCString& aBaseURI,
+                   const DocShellOriginAttributes& aOpenerOriginAttributes,
+                   const float& aFullZoom,
+                   nsresult* aResult,
+                   bool* aWindowIsNew,
+                   InfallibleTArray<FrameScriptInfo>* aFrameScripts,
+                   nsCString* aURLToLoad,
+                   layers::TextureFactoryIdentifier* aTextureFactoryIdentifier,
+                   uint64_t* aLayersId) override;
+
+  virtual mozilla::ipc::IPCResult RecvCreateWindowInDifferentProcess(
+    PBrowserParent* aThisTab,
+    const uint32_t& aChromeFlags,
+    const bool& aCalledFromJS,
+    const bool& aPositionSpecified,
+    const bool& aSizeSpecified,
+    const URIParams& aURIToLoad,
+    const nsCString& aFeatures,
+    const nsCString& aBaseURI,
+    const DocShellOriginAttributes& aOpenerOriginAttributes,
+    const float& aFullZoom) override;
 
   static bool AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId);
 
   static void
   BroadcastBlobURLRegistration(const nsACString& aURI,
                                BlobImpl* aBlobImpl,
                                nsIPrincipal* aPrincipal,
                                ContentParent* aIgnoreThisCP = nullptr);
@@ -578,16 +592,32 @@ private:
       PBrowserParent* actor,
       const TabId& aTabId,
       const IPCTabContext& context,
       const uint32_t& chromeFlags,
       const ContentParentId& aCpId,
       const bool& aIsForBrowser) override;
   using PContentParent::SendPTestShellConstructor;
 
+  mozilla::ipc::IPCResult
+  CommonCreateWindow(PBrowserParent* aThisTab,
+                     bool aSetOpener,
+                     const uint32_t& aChromeFlags,
+                     const bool& aCalledFromJS,
+                     const bool& aPositionSpecified,
+                     const bool& aSizeSpecified,
+                     nsIURI* aURIToLoad,
+                     const nsCString& aFeatures,
+                     const nsCString& aBaseURI,
+                     const DocShellOriginAttributes& aOpenerOriginAttributes,
+                     const float& aFullZoom,
+                     nsresult& aResult,
+                     nsCOMPtr<nsITabParent>& aNewTabParent,
+                     bool* aWindowIsNew);
+
   FORWARD_SHMEM_ALLOCATOR_TO(PContentParent)
 
   ContentParent(ContentParent* aOpener,
                 const nsAString& aRemoteType);
 
   // The common initialization for the constructors.
   void InitializeMembers();
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1076,16 +1076,28 @@ parent:
                       float aFullZoom)
       returns (nsresult rv,
                bool windowOpened,
                FrameScriptInfo[] frameScripts,
                nsCString urlToLoad,
                TextureFactoryIdentifier textureFactoryIdentifier,
                uint64_t layersId);
 
+    async CreateWindowInDifferentProcess(
+      PBrowser aThisTab,
+      uint32_t aChromeFlags,
+      bool aCalledFromJS,
+      bool aPositionSpecified,
+      bool aSizeSpecified,
+      URIParams aURIToLoad,
+      nsCString aFeatures,
+      nsCString aBaseURI,
+      DocShellOriginAttributes aOpenerOriginAttributes,
+      float aFullZoom);
+
     sync GetDeviceStorageLocation(nsString type)
         returns (nsString path);
 
     sync GetDeviceStorageLocations()
 	returns (DeviceStorageLocationInfo info);
 
     sync GetAndroidSystemInfo()
         returns (AndroidSystemInfo info);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -215,17 +215,17 @@ TabChildBase::GetPresShell() const
 }
 
 void
 TabChildBase::DispatchMessageManagerMessage(const nsAString& aMessageName,
                                             const nsAString& aJSONData)
 {
     AutoSafeJSContext cx;
     JS::Rooted<JS::Value> json(cx, JS::NullValue());
-    StructuredCloneData data;
+    dom::ipc::StructuredCloneData data;
     if (JS_ParseJSON(cx,
                       static_cast<const char16_t*>(aJSONData.BeginReading()),
                       aJSONData.Length(),
                       &json)) {
         ErrorResult rv;
         data.Write(cx, json, rv);
         if (NS_WARN_IF(rv.Failed())) {
             rv.SuppressException();
--- a/embedding/browser/nsIWebBrowserChrome3.idl
+++ b/embedding/browser/nsIWebBrowserChrome3.idl
@@ -43,16 +43,18 @@ interface nsIWebBrowserChrome3 : nsIWebB
    *        The URI being loaded.
    * @param aReferrer
    *        The referrer of the load.
    */
   bool shouldLoadURI(in nsIDocShell    aDocShell,
                      in nsIURI         aURI,
                      in nsIURI         aReferrer);
 
+  bool shouldLoadURIInThisProcess(in nsIURI aURI);
+
   /**
    * Attempts to load the currently loaded page into a fresh process to increase
    * available memory.
    *
    * @param aDocShell
    *        The docshell performing the load.
    */
   bool reloadInFreshProcess(in nsIDocShell aDocShell,
--- a/xpfe/appshell/nsContentTreeOwner.cpp
+++ b/xpfe/appshell/nsContentTreeOwner.cpp
@@ -399,16 +399,24 @@ NS_IMETHODIMP nsContentTreeOwner::Should
 
   if (xulBrowserWindow)
     return xulBrowserWindow->ShouldLoadURI(aDocShell, aURI, aReferrer, _retval);
 
   *_retval = true;
   return NS_OK;
 }
 
+NS_IMETHODIMP nsContentTreeOwner::ShouldLoadURIInThisProcess(nsIURI* aURI,
+                                                             bool* aRetVal)
+{
+  MOZ_ASSERT_UNREACHABLE("Should only be called in child process.");
+  *aRetVal = true;
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsContentTreeOwner::ReloadInFreshProcess(nsIDocShell* aDocShell,
                                                        nsIURI* aURI,
                                                        nsIURI* aReferrer,
                                                        bool* aRetVal)
 {
   NS_WARNING("Cannot reload in fresh process from a nsContentTreeOwner!");
   *aRetVal = false;
   return NS_OK;