Bug 1172870 - Part 2 - Enable ServiceWorkerClients::OpenWindow on e10s desktop. r=smaug
authorCatalin Badea <catalin.badea392@gmail.com>
Thu, 12 Nov 2015 04:02:34 +0200
changeset 305469 be8391c58a8307f2b15cfe0b12ee4ba4d9bfb27b
parent 305468 953d8110ed9d0aab346328ebcc4d9406394d8793
child 305470 70132ea00d708cd89e146bd8202beba2636f8a41
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1172870
milestone44.0a2
Bug 1172870 - Part 2 - Enable ServiceWorkerClients::OpenWindow on e10s desktop. r=smaug
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentProcessManager.cpp
dom/ipc/PContent.ipdl
dom/ipc/PTabContext.ipdlh
dom/ipc/TabContext.cpp
dom/ipc/nsIContentParent.cpp
dom/workers/ServiceWorkerClients.cpp
embedding/components/windowwatcher/nsWindowWatcher.cpp
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -31,16 +31,17 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/AutoTimelineMarker.h"
 #include "mozilla/Base64.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/LoadInfo.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
 #include "mozilla/dom/HTMLContentElement.h"
 #include "mozilla/dom/HTMLShadowElement.h"
 #include "mozilla/dom/ipc/BlobChild.h"
@@ -183,16 +184,17 @@
 #include "nsTextFragment.h"
 #include "nsTextNode.h"
 #include "nsThreadUtils.h"
 #include "nsUnicharUtilCIID.h"
 #include "nsUnicodeProperties.h"
 #include "nsViewManager.h"
 #include "nsViewportInfo.h"
 #include "nsWidgetsCID.h"
+#include "nsIWindowProvider.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsXULPopupManager.h"
 #include "xpcprivate.h" // nsXPConnect
 #include "HTMLSplitOnSpacesTokenizer.h"
 #include "nsContentTypeParser.h"
 #include "nsICookiePermission.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsICookieService.h"
@@ -5167,16 +5169,24 @@ nsContentUtils::RemoveScriptBlocker()
 #ifdef DEBUG
   AutoRestore<bool> removingScriptBlockers(sRemovingScriptBlockers);
   sRemovingScriptBlockers = true;
 #endif
   sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
 }
 
 /* static */
+nsIWindowProvider*
+nsContentUtils::GetWindowProviderForContentProcess()
+{
+  MOZ_ASSERT(XRE_IsContentProcess());
+  return ContentChild::GetSingleton();
+}
+
+/* static */
 void
 nsContentUtils::WarnScriptWasIgnored(nsIDocument* aDocument)
 {
   nsAutoString msg;
   if (aDocument) {
     nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
     if (uri) {
       nsCString spec;
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -97,16 +97,17 @@ class nsStringBuffer;
 class nsStringHashKey;
 class nsTextFragment;
 class nsView;
 class nsViewportInfo;
 class nsWrapperCache;
 class nsAttrValue;
 class nsITransferable;
 class nsPIWindowRoot;
+class nsIWindowProvider;
 
 struct JSPropertyDescriptor;
 struct JSRuntime;
 struct nsIntMargin;
 
 template<class E> class nsCOMArray;
 template<class K, class V> class nsDataHashtable;
 template<class K, class V> class nsRefPtrHashtable;
@@ -1632,16 +1633,21 @@ public:
    *
    * The only known case where this lies is mutation events. They run, and can
    * run anything else, when this function returns false, but this is ok.
    */
   static bool IsSafeToRunScript() {
     return sScriptBlockerCount == 0;
   }
 
+  // XXXcatalinb: workaround for weird include error when trying to reference
+  // ipdl types in WindowWatcher.
+  static nsIWindowProvider*
+  GetWindowProviderForContentProcess();
+
   /**
    * Call this function if !IsSafeToRunScript() and we fail to run the script
    * (rather than using AddScriptRunner as we usually do). |aDocument| is
    * optional as it is only used for showing the URL in the console.
    */
   static void WarnScriptWasIgnored(nsIDocument* aDocument);
 
   /**
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -770,44 +770,57 @@ ContentChild::ProvideWindowCommon(TabChi
                                   bool aPositionSpecified,
                                   bool aSizeSpecified,
                                   nsIURI* aURI,
                                   const nsAString& aName,
                                   const nsACString& aFeatures,
                                   bool* aWindowIsNew,
                                   nsIDOMWindow** aReturn)
 {
-    MOZ_ASSERT(aTabOpener);
     *aReturn = nullptr;
 
-    const TabId openerTabId = aTabOpener->GetTabId();
-
-    PopupIPCTabContext context;
-    context.opener() = openerTabId;
-    context.isBrowserElement() = aTabOpener->IsBrowserElement();
-
-    IPCTabContext ipcContext(context);
-
+    nsAutoPtr<IPCTabContext> ipcContext;
+    TabId openerTabId = TabId(0);
+
+    if (aTabOpener) {
+        PopupIPCTabContext context;
+        openerTabId = aTabOpener->GetTabId();
+        context.opener() = openerTabId;
+        context.isBrowserElement() = aTabOpener->IsBrowserElement();
+        ipcContext = new IPCTabContext(context);
+    } else {
+        // It's possible to not have a TabChild opener in the case
+        // of ServiceWorker::OpenWindow.
+        UnsafeIPCTabContext unsafeTabContext;
+        ipcContext = new IPCTabContext(unsafeTabContext);
+    }
+
+    MOZ_ASSERT(ipcContext);
     TabId tabId;
     SendAllocateTabId(openerTabId,
-                      ipcContext,
+                      *ipcContext,
                       GetID(),
                       &tabId);
 
+    TabContext newTabContext = aTabOpener ? *aTabOpener : TabContext();
     RefPtr<TabChild> newChild = new TabChild(this, tabId,
-                                             *aTabOpener, aChromeFlags);
+                                             newTabContext, aChromeFlags);
     if (NS_FAILED(newChild->Init())) {
         return NS_ERROR_ABORT;
     }
 
-    context.opener() = aTabOpener;
+    if (aTabOpener) {
+        MOZ_ASSERT(ipcContext->type() == IPCTabContext::TPopupIPCTabContext);
+        ipcContext->get_PopupIPCTabContext().opener() = aTabOpener;
+    }
+
     unused << SendPBrowserConstructor(
         // We release this ref in DeallocPBrowserChild
         RefPtr<TabChild>(newChild).forget().take(),
-        tabId, IPCTabContext(context), aChromeFlags,
+        tabId, *ipcContext, aChromeFlags,
         GetID(), IsForApp(), IsForBrowser());
 
     nsAutoCString url;
     if (aURI) {
         aURI->GetSpec(url);
     } else {
         // We can't actually send a nullptr up as the URI, since IPDL doesn't let us
         // send nullptr's for primitives. We indicate that the nsString for the URI
@@ -821,27 +834,29 @@ ContentChild::ProvideWindowCommon(TabChi
     nsCString urlToLoad;
 
     if (aIframeMoz) {
         MOZ_ASSERT(aTabOpener);
         newChild->SendBrowserFrameOpenWindow(aTabOpener, NS_ConvertUTF8toUTF16(url), name,
                                              NS_ConvertUTF8toUTF16(features),
                                              aWindowIsNew);
     } else {
-        nsCOMPtr<nsPIDOMWindow> opener = do_QueryInterface(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;
+        nsAutoCString baseURIString;
+        if (aTabOpener) {
+            nsCOMPtr<nsPIDOMWindow> opener = do_QueryInterface(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);
         }
 
-        nsAutoCString baseURIString;
-        baseURI->GetSpec(baseURIString);
-
         nsresult rv;
         if (!SendCreateWindow(aTabOpener, newChild,
                               aChromeFlags, aCalledFromJS, aPositionSpecified,
                               aSizeSpecified, url,
                               name, features,
                               baseURIString,
                               &rv,
                               aWindowIsNew,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5396,20 +5396,22 @@ ContentParent::RecvCreateWindow(PBrowser
 
     // 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.
     MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW));
     MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW));
     MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME));
     MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW));
 
-    TabParent* thisTabParent = TabParent::GetFrom(aThisTab);
-    MOZ_ASSERT(thisTabParent);
-
-    if (NS_WARN_IF(thisTabParent->IsBrowserOrApp())) {
+    TabParent* thisTabParent = nullptr;
+    if (aThisTab) {
+        thisTabParent = TabParent::GetFrom(aThisTab);
+    }
+
+    if (NS_WARN_IF(thisTabParent && thisTabParent->IsBrowserOrApp())) {
         return false;
     }
 
     nsCOMPtr<nsPIWindowWatcher> pwwatch =
         do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult);
 
     if (NS_WARN_IF(NS_FAILED(*aResult))) {
         return true;
@@ -5417,30 +5419,36 @@ ContentParent::RecvCreateWindow(PBrowser
 
     TabParent* newTab = TabParent::GetFrom(aNewTab);
     MOZ_ASSERT(newTab);
 
     // Content has requested that we open this new content window, so
     // we must have an opener.
     newTab->SetHasContentOpener(true);
 
-    nsCOMPtr<nsIContent> frame(do_QueryInterface(thisTabParent->GetOwnerElement()));
+    nsCOMPtr<nsIContent> frame;
+    if (thisTabParent) {
+        frame = do_QueryInterface(thisTabParent->GetOwnerElement());
+    }
 
     nsCOMPtr<nsPIDOMWindow> 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 = thisTabParent->GetBrowserDOMWindow();
+    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 = FindMostRecentOpenWindow();
         if (NS_WARN_IF(!parent)) {
             *aResult = NS_ERROR_FAILURE;
             return true;
@@ -5462,18 +5470,20 @@ ContentParent::RecvCreateWindow(PBrowser
     // Opening new tabs is the easy case...
     if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
         if (NS_WARN_IF(!browserDOMWin)) {
             *aResult = NS_ERROR_FAILURE;
             return true;
         }
 
         bool isPrivate = false;
-        nsCOMPtr<nsILoadContext> loadContext = thisTabParent->GetLoadContext();
-        loadContext->GetUsePrivateBrowsing(&isPrivate);
+        if (thisTabParent) {
+            nsCOMPtr<nsILoadContext> loadContext = thisTabParent->GetLoadContext();
+            loadContext->GetUsePrivateBrowsing(&isPrivate);
+        }
 
         nsCOMPtr<nsIOpenURIInFrameParams> params = new nsOpenURIInFrameParams();
         params->SetReferrer(NS_ConvertUTF8toUTF16(aBaseURI));
         params->SetIsPrivate(isPrivate);
 
         TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);
 
         nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner;
--- a/dom/ipc/ContentProcessManager.cpp
+++ b/dom/ipc/ContentProcessManager.cpp
@@ -145,29 +145,28 @@ ContentProcessManager::AllocateTabId(con
   auto iter = mContentParentMap.find(aChildCpId);
   if (NS_WARN_IF(iter == mContentParentMap.end())) {
     ASSERT_UNLESS_FUZZING();
     return TabId(0);
   }
 
   struct RemoteFrameInfo info;
 
-  const IPCTabContextUnion& contextUnion = aContext.contextUnion();
   // If it's a PopupIPCTabContext, it's the case that a TabChild want to
   // open a new tab. aOpenerTabId has to be it's parent frame's opener id.
-  if (contextUnion.type() == IPCTabContextUnion::TPopupIPCTabContext) {
+  if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
     auto remoteFrameIter = iter->second.mRemoteFrames.find(aOpenerTabId);
     if (remoteFrameIter == iter->second.mRemoteFrames.end()) {
       ASSERT_UNLESS_FUZZING("Failed to find parent frame's opener id.");
       return TabId(0);
     }
 
     info.mOpenerTabId = remoteFrameIter->second.mOpenerTabId;
 
-    const PopupIPCTabContext &ipcContext = contextUnion.get_PopupIPCTabContext();
+    const PopupIPCTabContext &ipcContext = aContext.get_PopupIPCTabContext();
     MOZ_ASSERT(ipcContext.opener().type() == PBrowserOrId::TTabId);
 
     remoteFrameIter = iter->second.mRemoteFrames.find(ipcContext.opener().get_TabId());
     if (remoteFrameIter == iter->second.mRemoteFrames.end()) {
       ASSERT_UNLESS_FUZZING("Failed to find tab id.");
       return TabId(0);
     }
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1108,17 +1108,17 @@ parent:
     async Profile(nsCString aProfile);
 
     /**
      * Request graphics initialization information from the parent.
      */
     sync GetGraphicsDeviceInitData()
         returns (DeviceInitData aData);
 
-    sync CreateWindow(PBrowser aThisTab,
+    sync CreateWindow(nullable PBrowser aThisTab,
                       PBrowser aNewTab,
                       uint32_t aChromeFlags,
                       bool aCalledFromJS,
                       bool aPositionSpecified,
                       bool aSizeSpecified,
                       nsCString aURI,
                       nsString aName,
                       nsCString aFeatures,
--- a/dom/ipc/PTabContext.ipdlh
+++ b/dom/ipc/PTabContext.ipdlh
@@ -39,28 +39,31 @@ struct FrameIPCTabContext
   uint32_t frameOwnerAppId;
 
   // The origin without originAttribute suffix for a signed package.
   // This value would be empty if the TabContext doesn't own a signed
   // package.
   nsCString signedPkgOriginNoSuffix;
 };
 
+// XXXcatalinb: This is only used by ServiceWorkerClients::OpenWindow.
+// Because service workers don't have an associated TabChild
+// we can't satisfy the security constraints on b2g. As such, the parent
+// process will accept this tab context only on desktop.
+struct UnsafeIPCTabContext
+{ };
+
 // IPCTabContext is an analog to mozilla::dom::TabContext.  Both specify an
 // iframe/PBrowser's own and containing app-ids and tell you whether the
 // iframe/PBrowser is a browser frame.  But only IPCTabContext is allowed to
 // travel over IPC.
 //
 // We need IPCTabContext (specifically, PopupIPCTabContext) to prevent a
-// privilege escalation attack by a compromised child process.  See the comment
-// on AllocPBrowser for details.
-union IPCTabContextUnion
+// privilege escalation attack by a compromised child process.
+union IPCTabContext
 {
   PopupIPCTabContext;
   FrameIPCTabContext;
-};
-
-struct IPCTabContext {
-  IPCTabContextUnion contextUnion;
+  UnsafeIPCTabContext;
 };
 
 }
 }
--- a/dom/ipc/TabContext.cpp
+++ b/dom/ipc/TabContext.cpp
@@ -227,20 +227,19 @@ GetAppForId(uint32_t aAppId)
 MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
   : mInvalidReason(nullptr)
 {
   uint32_t containingAppId = NO_APP_ID;
   OriginAttributes originAttributes = OriginAttributes();
   nsAutoCString originSuffix;
   nsAutoCString signedPkgOriginNoSuffix;
 
-  const IPCTabContextUnion& contextUnion = aParams.contextUnion();
-  switch(contextUnion.type()) {
-    case IPCTabContextUnion::TPopupIPCTabContext: {
-      const PopupIPCTabContext &ipcContext = contextUnion.get_PopupIPCTabContext();
+  switch(aParams.type()) {
+    case IPCTabContext::TPopupIPCTabContext: {
+      const PopupIPCTabContext &ipcContext = aParams.get_PopupIPCTabContext();
 
       TabContext *context;
       if (ipcContext.opener().type() == PBrowserOrId::TPBrowserParent) {
         context = TabParent::GetFrom(ipcContext.opener().get_PBrowserParent());
         if (context->IsBrowserElement() && !ipcContext.isBrowserElement()) {
           // If the TabParent corresponds to a browser element, then it can only
           // open other browser elements, for security reasons.  We should have
           // checked this before calling the TabContext constructor, so this is
@@ -276,26 +275,43 @@ MaybeInvalidTabContext::MaybeInvalidTabC
       originAttributes = context->mOriginAttributes;
       if (ipcContext.isBrowserElement()) {
         containingAppId = context->OwnOrContainingAppId();
       } else {
         containingAppId = context->mContainingAppId;
       }
       break;
     }
-    case IPCTabContextUnion::TFrameIPCTabContext: {
+    case IPCTabContext::TFrameIPCTabContext: {
       const FrameIPCTabContext &ipcContext =
-        contextUnion.get_FrameIPCTabContext();
+        aParams.get_FrameIPCTabContext();
 
       containingAppId = ipcContext.frameOwnerAppId();
       signedPkgOriginNoSuffix = ipcContext.signedPkgOriginNoSuffix();
       originSuffix = ipcContext.originSuffix();
       originAttributes.PopulateFromSuffix(originSuffix);
       break;
     }
+    case IPCTabContext::TUnsafeIPCTabContext: {
+      // XXXcatalinb: This used *only* by ServiceWorkerClients::OpenWindow.
+      // It is meant as a temporary solution until service workers can
+      // provide a TabChild equivalent. Don't allow this on b2g since
+      // it might be used to escalate privileges.
+#ifdef MOZ_B2G
+      mInvalidReason = "ServiceWorkerClients::OpenWindow is not supported.";
+      return;
+#endif
+      if (!Preferences::GetBool("dom.serviceWorkers.enabled", false)) {
+        mInvalidReason = "ServiceWorkers should be enabled.";
+        return;
+      }
+
+      containingAppId = NO_APP_ID;
+      break;
+    }
     default: {
       MOZ_CRASH();
     }
   }
 
   nsCOMPtr<mozIApplication> ownApp;
   if (!originAttributes.mInBrowser) {
     // mAppId corresponds to OwnOrContainingAppId; if mInBrowser is
--- a/dom/ipc/nsIContentParent.cpp
+++ b/dom/ipc/nsIContentParent.cpp
@@ -66,45 +66,46 @@ nsIContentParent::DeallocPJavaScriptPare
 {
   ReleaseJavaScriptParent(aParent);
   return true;
 }
 
 bool
 nsIContentParent::CanOpenBrowser(const IPCTabContext& aContext)
 {
-  const IPCTabContextUnion& contextUnion = aContext.contextUnion();
-
-  // We don't trust the IPCTabContext we receive from the child, so we'll bail
-  // if we receive an IPCTabContext that's not a PopupIPCTabContext.
   // (PopupIPCTabContext lets the child process prove that it has access to
   // the app it's trying to open.)
-  if (contextUnion.type() != IPCTabContextUnion::TPopupIPCTabContext) {
+  // On e10s we also allow UnsafeTabContext to allow service workers to open
+  // windows. This is enforced in MaybeInvalidTabContext.
+  if (aContext.type() != IPCTabContext::TPopupIPCTabContext &&
+      aContext.type() != IPCTabContext::TUnsafeIPCTabContext) {
     ASSERT_UNLESS_FUZZING("Unexpected IPCTabContext type.  Aborting AllocPBrowserParent.");
     return false;
   }
 
-  const PopupIPCTabContext& popupContext = contextUnion.get_PopupIPCTabContext();
-  if (popupContext.opener().type() != PBrowserOrId::TPBrowserParent) {
-    ASSERT_UNLESS_FUZZING("Unexpected PopupIPCTabContext type.  Aborting AllocPBrowserParent.");
-    return false;
-  }
+  if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
+    const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
+    if (popupContext.opener().type() != PBrowserOrId::TPBrowserParent) {
+      ASSERT_UNLESS_FUZZING("Unexpected PopupIPCTabContext type.  Aborting AllocPBrowserParent.");
+      return false;
+    }
 
-  auto opener = TabParent::GetFrom(popupContext.opener().get_PBrowserParent());
-  if (!opener) {
-    ASSERT_UNLESS_FUZZING("Got null opener from child; aborting AllocPBrowserParent.");
-    return false;
-  }
+    auto opener = TabParent::GetFrom(popupContext.opener().get_PBrowserParent());
+    if (!opener) {
+      ASSERT_UNLESS_FUZZING("Got null opener from child; aborting AllocPBrowserParent.");
+      return false;
+    }
 
-  // Popup windows of isBrowser frames must be isBrowser if the parent
-  // isBrowser.  Allocating a !isBrowser frame with same app ID would allow
-  // the content to access data it's not supposed to.
-  if (!popupContext.isBrowserElement() && opener->IsBrowserElement()) {
-    ASSERT_UNLESS_FUZZING("Child trying to escalate privileges!  Aborting AllocPBrowserParent.");
-    return false;
+    // Popup windows of isBrowser frames must be isBrowser if the parent
+    // isBrowser.  Allocating a !isBrowser frame with same app ID would allow
+    // the content to access data it's not supposed to.
+    if (!popupContext.isBrowserElement() && opener->IsBrowserElement()) {
+      ASSERT_UNLESS_FUZZING("Child trying to escalate privileges!  Aborting AllocPBrowserParent.");
+      return false;
+    }
   }
 
   MaybeInvalidTabContext tc(aContext);
   if (!tc.IsValid()) {
     NS_ERROR(nsPrintfCString("Child passed us an invalid TabContext.  (%s)  "
                              "Aborting AllocPBrowserParent.",
                              tc.GetInvalidReason()).get());
     return false;
@@ -124,36 +125,35 @@ nsIContentParent::AllocPBrowserParent(co
   unused << aCpId;
   unused << aIsForApp;
   unused << aIsForBrowser;
 
   if (!CanOpenBrowser(aContext)) {
     return nullptr;
   }
 
-  const IPCTabContextUnion& contextUnion = aContext.contextUnion();
-  const PopupIPCTabContext& popupContext = contextUnion.get_PopupIPCTabContext();
-
   uint32_t chromeFlags = aChromeFlags;
+  if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
+    // CanOpenBrowser has ensured that the IPCTabContext is of
+    // type PopupIPCTabContext, and that the opener TabParent is
+    // reachable.
+    const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
+    auto opener = TabParent::GetFrom(popupContext.opener().get_PBrowserParent());
+    // We must ensure that the private browsing and remoteness flags
+    // match those of the opener.
+    nsCOMPtr<nsILoadContext> loadContext = opener->GetLoadContext();
+    if (!loadContext) {
+      return nullptr;
+    }
 
-  // CanOpenBrowser has ensured that the IPCTabContext is of
-  // type PopupIPCTabContext, and that the opener TabParent is
-  // reachable.
-  auto opener = TabParent::GetFrom(popupContext.opener().get_PBrowserParent());
-  // We must ensure that the private browsing and remoteness flags
-  // match those of the opener.
-  nsCOMPtr<nsILoadContext> loadContext = opener->GetLoadContext();
-  if (!loadContext) {
-    return nullptr;
-  }
-
-  bool isPrivate;
-  loadContext->GetUsePrivateBrowsing(&isPrivate);
-  if (isPrivate) {
-    chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
+    bool isPrivate;
+    loadContext->GetUsePrivateBrowsing(&isPrivate);
+    if (isPrivate) {
+      chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
+    }
   }
 
   // And because we're allocating a remote browser, of course the
   // window is remote.
   chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
 
   MaybeInvalidTabContext tc(aContext);
   MOZ_ASSERT(tc.IsValid());
--- a/dom/workers/ServiceWorkerClients.cpp
+++ b/dom/workers/ServiceWorkerClients.cpp
@@ -15,19 +15,22 @@
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
 #include "nsIDocShell.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIDOMWindow.h"
 #include "nsIWebNavigation.h"
-#include "nsIWindowMediator.h"
 #include "nsIWebProgress.h"
 #include "nsIWebProgressListener.h"
+#include "nsIWindowMediator.h"
+#include "nsIWindowWatcher.h"
+#include "nsPIWindowWatcher.h"
+#include "nsWindowWatcher.h"
 #include "nsWeakReference.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::workers;
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClients)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerClients)
@@ -493,16 +496,41 @@ private:
 
     // [[6.1 Open Window]]
     nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID,
                                                    &rv);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
+    if (XRE_IsContentProcess()) {
+      // ContentProcess
+      nsCOMPtr<nsIWindowWatcher> wwatch =
+        do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
+      NS_ENSURE_STATE(pwwatch);
+
+      nsCString spec;
+      uri->GetSpec(spec);
+
+      nsCOMPtr<nsIDOMWindow> newWindow;
+      pwwatch->OpenWindow2(nullptr,
+                           spec.get(),
+                           nullptr,
+                           nullptr,
+                           false, false, true, nullptr, nullptr,
+                           getter_AddRefs(newWindow));
+      nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(newWindow);
+      pwindow.forget(aWindow);
+      return NS_OK;
+    }
+
     // Find the most recent browser window and open a new tab in it.
     nsCOMPtr<nsIDOMWindow> browserWindow;
     rv = wm->GetMostRecentWindow(MOZ_UTF16("navigator:browser"),
                                  getter_AddRefs(browserWindow));
     if (NS_WARN_IF(NS_FAILED(rv)) || !browserWindow) {
       // It is possible to be running without a browser window on Mac OS, so
       // we need to open a new chrome window.
       // TODO(catalinb): open new chrome window. Bug 1218080
@@ -575,23 +603,16 @@ ServiceWorkerClients::MatchAll(const Cli
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerClients::OpenWindow(const nsAString& aUrl,
                                  ErrorResult& aRv)
 {
-  // XXXcatalinb: This works only on non-multiprocess for now, bail if we're
-  // running in a content process.
-  if (XRE_IsContentProcess()) {
-    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return nullptr;
-  }
-
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
 
   RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
--- a/embedding/components/windowwatcher/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp
@@ -547,18 +547,18 @@ nsWindowWatcher::OpenWindowInternal(nsID
       if (parentDocShell->IsSandboxedFrom(foundDocShell)) {
         return NS_ERROR_DOM_INVALID_ACCESS_ERR;
       }
     }
   }
 
   // no extant window? make a new one.
 
-  // If no parent, consider it chrome.
-  bool hasChromeParent = true;
+  // If no parent, consider it chrome when running in the parent process.
+  bool hasChromeParent = XRE_IsContentProcess() ? false : true;
   if (aParent) {
     // Check if the parent document has chrome privileges.
     nsIDocument* doc = parentWindow->GetDoc();
     hasChromeParent =
       doc && nsContentUtils::IsChromeDoc(doc) && !openedFromRemoteTab;
   }
 
   // Make sure we call CalculateChromeFlags() *before* we push the
@@ -615,17 +615,19 @@ nsWindowWatcher::OpenWindowInternal(nsID
     // window parented to a content window (such as a download dialog), usually
     // directly with nsIWindowWatcher. In those cases, we want the principal of
     // the initial about:blank document to be system, so that the subsequent XUL
     // load can reuse the inner window and avoid blowing away expandos. As such,
     // we decide whether to load with the principal of the caller or of the parent
     // based on whether the docshell type is chrome or content.
 
     nsCOMPtr<nsIGlobalObject> parentGlobalObject = do_QueryInterface(aParent);
-    if (NS_WARN_IF(!jsapiChromeGuard.Init(parentGlobalObject))) {
+    if (!aParent) {
+      jsapiChromeGuard.Init();
+    } else if (NS_WARN_IF(!jsapiChromeGuard.Init(parentGlobalObject))) {
       return NS_ERROR_UNEXPECTED;
     }
   }
 
   uint32_t activeDocsSandboxFlags = 0;
   if (!newDocShellItem) {
     // We're going to either open up a new window ourselves or ask a
     // nsIWindowProvider for one.  In either case, we'll want to set the right
@@ -647,20 +649,26 @@ nsWindowWatcher::OpenWindowInternal(nsID
     // Now check whether it's ok to ask a window provider for a window.  Don't
     // do it if we're opening a dialog or if our parent is a chrome window or
     // if we're opening something that has modal, dialog, or chrome flags set.
     nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(aParent);
     if (!aDialog && !chromeWin &&
         !(chromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
                          nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
                          nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
-      nsCOMPtr<nsIWindowProvider> provider = do_GetInterface(parentTreeOwner);
+      nsCOMPtr<nsIWindowProvider> provider;
+      if (parentTreeOwner) {
+        provider = do_GetInterface(parentTreeOwner);
+      } else if (XRE_IsContentProcess()) {
+        // we're in a content process but we don't have a tabchild we can
+        // use.
+        provider = nsContentUtils::GetWindowProviderForContentProcess();
+      }
+
       if (provider) {
-        NS_ASSERTION(aParent, "We've _got_ to have a parent here!");
-
         nsCOMPtr<nsIDOMWindow> newWindow;
         rv = provider->ProvideWindow(aParent, chromeFlags, aCalledFromJS,
                                      sizeSpec.PositionSpecified(),
                                      sizeSpec.SizeSpecified(),
                                      uriToLoad, name, features, &windowIsNew,
                                      getter_AddRefs(newWindow));
 
         if (NS_SUCCEEDED(rv)) {