Bug 1542791 - Part 1: Support enabling out-of-process iframes with per-frame granularity, r=mconley
authorNika Layzell <nika@thelayzells.com>
Wed, 17 Apr 2019 00:53:22 +0000
changeset 469832 2846ae7210336f4e8c72076edaab027e7027ac8f
parent 469831 59f0c7c6226621e567e311cf47482a97c05543c7
child 469833 32161364d032bdb0e0f50ec352f4788f2e6ef080
push id35883
push userbtara@mozilla.com
push dateWed, 17 Apr 2019 21:47:29 +0000
treeherdermozilla-central@02b89c29412b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley
bugs1542791
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1542791 - Part 1: Support enabling out-of-process iframes with per-frame granularity, r=mconley This code builds on top of the E10S "remote tabs" configuration system to add a system for specifying that remote subframes should be used. Fission can be enabled for a window by including the "fission" flag in options when opening the window. Differential Revision: https://phabricator.services.mozilla.com/D26560
browser/base/content/browser.js
docshell/base/LoadContext.cpp
docshell/base/LoadContext.h
docshell/base/SerializedLoadContext.cpp
docshell/base/SerializedLoadContext.h
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsILoadContext.idl
dom/ipc/TabChild.cpp
dom/ipc/TabParent.cpp
toolkit/components/browser/nsIWebBrowserChrome.idl
toolkit/components/startup/nsAppStartup.cpp
toolkit/components/windowwatcher/nsWindowWatcher.cpp
uriloader/prefetch/OfflineCacheUpdateParent.cpp
xpfe/appshell/nsAppShellService.cpp
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4536,16 +4536,19 @@ function toOpenWindowByType(inType, uri,
  *
  * @param {Object} options
  *        {
  *          private: A boolean indicating if the window should be
  *                   private
  *          remote:  A boolean indicating if the window should run
  *                   remote browser tabs or not. If omitted, the window
  *                   will choose the profile default state.
+ *          fission: A boolean indicating if the window should run
+ *                   with fission enabled or not. If omitted, the window
+ *                   will choose the profile default state.
  *        }
  * @return a reference to the new window.
  */
 function OpenBrowserWindow(options) {
   var telemetryObj = {};
   TelemetryStopwatch.start("FX_NEW_WINDOW_MS", telemetryObj);
 
   var handler = Cc["@mozilla.org/browser/clh;1"]
@@ -4565,16 +4568,22 @@ function OpenBrowserWindow(options) {
   }
 
   if (options && options.remote) {
     extraFeatures += ",remote";
   } else if (options && options.remote === false) {
     extraFeatures += ",non-remote";
   }
 
+  if (options && options.fission) {
+    extraFeatures += ",fission";
+  } else if (options && options.fission === false) {
+    extraFeatures += ",non-fission";
+  }
+
   // If the window is maximized, we want to skip the animation, since we're
   // going to be taking up most of the screen anyways, and we want to optimize
   // for showing the user a useful window as soon as possible.
   if (window.windowState == window.STATE_MAXIMIZED) {
     extraFeatures += ",suppressanimation";
   }
 
   // if and only if the current window is a browser window and it has a document with a character
--- a/docshell/base/LoadContext.cpp
+++ b/docshell/base/LoadContext.cpp
@@ -18,28 +18,31 @@ namespace mozilla {
 NS_IMPL_ISUPPORTS(LoadContext, nsILoadContext, nsIInterfaceRequestor)
 
 LoadContext::LoadContext(nsIPrincipal* aPrincipal,
                          nsILoadContext* aOptionalBase)
     : mTopFrameElement(nullptr),
       mNestedFrameId(0),
       mIsContent(true),
       mUseRemoteTabs(false),
+      mUseRemoteSubframes(false),
       mUseTrackingProtection(false),
 #ifdef DEBUG
       mIsNotNull(true),
 #endif
       mOriginAttributes(aPrincipal->OriginAttributesRef()) {
   if (!aOptionalBase) {
     return;
   }
 
   MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetIsContent(&mIsContent));
   MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetUseRemoteTabs(&mUseRemoteTabs));
   MOZ_ALWAYS_SUCCEEDS(
+      aOptionalBase->GetUseRemoteSubframes(&mUseRemoteSubframes));
+  MOZ_ALWAYS_SUCCEEDS(
       aOptionalBase->GetUseTrackingProtection(&mUseTrackingProtection));
 }
 
 //-----------------------------------------------------------------------------
 // LoadContext::nsILoadContext
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
@@ -122,16 +125,34 @@ NS_IMETHODIMP
 LoadContext::SetRemoteTabs(bool aUseRemoteTabs) {
   MOZ_ASSERT(mIsNotNull);
 
   // We shouldn't need this on parent...
   return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
+LoadContext::GetUseRemoteSubframes(bool* aUseRemoteSubframes) {
+  MOZ_ASSERT(mIsNotNull);
+
+  NS_ENSURE_ARG_POINTER(aUseRemoteSubframes);
+
+  *aUseRemoteSubframes = mUseRemoteSubframes;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::SetRemoteSubframes(bool aUseRemoteSubframes) {
+  MOZ_ASSERT(mIsNotNull);
+
+  // We shouldn't need this on parent...
+  return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
 LoadContext::GetIsInIsolatedMozBrowserElement(
     bool* aIsInIsolatedMozBrowserElement) {
   MOZ_ASSERT(mIsNotNull);
 
   NS_ENSURE_ARG_POINTER(aIsInIsolatedMozBrowserElement);
 
   *aIsInIsolatedMozBrowserElement = mOriginAttributes.mInIsolatedMozBrowser;
   return NS_OK;
--- a/docshell/base/LoadContext.h
+++ b/docshell/base/LoadContext.h
@@ -36,60 +36,65 @@ class LoadContext final : public nsILoad
   // appId/inIsolatedMozBrowser arguments override those in
   // SerializedLoadContext provided by child process.
   LoadContext(const IPC::SerializedLoadContext& aToCopy,
               dom::Element* aTopFrameElement, OriginAttributes& aAttrs)
       : mTopFrameElement(do_GetWeakReference(aTopFrameElement)),
         mNestedFrameId(0),
         mIsContent(aToCopy.mIsContent),
         mUseRemoteTabs(aToCopy.mUseRemoteTabs),
+        mUseRemoteSubframes(aToCopy.mUseRemoteSubframes),
         mUseTrackingProtection(aToCopy.mUseTrackingProtection),
 #ifdef DEBUG
         mIsNotNull(aToCopy.mIsNotNull),
 #endif
         mOriginAttributes(aAttrs) {
   }
 
   // appId/inIsolatedMozBrowser arguments override those in
   // SerializedLoadContext provided by child process.
   LoadContext(const IPC::SerializedLoadContext& aToCopy,
               uint64_t aNestedFrameId, OriginAttributes& aAttrs)
       : mTopFrameElement(nullptr),
         mNestedFrameId(aNestedFrameId),
         mIsContent(aToCopy.mIsContent),
         mUseRemoteTabs(aToCopy.mUseRemoteTabs),
+        mUseRemoteSubframes(aToCopy.mUseRemoteSubframes),
         mUseTrackingProtection(aToCopy.mUseTrackingProtection),
 #ifdef DEBUG
         mIsNotNull(aToCopy.mIsNotNull),
 #endif
         mOriginAttributes(aAttrs) {
   }
 
   LoadContext(dom::Element* aTopFrameElement, bool aIsContent,
               bool aUsePrivateBrowsing, bool aUseRemoteTabs,
-              bool aUseTrackingProtection, const OriginAttributes& aAttrs)
+              bool aUseRemoteSubframes, bool aUseTrackingProtection,
+              const OriginAttributes& aAttrs)
       : mTopFrameElement(do_GetWeakReference(aTopFrameElement)),
         mNestedFrameId(0),
         mIsContent(aIsContent),
         mUseRemoteTabs(aUseRemoteTabs),
+        mUseRemoteSubframes(aUseRemoteSubframes),
         mUseTrackingProtection(aUseTrackingProtection),
 #ifdef DEBUG
         mIsNotNull(true),
 #endif
         mOriginAttributes(aAttrs) {
     MOZ_DIAGNOSTIC_ASSERT(aUsePrivateBrowsing ==
                           (aAttrs.mPrivateBrowsingId > 0));
   }
 
   // Constructor taking reserved origin attributes.
   explicit LoadContext(OriginAttributes& aAttrs)
       : mTopFrameElement(nullptr),
         mNestedFrameId(0),
         mIsContent(false),
         mUseRemoteTabs(false),
+        mUseRemoteSubframes(false),
         mUseTrackingProtection(false),
 #ifdef DEBUG
         mIsNotNull(true),
 #endif
         mOriginAttributes(aAttrs) {
   }
 
   // Constructor for creating a LoadContext with a given principal's appId and
@@ -99,16 +104,17 @@ class LoadContext final : public nsILoad
 
  private:
   ~LoadContext() {}
 
   nsWeakPtr mTopFrameElement;
   uint64_t mNestedFrameId;
   bool mIsContent;
   bool mUseRemoteTabs;
+  bool mUseRemoteSubframes;
   bool mUseTrackingProtection;
 #ifdef DEBUG
   bool mIsNotNull;
 #endif
   OriginAttributes mOriginAttributes;
 };
 
 already_AddRefed<nsILoadContext> CreateLoadContext();
--- a/docshell/base/SerializedLoadContext.cpp
+++ b/docshell/base/SerializedLoadContext.cpp
@@ -8,22 +8,28 @@
 #include "nsNetUtil.h"
 #include "nsIChannel.h"
 #include "nsIPrivateBrowsingChannel.h"
 #include "nsIWebSocketChannel.h"
 
 namespace IPC {
 
 SerializedLoadContext::SerializedLoadContext(nsILoadContext* aLoadContext)
-    : mIsContent(false), mUseRemoteTabs(false), mUseTrackingProtection(false) {
+    : mIsContent(false),
+      mUseRemoteTabs(false),
+      mUseRemoteSubframes(false),
+      mUseTrackingProtection(false) {
   Init(aLoadContext);
 }
 
 SerializedLoadContext::SerializedLoadContext(nsIChannel* aChannel)
-    : mIsContent(false), mUseRemoteTabs(false), mUseTrackingProtection(false) {
+    : mIsContent(false),
+      mUseRemoteTabs(false),
+      mUseRemoteSubframes(false),
+      mUseTrackingProtection(false) {
   if (!aChannel) {
     Init(nullptr);
     return;
   }
 
   nsCOMPtr<nsILoadContext> loadContext;
   NS_QueryNotificationCallbacks(aChannel, loadContext);
   Init(loadContext);
@@ -40,36 +46,41 @@ SerializedLoadContext::SerializedLoadCon
         isOverriden) {
       mIsPrivateBitValid = true;
     }
     mOriginAttributes.SyncAttributesWithPrivateBrowsing(isPrivate);
   }
 }
 
 SerializedLoadContext::SerializedLoadContext(nsIWebSocketChannel* aChannel)
-    : mIsContent(false), mUseRemoteTabs(false), mUseTrackingProtection(false) {
+    : mIsContent(false),
+      mUseRemoteTabs(false),
+      mUseRemoteSubframes(false),
+      mUseTrackingProtection(false) {
   nsCOMPtr<nsILoadContext> loadContext;
   if (aChannel) {
     NS_QueryNotificationCallbacks(aChannel, loadContext);
   }
   Init(loadContext);
 }
 
 void SerializedLoadContext::Init(nsILoadContext* aLoadContext) {
   if (aLoadContext) {
     mIsNotNull = true;
     mIsPrivateBitValid = true;
     aLoadContext->GetIsContent(&mIsContent);
     aLoadContext->GetUseRemoteTabs(&mUseRemoteTabs);
+    aLoadContext->GetUseRemoteSubframes(&mUseRemoteSubframes);
     aLoadContext->GetUseTrackingProtection(&mUseTrackingProtection);
     aLoadContext->GetOriginAttributes(mOriginAttributes);
   } else {
     mIsNotNull = false;
     mIsPrivateBitValid = false;
     // none of below values really matter when mIsNotNull == false:
     // we won't be GetInterfaced to nsILoadContext
     mIsContent = true;
     mUseRemoteTabs = false;
+    mUseRemoteSubframes = false;
     mUseTrackingProtection = false;
   }
 }
 
 }  // namespace IPC
--- a/docshell/base/SerializedLoadContext.h
+++ b/docshell/base/SerializedLoadContext.h
@@ -26,16 +26,17 @@ namespace IPC {
 
 class SerializedLoadContext {
  public:
   SerializedLoadContext()
       : mIsNotNull(false),
         mIsPrivateBitValid(false),
         mIsContent(false),
         mUseRemoteTabs(false),
+        mUseRemoteSubframes(false),
         mUseTrackingProtection(false) {
     Init(nullptr);
   }
 
   explicit SerializedLoadContext(nsILoadContext* aLoadContext);
   explicit SerializedLoadContext(nsIChannel* aChannel);
   explicit SerializedLoadContext(nsIWebSocketChannel* aChannel);
 
@@ -46,16 +47,17 @@ class SerializedLoadContext {
 
   // used to indicate if child-side LoadContext * was null.
   bool mIsNotNull;
   // used to indicate if child-side mUsePrivateBrowsing flag is valid, even if
   // mIsNotNull is false, i.e., child LoadContext was null.
   bool mIsPrivateBitValid;
   bool mIsContent;
   bool mUseRemoteTabs;
+  bool mUseRemoteSubframes;
   bool mUseTrackingProtection;
   mozilla::OriginAttributes mOriginAttributes;
 };
 
 // Function to serialize over IPDL
 template <>
 struct ParamTraits<SerializedLoadContext> {
   typedef SerializedLoadContext paramType;
@@ -63,27 +65,29 @@ struct ParamTraits<SerializedLoadContext
   static void Write(Message* aMsg, const paramType& aParam) {
     nsAutoCString suffix;
     aParam.mOriginAttributes.CreateSuffix(suffix);
 
     WriteParam(aMsg, aParam.mIsNotNull);
     WriteParam(aMsg, aParam.mIsContent);
     WriteParam(aMsg, aParam.mIsPrivateBitValid);
     WriteParam(aMsg, aParam.mUseRemoteTabs);
+    WriteParam(aMsg, aParam.mUseRemoteSubframes);
     WriteParam(aMsg, aParam.mUseTrackingProtection);
     WriteParam(aMsg, suffix);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter,
                    paramType* aResult) {
     nsAutoCString suffix;
     if (!ReadParam(aMsg, aIter, &aResult->mIsNotNull) ||
         !ReadParam(aMsg, aIter, &aResult->mIsContent) ||
         !ReadParam(aMsg, aIter, &aResult->mIsPrivateBitValid) ||
         !ReadParam(aMsg, aIter, &aResult->mUseRemoteTabs) ||
+        !ReadParam(aMsg, aIter, &aResult->mUseRemoteSubframes) ||
         !ReadParam(aMsg, aIter, &aResult->mUseTrackingProtection) ||
         !ReadParam(aMsg, aIter, &suffix)) {
       return false;
     }
     return aResult->mOriginAttributes.PopulateFromSuffix(suffix);
   }
 };
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -363,16 +363,17 @@ nsDocShell::nsDocShell(BrowsingContext* 
       mAllowAuth(mItemType == typeContent),
       mAllowKeywordFixup(false),
       mIsOffScreenBrowser(false),
       mIsActive(true),
       mDisableMetaRefreshWhenInactive(false),
       mIsAppTab(false),
       mUseGlobalHistory(false),
       mUseRemoteTabs(false),
+      mUseRemoteSubframes(false),
       mUseTrackingProtection(false),
       mDeviceSizeIsPageSize(false),
       mWindowDraggingAllowed(false),
       mInFrameSwap(false),
       mInheritPrivateBrowsingId(true),
       mCanExecuteScripts(false),
       mFiredUnloadEvent(false),
       mEODForCurrentDocument(false),
@@ -1521,21 +1522,47 @@ nsDocShell::GetUseRemoteTabs(bool* aUseR
 
 NS_IMETHODIMP
 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) {
   if (aUseRemoteTabs) {
     CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::DOMIPCEnabled,
                                        true);
   }
 
+  // Don't allow non-remote tabs with remote subframes.
+  if (NS_WARN_IF(!aUseRemoteTabs && mUseRemoteSubframes)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
   mUseRemoteTabs = aUseRemoteTabs;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShell::GetUseRemoteSubframes(bool* aUseRemoteSubframes) {
+  NS_ENSURE_ARG_POINTER(aUseRemoteSubframes);
+
+  *aUseRemoteSubframes = mUseRemoteSubframes;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetRemoteSubframes(bool aUseRemoteSubframes) {
+  // Should we annotate crash reports like in aUseRemoteTabs?
+
+  // Don't allow non-remote tabs with remote subframes.
+  if (NS_WARN_IF(aUseRemoteSubframes && !mUseRemoteTabs)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  mUseRemoteSubframes = aUseRemoteSubframes;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime) {
   MOZ_ASSERT(!mIsBeingDestroyed);
 
   bool change = aAffectLifetime != mAffectPrivateSessionLifetime;
   if (change && UsePrivateBrowsing()) {
     AssertOriginAttributesMatchPrivateBrowsing();
     if (aAffectLifetime) {
       IncreasePrivateDocShellCount();
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -238,16 +238,18 @@ class nsDocShell final : public nsDocLoa
   NS_IMETHOD GetTopFrameElement(mozilla::dom::Element**) override;
   NS_IMETHOD GetNestedFrameId(uint64_t*) override;
   NS_IMETHOD GetIsContent(bool*) override;
   NS_IMETHOD GetUsePrivateBrowsing(bool*) override;
   NS_IMETHOD SetUsePrivateBrowsing(bool) override;
   NS_IMETHOD SetPrivateBrowsing(bool) override;
   NS_IMETHOD GetUseRemoteTabs(bool*) override;
   NS_IMETHOD SetRemoteTabs(bool) override;
+  NS_IMETHOD GetUseRemoteSubframes(bool*) override;
+  NS_IMETHOD SetRemoteSubframes(bool) override;
   NS_IMETHOD GetScriptableOriginAttributes(
       JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD_(void)
   GetOriginAttributes(mozilla::OriginAttributes& aAttrs) override;
 
   // Restores a cached presentation from history (mLSHE).
   // This method swaps out the content viewer and simulates loads for
   // subframes. It then simulates the completion of the toplevel load.
@@ -1141,16 +1143,17 @@ class nsDocShell final : public nsDocLoa
   bool mAllowAuth : 1;
   bool mAllowKeywordFixup : 1;
   bool mIsOffScreenBrowser : 1;
   bool mIsActive : 1;
   bool mDisableMetaRefreshWhenInactive : 1;
   bool mIsAppTab : 1;
   bool mUseGlobalHistory : 1;
   bool mUseRemoteTabs : 1;
+  bool mUseRemoteSubframes : 1;
   bool mUseTrackingProtection : 1;
   bool mDeviceSizeIsPageSize : 1;
   bool mWindowDraggingAllowed : 1;
   bool mInFrameSwap : 1;
   bool mInheritPrivateBrowsingId : 1;
 
   // Because scriptability depends on the mAllowJavascript values of our
   // ancestors, we cache the effective scriptability and recompute it when
--- a/docshell/base/nsILoadContext.idl
+++ b/docshell/base/nsILoadContext.idl
@@ -76,16 +76,21 @@ interface nsILoadContext : nsISupports
    */
   attribute boolean usePrivateBrowsing;
 
   /**
    * Attribute that determines if remote (out-of-process) tabs should be used.
    */
   readonly attribute boolean useRemoteTabs;
 
+  /**
+   * Determines if out-of-process iframes should be used.
+   */
+  readonly attribute boolean useRemoteSubframes;
+
   /*
    * Attribute that determines if tracking protection should be used. May not be
    * changed after a document has been loaded in this context.
    */
   attribute boolean useTrackingProtection;
 
 %{C++
   /**
@@ -100,16 +105,23 @@ interface nsILoadContext : nsISupports
 
   bool UseRemoteTabs()
   {
     bool usingRT = false;
     GetUseRemoteTabs(&usingRT);
     return usingRT;
   }
 
+  bool UseRemoteSubframes()
+  {
+    bool usingRSF = false;
+    GetUseRemoteSubframes(&usingRSF);
+    return usingRSF;
+  }
+
   bool UseTrackingProtection()
   {
     bool usingTP = false;
     GetUseTrackingProtection(&usingTP);
     return usingTP;
   }
 %}
 
@@ -119,16 +131,21 @@ interface nsILoadContext : nsISupports
   [noscript] void SetPrivateBrowsing(in boolean aInPrivateBrowsing);
 
   /**
    * Set the remote tabs state of the load context, meant to be used internally.
    */
   [noscript] void SetRemoteTabs(in boolean aUseRemoteTabs);
 
   /**
+   * Set the remote subframes bit of this load context. Exclusively meant to be used internally.
+   */
+  [noscript] void SetRemoteSubframes(in boolean aUseRemoteSubframes);
+
+  /**
    * Returns true iff the load is occurring inside an isolated mozbrowser
    * element. <xul:browser> is not considered to be a mozbrowser element.
    * <iframe mozbrowser noisolation> does not count as isolated since
    * isolation is disabled.  Isolation can only be disabled if the
    * containing document is chrome.
    */
   readonly attribute boolean isInIsolatedMozBrowserElement;
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -558,16 +558,18 @@ nsresult TabChild::Init(mozIDOMWindowPro
 
   docShell->SetAffectPrivateSessionLifetime(
       mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME);
   nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(WebNavigation());
   MOZ_ASSERT(loadContext);
   loadContext->SetPrivateBrowsing(OriginAttributesRef().mPrivateBrowsingId > 0);
   loadContext->SetRemoteTabs(mChromeFlags &
                              nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
+  loadContext->SetRemoteSubframes(mChromeFlags &
+                                  nsIWebBrowserChrome::CHROME_FISSION_WINDOW);
 
   // Few lines before, baseWindow->Create() will end up creating a new
   // window root in nsGlobalWindow::SetDocShell.
   // Then this chrome event handler, will be inherited to inner windows.
   // We want to also set it to the docshell so that inner windows
   // and any code that has access to the docshell
   // can all listen to the same chrome event handler.
   // XXX: ideally, we would set a chrome event handler earlier,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2906,16 +2906,17 @@ already_AddRefed<nsILoadContext> TabPare
     bool useTrackingProtection = false;
     nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell();
     if (docShell) {
       docShell->GetUseTrackingProtection(&useTrackingProtection);
     }
     loadContext = new LoadContext(
         GetOwnerElement(), true /* aIsContent */, isPrivate,
         mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW,
+        mChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW,
         useTrackingProtection, OriginAttributesRef());
     mLoadContext = loadContext;
   }
   return loadContext.forget();
 }
 
 // defined in nsITabParent
 NS_IMETHODIMP
@@ -3395,16 +3396,18 @@ class FakeChannel final : public nsIChan
   NS_IMETHOD SetPrivateBrowsing(bool) NO_IMPL;
   NS_IMETHOD GetIsInIsolatedMozBrowserElement(bool*) NO_IMPL;
   NS_IMETHOD GetScriptableOriginAttributes(JSContext*,
                                            JS::MutableHandleValue) NO_IMPL;
   NS_IMETHOD_(void)
   GetOriginAttributes(mozilla::OriginAttributes& aAttrs) override {}
   NS_IMETHOD GetUseRemoteTabs(bool*) NO_IMPL;
   NS_IMETHOD SetRemoteTabs(bool) NO_IMPL;
+  NS_IMETHOD GetUseRemoteSubframes(bool*) NO_IMPL;
+  NS_IMETHOD SetRemoteSubframes(bool) NO_IMPL;
   NS_IMETHOD GetUseTrackingProtection(bool*) NO_IMPL;
   NS_IMETHOD SetUseTrackingProtection(bool) NO_IMPL;
 #undef NO_IMPL
 
  protected:
   ~FakeChannel() {}
 
   nsCOMPtr<nsIURI> mUri;
--- a/toolkit/components/browser/nsIWebBrowserChrome.idl
+++ b/toolkit/components/browser/nsIWebBrowserChrome.idl
@@ -65,16 +65,19 @@ interface nsIWebBrowserChrome : nsISuppo
     const unsigned long CHROME_PRIVATE_WINDOW         = 0x00010000;
     const unsigned long CHROME_NON_PRIVATE_WINDOW     = 0x00020000;
     const unsigned long CHROME_PRIVATE_LIFETIME       = 0x00040000;
     const unsigned long CHROME_ALWAYS_ON_TOP          = 0x00080000;
 
     // Whether this window should use remote (out-of-process) tabs.
     const unsigned long CHROME_REMOTE_WINDOW          = 0x00100000;
 
+    // Whether this window should use out-of-process cross-origin subframes.
+    const unsigned long CHROME_FISSION_WINDOW         = 0x00200000;
+
     // Prevents new window animations on MacOS and Windows. Currently
     // ignored for Linux.
     const unsigned long CHROME_SUPPRESS_ANIMATION     = 0x01000000;
 
     const unsigned long CHROME_WINDOW_RAISED          = 0x02000000;
     const unsigned long CHROME_WINDOW_LOWERED         = 0x04000000;
     const unsigned long CHROME_CENTER_SCREEN          = 0x08000000;
 
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -593,16 +593,23 @@ nsAppStartup::CreateChromeWindow2(nsIWeb
   *aCancel = false;
   *_retval = 0;
 
   // Non-modal windows cannot be opened if we are attempting to quit
   if (mAttemptingQuit &&
       (aChromeFlags & nsIWebBrowserChrome::CHROME_MODAL) == 0)
     return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
 
+  // Fission windows must also be marked as remote
+  if ((aChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW) &&
+      !(aChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW)) {
+    NS_WARNING("Cannot create non-remote fission window!");
+    return NS_ERROR_FAILURE;
+  }
+
   nsCOMPtr<nsIXULWindow> newWindow;
 
   if (aParent) {
     nsCOMPtr<nsIXULWindow> xulParent(do_GetInterface(aParent));
     NS_ASSERTION(xulParent,
                  "window created using non-XUL parent. that's unexpected, but "
                  "may work.");
 
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
@@ -463,33 +463,34 @@ nsWindowWatcher::OpenWindowWithTabParent
     nsContentUtils::WarnScriptWasIgnored(nullptr);
     return NS_ERROR_FAILURE;
   }
 
   if (NS_WARN_IF(!mWindowCreator)) {
     return NS_ERROR_UNEXPECTED;
   }
 
+  bool isFissionWindow = Preferences::GetBool("fission.autostart");
   bool isPrivateBrowsingWindow =
       Preferences::GetBool("browser.privatebrowsing.autostart");
 
   nsCOMPtr<nsPIDOMWindowOuter> parentWindowOuter;
   if (aOpeningTabParent) {
     // We need to examine the window that aOpeningTabParent belongs to in
     // order to inform us of what kind of window we're going to open.
     TabParent* openingTab = TabParent::GetFrom(aOpeningTabParent);
     parentWindowOuter = openingTab->GetParentWindowOuter();
 
-    // Propagate the privacy status of the parent window, if
+    // Propagate the privacy & fission status of the parent window, if
     // available, to the child.
-    if (!isPrivateBrowsingWindow) {
-      nsCOMPtr<nsILoadContext> parentContext = openingTab->GetLoadContext();
-      if (parentContext) {
-        isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
-      }
+    nsCOMPtr<nsILoadContext> parentContext = openingTab->GetLoadContext();
+    if (parentContext) {
+      isFissionWindow = parentContext->UseRemoteSubframes();
+      isPrivateBrowsingWindow =
+          isPrivateBrowsingWindow || parentContext->UsePrivateBrowsing();
     }
   }
 
   if (!parentWindowOuter) {
     // We couldn't find a browser window for the opener, so either we
     // never were passed aOpeningTabParent, the window is closed,
     // or it's in the process of closing. Either way, we'll use
     // the most recently opened browser window instead.
@@ -512,16 +513,20 @@ nsWindowWatcher::OpenWindowWithTabParent
   }
 
   uint32_t chromeFlags = CalculateChromeFlagsForChild(aFeatures);
 
   // A content process has asked for a new window, which implies
   // that the new window will need to be remote.
   chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
 
+  if (isFissionWindow) {
+    chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
+  }
+
   nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
   nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
 
   CreateChromeWindow(aFeatures, parentChrome, chromeFlags,
                      aForceNoOpener ? nullptr : aOpeningTabParent, nullptr,
                      aNextTabParentId, getter_AddRefs(newWindowChrome));
 
   if (NS_WARN_IF(!newWindowChrome)) {
@@ -541,16 +546,17 @@ nsWindowWatcher::OpenWindowWithTabParent
   }
 
   nsCOMPtr<nsILoadContext> chromeContext = do_QueryInterface(chromeTreeItem);
   if (NS_WARN_IF(!chromeContext)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   chromeContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
+  chromeContext->SetRemoteSubframes(isFissionWindow);
 
   // Tabs opened from a content process can only open new windows
   // that will also run with out-of-process tabs.
   chromeContext->SetRemoteTabs(true);
 
   MaybeDisablePersistence(aFeatures, chromeTreeOwner);
 
   SizeSpec sizeSpec;
@@ -1031,30 +1037,34 @@ nsresult nsWindowWatcher::OpenWindowInte
       }
     }
   }
 
   // We rely on CalculateChromeFlags to decide whether remote (out-of-process)
   // tabs should be used.
   bool isRemoteWindow =
       !!(chromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
+  bool isFissionWindow =
+      !!(chromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW);
 
   if (isNewToplevelWindow) {
     nsCOMPtr<nsIDocShellTreeItem> childRoot;
     newDocShellItem->GetRootTreeItem(getter_AddRefs(childRoot));
     nsCOMPtr<nsILoadContext> childContext = do_QueryInterface(childRoot);
     if (childContext) {
       childContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
       childContext->SetRemoteTabs(isRemoteWindow);
+      childContext->SetRemoteSubframes(isFissionWindow);
     }
   } else if (windowIsNew) {
     nsCOMPtr<nsILoadContext> childContext = do_QueryInterface(newDocShellItem);
     if (childContext) {
       childContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
       childContext->SetRemoteTabs(isRemoteWindow);
+      childContext->SetRemoteSubframes(isFissionWindow);
     }
   }
 
   RefPtr<nsDocShellLoadState> loadState = aLoadState;
   if (uriToLoad && loadState) {
     // If a URI was passed to this function, open that, not what was passed in
     // the original LoadState. See Bug 1515433.
     loadState->SetURI(uriToLoad);
@@ -1750,16 +1760,29 @@ uint32_t nsWindowWatcher::CalculateChrom
   } else {
     remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag);
   }
 
   if (remote) {
     chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
   }
 
+  // Determine whether the window should have remote subframes
+  bool fission = Preferences::GetBool("fission.autostart");
+
+  if (fission) {
+    fission = !WinHasOption(aFeatures, "non-fission", 0, &presenceFlag);
+  } else {
+    fission = WinHasOption(aFeatures, "fission", 0, &presenceFlag);
+  }
+
+  if (fission) {
+    chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
+  }
+
   chromeFlags |= WinHasOption(aFeatures, "popup", 0, &presenceFlag)
                      ? nsIWebBrowserChrome::CHROME_WINDOW_POPUP
                      : 0;
 
   /* OK.
      Normal browser windows, in spite of a stated pattern of turning off
      all chrome not mentioned explicitly, will want the new OS chrome (window
      borders, titlebars, closebox) on, unless explicitly turned off.
--- a/uriloader/prefetch/OfflineCacheUpdateParent.cpp
+++ b/uriloader/prefetch/OfflineCacheUpdateParent.cpp
@@ -226,16 +226,26 @@ OfflineCacheUpdateParent::GetUseRemoteTa
 }
 
 NS_IMETHODIMP
 OfflineCacheUpdateParent::SetRemoteTabs(bool aUseRemoteTabs) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+OfflineCacheUpdateParent::GetUseRemoteSubframes(bool* aUseRemoteSubframes) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateParent::SetRemoteSubframes(bool aUseRemoteSubframes) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 OfflineCacheUpdateParent::GetIsInIsolatedMozBrowserElement(
     bool* aIsInIsolatedMozBrowserElement) {
   NS_ENSURE_TRUE(mLoadingPrincipal, NS_ERROR_UNEXPECTED);
   return mLoadingPrincipal->GetIsInIsolatedMozBrowserElement(
       aIsInIsolatedMozBrowserElement);
 }
 
 NS_IMETHODIMP
--- a/xpfe/appshell/nsAppShellService.cpp
+++ b/xpfe/appshell/nsAppShellService.cpp
@@ -699,47 +699,53 @@ nsresult nsAppShellService::JustCreateTo
       aIsHiddenWindow, aOpeningTab, aOpenerWindow, widgetInitData);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Enforce the Private Browsing autoStart pref first.
   bool isPrivateBrowsingWindow =
       Preferences::GetBool("browser.privatebrowsing.autostart");
   bool isUsingRemoteTabs = mozilla::BrowserTabsRemoteAutostart();
+  bool isUsingRemoteSubframes = Preferences::GetBool("fission.autostart");
 
   if (aChromeMask & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW) {
     // Caller requested a private window
     isPrivateBrowsingWindow = true;
   }
   if (aChromeMask & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW) {
     isUsingRemoteTabs = true;
   }
+  if (aChromeMask & nsIWebBrowserChrome::CHROME_FISSION_WINDOW) {
+    isUsingRemoteSubframes = true;
+  }
 
   nsCOMPtr<mozIDOMWindowProxy> 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.
     isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
   }
 
   if (parentContext) {
     isUsingRemoteTabs = parentContext->UseRemoteTabs();
+    isUsingRemoteSubframes = parentContext->UseRemoteSubframes();
   }
 
   nsCOMPtr<mozIDOMWindowProxy> 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);
+    thisContext->SetRemoteSubframes(isUsingRemoteSubframes);
   }
 
   window.forget(aResult);
   if (parent) parent->AddChildWindow(*aResult);
 
   if (center) rv = (*aResult)->Center(parent, parent ? false : true, false);
 
   return rv;