Bug 989501 - Part 2: When content in an e10s tab requests a new window be opened, forward that call to the TabParent, and have the TabParent pass itself in when creating the new window. r=smaug.
authorMike Conley <mconley@mozilla.com>
Fri, 20 Jun 2014 14:07:47 -0400
changeset 189993 68ef4eeae5de700d0596552fe72ecfdc29454f59
parent 189992 42245901f967985b5aec86b30a0079a0970c9e05
child 189994 2be95d9f6b6986e695b0fc4b9f733a3fb4bceded
push id8288
push userryanvm@gmail.com
push dateMon, 23 Jun 2014 14:59:00 +0000
treeherderb2g-inbound@c65bf5a0595c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs989501
milestone33.0a1
Bug 989501 - Part 2: When content in an e10s tab requests a new window be opened, forward that call to the TabParent, and have the TabParent pass itself in when creating the new window. r=smaug.
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
embedding/components/windowwatcher/src/moz.build
embedding/components/windowwatcher/src/nsWindowWatcher.cpp
embedding/components/windowwatcher/src/nsWindowWatcher.h
xpfe/appshell/src/nsAppShellService.cpp
xpfe/appshell/src/nsContentTreeOwner.cpp
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -89,17 +89,25 @@ parent:
     /**
      * When child sends this message, parent should move focus to
      * the next or previous focusable element.
      */
     MoveFocus(bool forward);
 
     Event(RemoteDOMEvent aEvent);
 
-    intr CreateWindow() returns (PBrowser window);
+    intr CreateWindow(uint32_t aChromeFlags,
+                      bool aCalledFromJS,
+                      bool aPositionSpecified,
+                      bool aSizeSpecified,
+                      nsString aURI,
+                      nsString aName,
+                      nsString aFeatures,
+                      nsString aBaseURI)
+      returns (bool windowIsNew, PBrowser window);
 
     sync SyncMessage(nsString aMessage, ClonedMessageData aData,
                      CpowEntry[] aCpows, Principal aPrincipal)
       returns (nsString[] retval);
 
     rpc RpcMessage(nsString aMessage, ClonedMessageData aData,
                    CpowEntry[] aCpows, Principal aPrincipal)
       returns (nsString[] retval);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -37,19 +37,21 @@
 #include "nsEmbedCID.h"
 #include <algorithm>
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 #include "nsFilePickerProxy.h"
 #include "mozilla/dom/Element.h"
 #include "nsIBaseWindow.h"
+#include "nsIBrowserDOMWindow.h"
 #include "nsICachedFileDescriptorListener.h"
 #include "nsIDocumentInlines.h"
 #include "nsIDocShellTreeOwner.h"
+#include "nsIDOMChromeWindow.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIDocShell.h"
 #include "nsIURI.h"
 #include "nsIURIFixup.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIWebBrowser.h"
@@ -59,16 +61,17 @@
 #include "nsIXULRuntime.h"
 #include "nsInterfaceHashtable.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsLayoutUtils.h"
 #include "nsPrintfCString.h"
 #include "nsThreadUtils.h"
 #include "nsWeakReference.h"
+#include "nsWindowWatcher.h"
 #include "PermissionMessageUtils.h"
 #include "PCOMContentPermissionRequestChild.h"
 #include "PuppetWidget.h"
 #include "StructuredCloneUtils.h"
 #include "nsViewportInfo.h"
 #include "JavaScriptChild.h"
 #include "nsILoadContext.h"
 #include "ipc/nsGUIEventIPC.h"
@@ -1225,23 +1228,78 @@ TabChild::ProvideWindow(nsIDOMWindow* aP
 
       // Note that BrowserFrameProvideWindow 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.
       return BrowserFrameProvideWindow(aParent, aURI, aName, aFeatures,
                                        aWindowIsNew, aReturn);
     }
 
-    // Otherwise, create a new top-level window.
+    int32_t openLocation =
+      nsWindowWatcher::GetWindowOpenLocation(aParent, aChromeFlags, aCalledFromJS,
+                                             aPositionSpecified, aSizeSpecified);
+
+    // If it turns out we're opening in the current browser, just hand over the
+    // current browser's docshell.
+    if (openLocation == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
+      nsCOMPtr<nsIWebBrowser> browser = do_GetInterface(WebNavigation());
+      *aWindowIsNew = false;
+      return browser->GetContentDOMWindow(aReturn);
+    }
+
+    // Otherwise, we're opening a new tab or a new window. We have to contact
+    // TabParent in order to do either.
+
     PBrowserChild* newChild;
-    if (!CallCreateWindow(&newChild)) {
+
+    nsAutoCString uriString;
+    if (aURI) {
+      aURI->GetSpec(uriString);
+    }
+
+    nsCOMPtr<nsIDOMDocument> domDoc;
+    aParent->GetDocument(getter_AddRefs(domDoc));
+    if (!domDoc) {
+      NS_ERROR("Could retrieve document from nsIBaseWindow");
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIDocument> doc;
+    doc = do_QueryInterface(domDoc);
+    if (!doc) {
+      NS_ERROR("Document from nsIBaseWindow didn't QI to nsIDocument");
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
+    if (!baseURI) {
+      NS_ERROR("nsIDocument didn't return a base URI");
+      return NS_ERROR_FAILURE;
+    }
+
+    nsAutoCString baseURIString;
+    baseURI->GetSpec(baseURIString);
+
+    nsAutoString nameString;
+    nameString.Assign(aName);
+    nsAutoCString features;
+
+    // We can assume that if content is requesting to open a window from a remote
+    // tab, then we want to enforce that the new window is also a remote tab.
+    features.Assign(aFeatures);
+    features.Append(",remote");
+
+    if (!CallCreateWindow(aChromeFlags, aCalledFromJS, aPositionSpecified,
+                          aSizeSpecified, NS_ConvertUTF8toUTF16(uriString),
+                          nameString, NS_ConvertUTF8toUTF16(features),
+                          NS_ConvertUTF8toUTF16(baseURIString),
+                          aWindowIsNew, &newChild)) {
         return NS_ERROR_NOT_AVAILABLE;
     }
 
-    *aWindowIsNew = true;
     nsCOMPtr<nsIDOMWindow> win =
         do_GetInterface(static_cast<TabChild*>(newChild)->WebNavigation());
     win.forget(aReturn);
     return NS_OK;
 }
 
 nsresult
 TabChild::BrowserFrameProvideWindow(nsIDOMWindow* aOpener,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -39,25 +39,28 @@
 #include "nsIDOMElement.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPromptFactory.h"
 #include "nsIURI.h"
 #include "nsIWebBrowserChrome.h"
+#include "nsIWindowCreator2.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsIXULWindow.h"
 #include "nsViewManager.h"
 #include "nsIWidget.h"
 #include "nsIWindowWatcher.h"
 #include "nsPIDOMWindow.h"
+#include "nsPIWindowWatcher.h"
 #include "nsPrintfCString.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
+#include "nsWindowWatcher.h"
 #include "private/pprio.h"
 #include "PermissionMessageUtils.h"
 #include "StructuredCloneUtils.h"
 #include "ColorPickerParent.h"
 #include "JavaScriptParent.h"
 #include "FilePickerParent.h"
 #include "TabChild.h"
 #include "LoadContext.h"
@@ -383,45 +386,105 @@ TabParent::RecvEvent(const RemoteDOMEven
   event->SetOwner(target);
 
   bool dummy;
   target->DispatchEvent(event, &dummy);
   return true;
 }
 
 bool
-TabParent::AnswerCreateWindow(PBrowserParent** retval)
+TabParent::AnswerCreateWindow(const uint32_t& aChromeFlags,
+                              const bool& aCalledFromJS,
+                              const bool& aPositionSpecified,
+                              const bool& aSizeSpecified,
+                              const nsString& aURI,
+                              const nsString& aName,
+                              const nsString& aFeatures,
+                              const nsString& aBaseURI,
+                              bool* aWindowIsNew,
+                              PBrowserParent** aRetVal)
 {
-    if (!mBrowserDOMWindow) {
-        return false;
-    }
+  if (IsBrowserOrApp()) {
+    return false;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsPIWindowWatcher> pwwatch =
+    do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, false);
 
-    // Only non-app, non-browser processes may call CreateWindow.
-    if (IsBrowserOrApp()) {
-        return false;
-    }
+  nsCOMPtr<nsIContent> frame(do_QueryInterface(mFrameElement));
+  NS_ENSURE_TRUE(frame, false);
+
+  nsCOMPtr<nsIDOMWindow> parent = do_QueryInterface(frame->OwnerDoc()->GetWindow());
+  NS_ENSURE_TRUE(parent, false);
+
+  int32_t openLocation =
+    nsWindowWatcher::GetWindowOpenLocation(parent, aChromeFlags, aCalledFromJS,
+                                           aPositionSpecified, aSizeSpecified);
 
-    // 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.
+  MOZ_ASSERT(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
+             openLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);
+
+  *aWindowIsNew = true;
+
+  // Opening new tabs is the easy case...
+  if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
+    NS_ENSURE_TRUE(mBrowserDOMWindow, false);
+
     nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner;
     mBrowserDOMWindow->OpenURIInFrame(nullptr, nullptr,
                                       nsIBrowserDOMWindow::OPEN_NEWTAB,
                                       nsIBrowserDOMWindow::OPEN_NEW,
                                       getter_AddRefs(frameLoaderOwner));
-    if (!frameLoaderOwner) {
-        return false;
-    }
+    NS_ENSURE_TRUE(frameLoaderOwner, false);
 
     nsRefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
-    if (!frameLoader) {
-        return false;
-    }
+    NS_ENSURE_TRUE(frameLoader, false);
+
+    *aRetVal = frameLoader->GetRemoteBrowser();
+    return true;
+  }
+
+  // WindowWatcher is going to expect a valid URI to open a window
+  // to. If it can't find one, it's going to attempt to figure one
+  // out on its own, which is problematic because it can't access
+  // the document for the remote browser we're opening. Luckily,
+  // TabChild has sent us a baseURI with which we can ensure that
+  // the URI we pass to WindowWatcher is valid.
+  nsCOMPtr<nsIURI> baseURI;
+  rv = NS_NewURI(getter_AddRefs(baseURI), aBaseURI);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  nsCOMPtr<nsIURI> finalURI;
+  rv = NS_NewURI(getter_AddRefs(finalURI), NS_ConvertUTF16toUTF8(aURI).get(), baseURI);
+  NS_ENSURE_SUCCESS(rv, false);
 
-    *retval = frameLoader->GetRemoteBrowser();
-    return true;
+  nsAutoCString finalURIString;
+  finalURI->GetSpec(finalURIString);
+
+  nsCOMPtr<nsIDOMWindow> window;
+
+  rv = pwwatch->OpenWindow2(parent, finalURIString.get(),
+                            NS_ConvertUTF16toUTF8(aName).get(),
+                            NS_ConvertUTF16toUTF8(aFeatures).get(), aCalledFromJS,
+                            false, false, this, nullptr, getter_AddRefs(window));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(window);
+  NS_ENSURE_TRUE(pwindow, false);
+
+  nsRefPtr<nsIDocShell> newDocShell = pwindow->GetDocShell();
+  NS_ENSURE_TRUE(newDocShell, false);
+
+  nsCOMPtr<nsITabParent> newRemoteTab = newDocShell->GetOpenedRemote();
+  NS_ENSURE_TRUE(newRemoteTab, false);
+
+  *aRetVal = static_cast<TabParent*>(newRemoteTab.get());
+  return true;
 }
 
 void
 TabParent::LoadURL(nsIURI* aURI)
 {
     MOZ_ASSERT(aURI);
 
     if (mIsDestroyed) {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -122,17 +122,26 @@ public:
                                              TextureFactoryIdentifier* aFactoryIdentifier,
                                              uint64_t* aLayersId,
                                              bool* aSuccess) MOZ_OVERRIDE;
     virtual bool RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
                                             const nsString& aURL,
                                             const nsString& aName,
                                             const nsString& aFeatures,
                                             bool* aOutWindowOpened) MOZ_OVERRIDE;
-    virtual bool AnswerCreateWindow(PBrowserParent** retval) MOZ_OVERRIDE;
+    virtual bool AnswerCreateWindow(const uint32_t& aChromeFlags,
+                                    const bool& aCalledFromJS,
+                                    const bool& aPositionSpecified,
+                                    const bool& aSizeSpecified,
+                                    const nsString& aURI,
+                                    const nsString& aName,
+                                    const nsString& aFeatures,
+                                    const nsString& aBaseURI,
+                                    bool* aWindowIsNew,
+                                    PBrowserParent** aRetVal) MOZ_OVERRIDE;
     virtual bool RecvSyncMessage(const nsString& aMessage,
                                  const ClonedMessageData& aData,
                                  const InfallibleTArray<CpowEntry>& aCpows,
                                  const IPC::Principal& aPrincipal,
                                  InfallibleTArray<nsString>* aJSONRetVal) MOZ_OVERRIDE;
     virtual bool AnswerRpcMessage(const nsString& aMessage,
                                   const ClonedMessageData& aData,
                                   const InfallibleTArray<CpowEntry>& aCpows,
--- a/embedding/components/windowwatcher/src/moz.build
+++ b/embedding/components/windowwatcher/src/moz.build
@@ -4,16 +4,20 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
     'nsAutoWindowStateHelper.cpp',
     'nsWindowWatcher.cpp',
 ]
 
+EXPORTS += [
+    'nsWindowWatcher.h',
+]
+
 if CONFIG['MOZ_XUL']:
     UNIFIED_SOURCES += [
         'nsDialogParamBlock.cpp',
     ]
 
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'embedcomponents'
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -10,16 +10,17 @@
 #include "nsAutoWindowStateHelper.h"
 
 #include "nsCRT.h"
 #include "nsNetUtil.h"
 #include "nsJSUtils.h"
 #include "plstr.h"
 
 #include "nsIBaseWindow.h"
+#include "nsIBrowserDOMWindow.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellLoadInfo.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocumentLoader.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMWindow.h"
@@ -433,25 +434,29 @@ nsWindowWatcher::OpenWindowInternal(nsID
   nsresult                        rv = NS_OK;
   bool                            nameSpecified,
                                   featuresSpecified,
                                   isNewToplevelWindow = false,
                                   windowIsNew = false,
                                   windowNeedsName = false,
                                   windowIsModal = false,
                                   uriToLoadIsChrome = false,
-                                  windowIsModalContentDialog = false;
+                                  windowIsModalContentDialog = false,
+  // Opening tabs are only ever passed to OpenWindowInternal if we're opening
+  // a window from a remote tab.
+                                  openedFromRemoteTab = !!aOpeningTab;
   uint32_t                        chromeFlags;
   nsAutoString                    name;             // string version of aName
   nsAutoCString                   features;         // string version of aFeatures
   nsCOMPtr<nsIURI>                uriToLoad;        // from aUrl, if any
   nsCOMPtr<nsIDocShellTreeOwner>  parentTreeOwner;  // from the parent window, if any
   nsCOMPtr<nsIDocShellTreeItem>   newDocShellItem;  // from the new window
   nsCxPusher                      callerContextGuard;
 
+  MOZ_ASSERT_IF(openedFromRemoteTab, XRE_GetProcessType() == GeckoProcessType_Default);
   NS_ENSURE_ARG_POINTER(_retval);
   *_retval = 0;
 
   if (!nsContentUtils::IsSafeToRunScript()) {
     return NS_ERROR_FAILURE;
   }
 
   GetWindowTreeOwner(aParent, getter_AddRefs(parentTreeOwner));
@@ -471,19 +476,27 @@ nsWindowWatcher::OpenWindowInternal(nsID
 
   featuresSpecified = false;
   if (aFeatures) {
     features.Assign(aFeatures);
     featuresSpecified = true;
     features.StripWhitespace();
   }
 
-  // try to find an extant window with the given name
-  nsCOMPtr<nsIDOMWindow> foundWindow = SafeGetWindowByName(name, aParent);
-  GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem));
+  // We only want to check for existing named windows if:
+  // a) We're the child process
+  // b) We're the parent process, and aOpeningTab wasn't passed
+  //    in.
+  // This is because when using child processes, the parent process shouldn't
+  // know or care about names - unless we're opening named windows from chrome.
+  if (!aOpeningTab) {
+    // try to find an extant window with the given name
+    nsCOMPtr<nsIDOMWindow> foundWindow = SafeGetWindowByName(name, aParent);
+    GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem));
+  }
 
   // Do sandbox checks here, instead of waiting until nsIDocShell::LoadURI.
   // The state of the window can change before this call and if we are blocked
   // because of sandboxing, we wouldn't want that to happen.
   nsCOMPtr<nsPIDOMWindow> parentWindow = do_QueryInterface(aParent);
   nsCOMPtr<nsIDocShell> parentDocShell;
   if (parentWindow) {
     parentDocShell = parentWindow->GetDocShell();
@@ -499,26 +512,31 @@ nsWindowWatcher::OpenWindowInternal(nsID
 
   // If no parent, consider it chrome.
   bool hasChromeParent = true;
   if (aParent) {
     // Check if the parent document has chrome privileges.
     nsCOMPtr<nsIDOMDocument> domdoc;
     aParent->GetDocument(getter_AddRefs(domdoc));
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
-    hasChromeParent = doc && nsContentUtils::IsChromeDoc(doc);
+    hasChromeParent = doc && nsContentUtils::IsChromeDoc(doc) && !openedFromRemoteTab;
   }
 
   // Make sure we call CalculateChromeFlags() *before* we push the
   // callee context onto the context stack so that
   // CalculateChromeFlags() sees the actual caller when doing its
   // security checks.
   chromeFlags = CalculateChromeFlags(aParent, features.get(), featuresSpecified,
                                      aDialog, uriToLoadIsChrome,
-                                     hasChromeParent);
+                                     hasChromeParent, openedFromRemoteTab);
+
+  // If we are opening a window from a remote browser, the resulting window
+  // should also be remote.
+  MOZ_ASSERT_IF(openedFromRemoteTab,
+                chromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
 
   // If we're not called through our JS version of the API, and we got
   // our internal modal option, treat the window we're opening as a
   // modal content window (and set the modal chrome flag).
   if (!aCalledFromJS && argv &&
       WinHasOption(features.get(), "-moz-internal-modal", 0, nullptr)) {
     windowIsModalContentDialog = true;
 
@@ -532,17 +550,17 @@ nsWindowWatcher::OpenWindowInternal(nsID
   }
 
   SizeSpec sizeSpec;
   CalcSizeSpec(features.get(), sizeSpec);
 
   nsCOMPtr<nsIScriptSecurityManager>
     sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
 
-  bool isCallerChrome = nsContentUtils::IsCallerChrome();
+  bool isCallerChrome = nsContentUtils::IsCallerChrome() && !openedFromRemoteTab;
 
   JSContext *cx = GetJSContextFromWindow(aParent);
 
   bool windowTypeIsChrome = chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
   if (isCallerChrome && !hasChromeParent && !windowTypeIsChrome && cx) {
     // open() is called from chrome on a non-chrome window, push the context of the
     // callee onto the context stack to prevent the caller's priveleges from leaking
     // into code that runs while opening the new window.
@@ -934,17 +952,17 @@ nsWindowWatcher::OpenWindowInternal(nsID
       nsCOMPtr<nsIDOMStorage> storage;
       parentStorageManager->GetStorage(subjectPrincipal, isPrivateBrowsingWindow, getter_AddRefs(storage));
       if (storage)
         newStorageManager->CloneStorage(storage);
     }
   }
 
   if (isNewToplevelWindow)
-    SizeOpenedDocShellItem(newDocShellItem, aParent, sizeSpec);
+    SizeOpenedDocShellItem(newDocShellItem, aParent, isCallerChrome, sizeSpec);
 
   // XXXbz isn't windowIsModal always true when windowIsModalContentDialog?
   if (windowIsModal || windowIsModalContentDialog) {
     nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
     newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
     nsCOMPtr<nsIWebBrowserChrome> newChrome(do_GetInterface(newTreeOwner));
 
     // Throw an exception here if no web browser chrome is available,
@@ -1398,17 +1416,18 @@ nsWindowWatcher::URIfromURL(const char *
  * @return the chrome bitmask
  */
 // static
 uint32_t nsWindowWatcher::CalculateChromeFlags(nsIDOMWindow *aParent,
                                                const char *aFeatures,
                                                bool aFeaturesSpecified,
                                                bool aDialog,
                                                bool aChromeURL,
-                                               bool aHasChromeParent)
+                                               bool aHasChromeParent,
+                                               bool aOpenedFromRemoteTab)
 {
    if(!aFeaturesSpecified || !aFeatures) {
       if(aDialog)
          return nsIWebBrowserChrome::CHROME_ALL | 
                 nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | 
                 nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
       else
          return nsIWebBrowserChrome::CHROME_ALL;
@@ -1426,28 +1445,28 @@ uint32_t nsWindowWatcher::CalculateChrom
   bool presenceFlag = false;
 
   chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS;
   if (aDialog && WinHasOption(aFeatures, "all", 0, &presenceFlag))
     chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
 
   /* Next, allow explicitly named options to override the initial settings */
 
-  bool isCallerChrome = nsContentUtils::IsCallerChrome();
+  bool isCallerChrome = nsContentUtils::IsCallerChrome() && !aOpenedFromRemoteTab;
 
   // Determine whether the window is a private browsing window
   if (isCallerChrome) {
     chromeFlags |= WinHasOption(aFeatures, "private", 0, &presenceFlag) ?
       nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW : 0;
     chromeFlags |= WinHasOption(aFeatures, "non-private", 0, &presenceFlag) ?
       nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW : 0;
   }
 
   // Determine whether the window should have remote tabs.
-  if (isCallerChrome) {
+  if (isCallerChrome || aOpenedFromRemoteTab) {
     bool remote;
     if (Preferences::GetBool("browser.tabs.remote.autostart")) {
       remote = !WinHasOption(aFeatures, "non-remote", 0, &presenceFlag);
     } else {
       remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag);
     }
     if (remote) {
       chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
@@ -1872,16 +1891,17 @@ nsWindowWatcher::CalcSizeSpec(const char
 /* Size and position the new window according to aSizeSpec. This method
    is assumed to be called after the window has already been given
    a default position and size; thus its current position and size are
    accurate defaults. The new window is made visible at method end.
 */
 void
 nsWindowWatcher::SizeOpenedDocShellItem(nsIDocShellTreeItem *aDocShellItem,
                                         nsIDOMWindow *aParent,
+                                        bool aIsCallerChrome,
                                         const SizeSpec & aSizeSpec)
 {
   // position and size of window
   int32_t left = 0,
           top = 0,
           width = 100,
           height = 100;
   // difference between chrome and content size
@@ -1979,17 +1999,17 @@ nsWindowWatcher::SizeOpenedDocShellItem(
       height = NSToIntRound(aSizeSpec.mInnerHeight * openerZoom);
     }
   }
 
   bool positionSpecified = aSizeSpec.PositionSpecified();
 
   // Check security state for use in determing window dimensions
   bool enabled = false;
-  if (nsContentUtils::IsCallerChrome()) {
+  if (aIsCallerChrome) {
     // Only enable special priveleges for chrome when chrome calls
     // open() on a chrome window
     nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(aParent));
     enabled = !aParent || chromeWin;
   }
 
   if (!enabled) {
 
@@ -2097,16 +2117,85 @@ nsWindowWatcher::GetWindowTreeOwner(nsID
   *outTreeOwner = 0;
 
   nsCOMPtr<nsIDocShellTreeItem> treeItem;
   GetWindowTreeItem(inWindow, getter_AddRefs(treeItem));
   if (treeItem)
     treeItem->GetTreeOwner(outTreeOwner);
 }
 
+/* static */
+int32_t nsWindowWatcher::GetWindowOpenLocation(nsIDOMWindow *aParent,
+                                               uint32_t aChromeFlags,
+                                               bool aCalledFromJS,
+                                               bool aPositionSpecified,
+                                               bool aSizeSpecified)
+{
+  bool isFullScreen = false;
+  if (aParent) {
+    aParent->GetFullScreen(&isFullScreen);
+  }
+
+  // Where should we open this?
+  int32_t containerPref;
+  if (NS_FAILED(Preferences::GetInt("browser.link.open_newwindow",
+                                    &containerPref))) {
+    // We couldn't read the user preference, so fall back on the default.
+    return nsIBrowserDOMWindow::OPEN_NEWTAB;
+  }
+
+  bool isDisabledOpenNewWindow =
+    isFullScreen &&
+    Preferences::GetBool("browser.link.open_newwindow.disabled_in_fullscreen");
+
+  if (isDisabledOpenNewWindow && (containerPref == nsIBrowserDOMWindow::OPEN_NEWWINDOW)) {
+    containerPref = nsIBrowserDOMWindow::OPEN_NEWTAB;
+  }
+
+  if (containerPref != nsIBrowserDOMWindow::OPEN_NEWTAB &&
+      containerPref != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
+    // Just open a window normally
+    return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
+  }
+
+  if (aCalledFromJS) {
+    /* Now check our restriction pref.  The restriction pref is a power-user's
+       fine-tuning pref. values:
+       0: no restrictions - divert everything
+       1: don't divert window.open at all
+       2: don't divert window.open with features
+    */
+    int32_t restrictionPref =
+      Preferences::GetInt("browser.link.open_newwindow.restriction", 2);
+    if (restrictionPref < 0 || restrictionPref > 2) {
+      restrictionPref = 2; // Sane default behavior
+    }
+
+    if (isDisabledOpenNewWindow) {
+      // In browser fullscreen, the window should be opened
+      // in the current window with no features (see bug 803675)
+      restrictionPref = 0;
+    }
+
+    if (restrictionPref == 1) {
+      return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
+    }
+
+    if (restrictionPref == 2 &&
+        // Only continue if there are no size/position features and no special
+        // chrome flags.
+        (aChromeFlags != nsIWebBrowserChrome::CHROME_ALL ||
+         aPositionSpecified || aSizeSpecified)) {
+      return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
+    }
+  }
+
+  return containerPref;
+}
+
 JSContext *
 nsWindowWatcher::GetJSContextFromWindow(nsIDOMWindow *aWindow)
 {
   JSContext *cx = 0;
 
   if (aWindow) {
     nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aWindow));
     if (sgo) {
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.h
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.h
@@ -46,16 +46,22 @@ public:
   nsresult Init();
 
   NS_DECL_ISUPPORTS
 
   NS_DECL_NSIWINDOWWATCHER
   NS_DECL_NSPIWINDOWWATCHER
   NS_DECL_NSIPROMPTFACTORY
 
+  static int32_t    GetWindowOpenLocation(nsIDOMWindow *aParent,
+                                          uint32_t aChromeFlags,
+                                          bool aCalledFromJS,
+                                          bool aPositionSpecified,
+                                          bool aSizeSpecified);
+
 protected:
   friend class nsPromptService;
   bool AddEnumerator(nsWatcherWindowEnumerator* inEnumerator);
   bool RemoveEnumerator(nsWatcherWindowEnumerator* inEnumerator);
 
   nsWatcherWindowEntry *FindWindowEntry(nsIDOMWindow *aWindow);
   nsresult RemoveWindow(nsWatcherWindowEntry *inInfo);
 
@@ -87,27 +93,29 @@ protected:
                                nsIDOMWindow *aParent,
                                nsIURI **aURI);
   
   static uint32_t   CalculateChromeFlags(nsIDOMWindow *aParent,
                                          const char *aFeatures,
                                          bool aFeaturesSpecified,
                                          bool aDialog,
                                          bool aChromeURL,
-                                         bool aHasChromeParent);
+                                         bool aHasChromeParent,
+                                         bool aOpenedFromRemoteTab);
   static int32_t    WinHasOption(const char *aOptions, const char *aName,
                                  int32_t aDefault, bool *aPresenceFlag);
   /* Compute the right SizeSpec based on aFeatures */
   static void       CalcSizeSpec(const char* aFeatures, SizeSpec& aResult);
   static nsresult   ReadyOpenedDocShellItem(nsIDocShellTreeItem *aOpenedItem,
                                             nsIDOMWindow *aParent,
                                             bool aWindowIsNew,
                                             nsIDOMWindow **aOpenedWindow);
   static void       SizeOpenedDocShellItem(nsIDocShellTreeItem *aDocShellItem,
                                            nsIDOMWindow *aParent,
+                                           bool aIsCallerChrome,
                                            const SizeSpec & aSizeSpec);
   static void       GetWindowTreeItem(nsIDOMWindow *inWindow,
                                       nsIDocShellTreeItem **outTreeItem);
   static void       GetWindowTreeOwner(nsIDOMWindow *inWindow,
                                        nsIDocShellTreeOwner **outTreeOwner);
 
   nsTArray<nsWatcherWindowEnumerator*> mEnumeratorList;
   nsWatcherWindowEntry *mOldestWindow;
--- a/xpfe/appshell/src/nsAppShellService.cpp
+++ b/xpfe/appshell/src/nsAppShellService.cpp
@@ -601,37 +601,49 @@ nsAppShellService::JustCreateTopWindow(n
                                    aUrl, aInitialWidth, aInitialHeight,
                                    aIsHiddenWindow, aOpeningTab, widgetInitData);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Enforce the Private Browsing autoStart pref first.
   bool isPrivateBrowsingWindow =
     Preferences::GetBool("browser.privatebrowsing.autostart");
+  bool isUsingRemoteTabs =
+    Preferences::GetBool("browser.tabs.remote.autostart");
+
   if (aChromeMask & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW) {
     // Caller requested a private window
     isPrivateBrowsingWindow = true;
   }
-  if (!isPrivateBrowsingWindow) {
+  if (aChromeMask & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW) {
+    isUsingRemoteTabs = true;
+  }
+
+  nsCOMPtr<nsIDOMWindow> domWin = do_GetInterface(aParent);
+  nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(domWin);
+  nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(webNav);
+
+  if (!isPrivateBrowsingWindow && parentContext) {
     // Ensure that we propagate any existing private browsing status
     // from the parent, even if it will not actually be used
     // as a parent value.
-    nsCOMPtr<nsIDOMWindow> domWin = do_GetInterface(aParent);
-    nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(domWin);
-    nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(webNav);
-    if (parentContext) {
-      isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
-    }
+    isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
   }
+
+  if (!isUsingRemoteTabs && parentContext) {
+    isUsingRemoteTabs = parentContext->UseRemoteTabs();
+  }
+
   nsCOMPtr<nsIDOMWindow> newDomWin =
       do_GetInterface(NS_ISUPPORTS_CAST(nsIBaseWindow*, window));
   nsCOMPtr<nsIWebNavigation> newWebNav = do_GetInterface(newDomWin);
   nsCOMPtr<nsILoadContext> thisContext = do_GetInterface(newWebNav);
   if (thisContext) {
     thisContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
+    thisContext->SetRemoteTabs(isUsingRemoteTabs);
   }
 
   window.swap(*aResult); // transfer reference
   if (parent)
     parent->AddChildWindow(*aResult);
 
   if (center)
     rv = (*aResult)->Center(parent, parent ? false : true, false);
--- a/xpfe/appshell/src/nsContentTreeOwner.cpp
+++ b/xpfe/appshell/src/nsContentTreeOwner.cpp
@@ -29,16 +29,17 @@
 #include "nsIPrincipal.h"
 #include "nsIURIFixup.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIWebNavigation.h"
 #include "nsDocShellCID.h"
 #include "nsIExternalURLHandlerService.h"
 #include "nsIMIMEInfo.h"
 #include "nsIWidget.h"
+#include "nsWindowWatcher.h"
 #include "mozilla/BrowserElementParent.h"
 
 #include "nsIDOMDocument.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
 #include "nsIDocument.h"
 #if defined(XP_MACOSX)
 #include "nsThreadUtils.h"
@@ -864,98 +865,49 @@ nsContentTreeOwner::ProvideWindow(nsIDOM
           info->LaunchWithURI(aURI, nullptr);
           return NS_ERROR_ABORT;
         }
 
       }
     }
   }
 
-  // the parent window is fullscreen mode or not.
-  bool isFullScreen = false;
-  if (aParent) {
-    aParent->GetFullScreen(&isFullScreen);
-  }
+  int32_t openLocation =
+    nsWindowWatcher::GetWindowOpenLocation(aParent, aChromeFlags, aCalledFromJS,
+                                           aPositionSpecified, aSizeSpecified);
 
-  // Where should we open this?
-  int32_t containerPref;
-  if (NS_FAILED(Preferences::GetInt("browser.link.open_newwindow",
-                                    &containerPref))) {
-    return NS_OK;
-  }
-
-  bool isDisabledOpenNewWindow =
-    isFullScreen &&
-    Preferences::GetBool("browser.link.open_newwindow.disabled_in_fullscreen");
-
-  if (isDisabledOpenNewWindow && (containerPref == nsIBrowserDOMWindow::OPEN_NEWWINDOW)) {
-    containerPref = nsIBrowserDOMWindow::OPEN_NEWTAB;
-  }
-
-  if (containerPref != nsIBrowserDOMWindow::OPEN_NEWTAB &&
-      containerPref != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
+  if (openLocation != nsIBrowserDOMWindow::OPEN_NEWTAB &&
+      openLocation != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
     // Just open a window normally
     return NS_OK;
   }
 
-  if (aCalledFromJS) {
-    /* Now check our restriction pref.  The restriction pref is a power-user's
-       fine-tuning pref. values:     
-       0: no restrictions - divert everything
-       1: don't divert window.open at all
-       2: don't divert window.open with features
-    */
-    int32_t restrictionPref =
-      Preferences::GetInt("browser.link.open_newwindow.restriction", 2);
-    if (restrictionPref < 0 || restrictionPref > 2) {
-      restrictionPref = 2; // Sane default behavior
-    }
-
-    if (isDisabledOpenNewWindow) {
-      // In browser fullscreen, the window should be opened
-      // in the current window with no features (see bug 803675)
-      restrictionPref = 0;
-    }
-
-    if (restrictionPref == 1) {
-      return NS_OK;
-    }
-
-    if (restrictionPref == 2 &&
-        // Only continue if there are no size/position features and no special
-        // chrome flags.
-        (aChromeFlags != nsIWebBrowserChrome::CHROME_ALL ||
-         aPositionSpecified || aSizeSpecified)) {
-      return NS_OK;
-    }
-  }
-
   nsCOMPtr<nsIDOMWindow> domWin;
   mXULWindow->GetWindowDOMWindow(getter_AddRefs(domWin));
   nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(domWin);
   if (!chromeWin) {
     // Really odd... but whatever
     NS_WARNING("nsXULWindow's DOMWindow is not a chrome window");
     return NS_OK;
   }
-  
+
   nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
   chromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
   if (!browserDOMWin) {
     return NS_OK;
   }
 
-  *aWindowIsNew = (containerPref != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW);
+  *aWindowIsNew = (openLocation != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW);
 
   {
     dom::AutoNoJSAPI nojsapi;
 
     // 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.
-    return browserDOMWin->OpenURI(nullptr, aParent, containerPref,
+    return browserDOMWin->OpenURI(nullptr, aParent, openLocation,
                                   nsIBrowserDOMWindow::OPEN_NEW, aReturn);
   }
 }
 
 //*****************************************************************************
 // nsContentTreeOwner: Accessors
 //*****************************************************************************