Bug 1540839 - Add ability to preserve browsing contexts between FrameLoaders; r=nika
☠☠ backed out by 0d1fd6730856 ☠ ☠
authorKyle Machulis <kyle@nonpolynomial.com>
Wed, 03 Apr 2019 15:40:28 -0700
changeset 473697 f1c35e8e84f6218ea00f81fd5fd849cdd1fc79e5
parent 473696 22a3a17e5a419109cb17d83d1803ae652145b6a7
child 473698 27492a30286c2cc22509e7ccdd84ff22f1425841
push id36010
push userapavel@mozilla.com
push dateTue, 14 May 2019 04:11:16 +0000
treeherdermozilla-central@e0a622476b77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnika
bugs1540839
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 1540839 - Add ability to preserve browsing contexts between FrameLoaders; r=nika When changing processes and therefore destroying/rebuilding frameloaders, add ability to keep the browsing context around and add it to the new frameloader. Differential Revision: https://phabricator.services.mozilla.com/D26267
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
dom/base/nsFrameLoader.cpp
dom/base/nsFrameLoader.h
dom/base/nsFrameLoaderOwner.cpp
dom/ipc/BrowserBridgeParent.cpp
dom/ipc/BrowserBridgeParent.h
dom/ipc/BrowserChild.cpp
dom/ipc/BrowserChild.h
dom/ipc/BrowserParent.cpp
dom/ipc/BrowserParent.h
dom/ipc/PBrowser.ipdl
dom/ipc/PBrowserBridge.ipdl
dom/ipc/WindowGlobalParent.cpp
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -380,17 +380,18 @@ nsDocShell::nsDocShell(BrowsingContext* 
       mIsPrintingOrPP(false),
       mSavingOldViewer(false),
       mDynamicallyCreated(false),
       mAffectPrivateSessionLifetime(true),
       mInvisible(false),
       mHasLoadedNonBlankURI(false),
       mBlankTiming(false),
       mTitleValidForCurrentURI(false),
-      mIsFrame(false) {
+      mIsFrame(false),
+      mSkipBrowsingContextDetachOnDestroy(false) {
   mHistoryID.m0 = 0;
   mHistoryID.m1 = 0;
   mHistoryID.m2 = 0;
   AssertOriginAttributesMatchPrivateBrowsing();
 
   nsContentUtils::GenerateUUIDInPlace(mHistoryID);
 
   if (gDocShellCount++ == 0) {
@@ -5031,17 +5032,21 @@ nsDocShell::Destroy() {
   if (mSessionHistory) {
     // We want to destroy these content viewers now rather than
     // letting their destruction wait for the session history
     // entries to get garbage collected.  (Bug 488394)
     mSessionHistory->EvictLocalContentViewers();
     mSessionHistory = nullptr;
   }
 
-  mBrowsingContext->Detach();
+  // This will be skipped in cases where we want to preserve the browsing
+  // context between loads.
+  if (!mSkipBrowsingContextDetachOnDestroy) {
+    mBrowsingContext->Detach();
+  }
 
   SetTreeOwner(nullptr);
 
   mBrowserChild = nullptr;
 
   mChromeEventHandler = nullptr;
 
   mOnePermittedSandboxedNavigator = nullptr;
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -400,16 +400,20 @@ class nsDocShell final : public nsDocLoa
    */
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   nsresult InternalLoad(nsDocShellLoadState* aLoadState,
                         nsIDocShell** aDocShell, nsIRequest** aRequest);
 
   // Clear the document's storage access flag if needed.
   void MaybeClearStorageAccessFlag();
 
+  void SkipBrowsingContextDetach() {
+    mSkipBrowsingContextDetachOnDestroy = true;
+  }
+
  private:  // member functions
   friend class nsDSURIContentListener;
   friend class FramingChecker;
   friend class OnLinkClickEvent;
   friend class nsIDocShell;
   friend class mozilla::dom::BrowsingContext;
 
   // It is necessary to allow adding a timeline marker wherever a docshell
@@ -1200,11 +1204,16 @@ class nsDocShell final : public nsDocLoa
   // We will check the innerWin's timing before creating a new one
   // in MaybeInitTiming()
   bool mBlankTiming : 1;
 
   // This flag indicates when the title is valid for the current URI.
   bool mTitleValidForCurrentURI : 1;
 
   bool mIsFrame : 1;
+
+  // If mSkipBrowsingContextDetachOnDestroy is set to true, then when the
+  // docshell is destroyed, the browsing context will not be detached. This is
+  // for cases where we want to preserve the BC for future use.
+  bool mSkipBrowsingContextDetachOnDestroy : 1;
 };
 
 #endif /* nsDocShell_h__ */
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -359,34 +359,39 @@ nsFrameLoader* nsFrameLoader::Create(Ele
 
   RefPtr<BrowsingContext> context = CreateBrowsingContext(aOwner, aOpener);
   NS_ENSURE_TRUE(context, nullptr);
   return new nsFrameLoader(aOwner, context, aNetworkCreated);
 }
 
 /* static */
 nsFrameLoader* nsFrameLoader::Create(
-    mozilla::dom::Element* aOwner,
+    mozilla::dom::Element* aOwner, BrowsingContext* aPreservedBrowsingContext,
     const mozilla::dom::RemotenessOptions& aOptions) {
   NS_ENSURE_TRUE(aOwner, nullptr);
   // This version of Create is only called for Remoteness updates, so we can
   // assume we need a FrameLoader here and skip the check in the other Create.
 
   bool hasOpener =
       aOptions.mOpener.WasPassed() && !aOptions.mOpener.Value().IsNull();
   MOZ_ASSERT(!aOptions.mRemoteType.WasPassed() ||
                  aOptions.mRemoteType.Value().IsVoid() || !hasOpener,
              "Cannot pass aOpener for a remote frame!");
 
   // This seems slightly unwieldy.
   RefPtr<BrowsingContext> opener;
   if (hasOpener) {
     opener = aOptions.mOpener.Value().Value().get();
   }
-  RefPtr<BrowsingContext> context = CreateBrowsingContext(aOwner, opener);
+  RefPtr<BrowsingContext> context;
+  if (aPreservedBrowsingContext) {
+    context = aPreservedBrowsingContext;
+  } else {
+    context = CreateBrowsingContext(aOwner, opener);
+  }
   NS_ENSURE_TRUE(context, nullptr);
   return new nsFrameLoader(aOwner, context, aOptions);
 }
 
 void nsFrameLoader::LoadFrame(bool aOriginalSrc) {
   if (NS_WARN_IF(!mOwnerContent)) {
     return;
   }
@@ -3485,8 +3490,27 @@ ProcessMessageManager* nsFrameLoader::Ge
 };
 
 JSObject* nsFrameLoader::WrapObject(JSContext* cx,
                                     JS::Handle<JSObject*> aGivenProto) {
   JS::RootedObject result(cx);
   FrameLoader_Binding::Wrap(cx, this, this, aGivenProto, &result);
   return result;
 }
+
+void nsFrameLoader::SkipBrowsingContextDetach() {
+  if (IsRemoteFrame()) {
+    // OOP Browser - Go directly over Browser Parent
+    if (mBrowserParent) {
+      Unused << mBrowserParent->SendSkipBrowsingContextDetach();
+    }
+    // OOP IFrame - Through Browser Bridge Parent, set on browser child
+    else if (mBrowserBridgeChild) {
+      Unused << mBrowserBridgeChild->SendSkipBrowsingContextDetach();
+    }
+    return;
+  }
+
+  // In process
+  RefPtr<nsDocShell> docshell = GetDocShell();
+  MOZ_ASSERT(docshell);
+  docshell->SkipBrowsingContextDetach();
+}
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -90,27 +90,29 @@ typedef struct _GtkWidget GtkWidget;
 class nsFrameLoader final : public nsStubMutationObserver,
                             public mozilla::dom::ipc::MessageManagerCallback,
                             public nsWrapperCache {
   friend class AutoResetInShow;
   friend class AutoResetInFrameSwap;
   typedef mozilla::dom::PBrowserParent PBrowserParent;
   typedef mozilla::dom::Document Document;
   typedef mozilla::dom::BrowserParent BrowserParent;
+  typedef mozilla::dom::BrowsingContext BrowsingContext;
   typedef mozilla::layout::RenderFrame RenderFrame;
 
  public:
   // Called by Frame Elements to create a new FrameLoader.
   static nsFrameLoader* Create(mozilla::dom::Element* aOwner,
                                mozilla::dom::BrowsingContext* aOpener,
                                bool aNetworkCreated);
 
   // Called by nsFrameLoaderOwner::ChangeRemoteness when switching out
   // FrameLoaders.
   static nsFrameLoader* Create(mozilla::dom::Element* aOwner,
+                               BrowsingContext* aPreservedBrowsingContext,
                                const mozilla::dom::RemotenessOptions& aOptions);
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_FRAMELOADER_IID)
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsFrameLoader)
 
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
@@ -380,16 +382,18 @@ class nsFrameLoader final : public nsStu
   // public because a callback needs these.
   RefPtr<mozilla::dom::ChromeMessageSender> mMessageManager;
   RefPtr<mozilla::dom::InProcessBrowserChildMessageManager>
       mChildMessageManager;
 
   virtual JSObject* WrapObject(JSContext* cx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
+  void SkipBrowsingContextDetach();
+
  private:
   nsFrameLoader(mozilla::dom::Element* aOwner,
                 mozilla::dom::BrowsingContext* aBrowsingContext,
                 bool aNetworkCreated);
   nsFrameLoader(mozilla::dom::Element* aOwner,
                 mozilla::dom::BrowsingContext* aBrowsingContext,
                 const mozilla::dom::RemotenessOptions& aOptions);
   ~nsFrameLoader();
--- a/dom/base/nsFrameLoaderOwner.cpp
+++ b/dom/base/nsFrameLoaderOwner.cpp
@@ -27,28 +27,35 @@ nsFrameLoaderOwner::GetBrowsingContext()
   if (mFrameLoader) {
     return mFrameLoader->GetBrowsingContext();
   }
   return nullptr;
 }
 
 void nsFrameLoaderOwner::ChangeRemoteness(
     const mozilla::dom::RemotenessOptions& aOptions, mozilla::ErrorResult& rv) {
+  RefPtr<BrowsingContext> bc;
   // If we already have a Frameloader, destroy it.
   if (mFrameLoader) {
+    bc = mFrameLoader->GetBrowsingContext();
+
+    // TODO pass in Cross-Origin-Load-Policy rules
+    mFrameLoader->SkipBrowsingContextDetach();
+
     mFrameLoader->Destroy();
     mFrameLoader = nullptr;
   }
 
   // In this case, we're not reparenting a frameloader, we're just destroying
   // our current one and creating a new one, so we can use ourselves as the
   // owner.
   RefPtr<Element> owner = do_QueryObject(this);
   MOZ_ASSERT(owner);
-  mFrameLoader = nsFrameLoader::Create(owner, aOptions);
+  mFrameLoader = nsFrameLoader::Create(owner, bc, aOptions);
+
   if (NS_WARN_IF(!mFrameLoader)) {
     return;
   }
 
   if (aOptions.mPendingSwitchID.WasPassed()) {
     mFrameLoader->ResumeLoad(aOptions.mPendingSwitchID.Value());
   } else {
     mFrameLoader->LoadFrame(false);
@@ -65,14 +72,13 @@ void nsFrameLoaderOwner::ChangeRemotenes
       fm->ActivateRemoteFrameIfNeeded(*owner);
     }
   }
 
   // Assuming this element is a XULFrameElement, once we've reset our
   // FrameLoader, fire an event to act like we've recreated ourselves, similar
   // to what XULFrameElement does after rebinding to the tree.
   // ChromeOnlyDispatch is turns on to make sure this isn't fired into content.
-  (new mozilla::AsyncEventDispatcher(owner,
-                                     NS_LITERAL_STRING("XULFrameLoaderCreated"),
-                                     mozilla::CanBubble::eYes,
-                                     mozilla::ChromeOnlyDispatch::eYes))
+  (new mozilla::AsyncEventDispatcher(
+       owner, NS_LITERAL_STRING("XULFrameLoaderCreated"),
+       mozilla::CanBubble::eYes, mozilla::ChromeOnlyDispatch::eYes))
       ->RunDOMEventWhenSafe();
 }
--- a/dom/ipc/BrowserBridgeParent.cpp
+++ b/dom/ipc/BrowserBridgeParent.cpp
@@ -177,16 +177,21 @@ IPCResult BrowserBridgeParent::RecvDispa
   layers::InputAPZContext context(
       layers::ScrollableLayerGuid(event.mLayersId, 0,
                                   layers::ScrollableLayerGuid::NULL_SCROLL_ID),
       0, nsEventStatus_eIgnore);
   mBrowserParent->SendRealMouseEvent(event);
   return IPC_OK();
 }
 
+IPCResult BrowserBridgeParent::RecvSkipBrowsingContextDetach() {
+  mBrowserParent->SkipBrowsingContextDetach();
+  return IPC_OK();
+}
+
 IPCResult BrowserBridgeParent::RecvActivate() {
   mBrowserParent->Activate();
   return IPC_OK();
 }
 
 IPCResult BrowserBridgeParent::RecvDeactivate() {
   mBrowserParent->Deactivate();
   return IPC_OK();
--- a/dom/ipc/BrowserBridgeParent.h
+++ b/dom/ipc/BrowserBridgeParent.h
@@ -54,16 +54,18 @@ class BrowserBridgeParent : public PBrow
                                            const LayersObserverEpoch& aEpoch);
 
   mozilla::ipc::IPCResult RecvNavigateByKey(const bool& aForward,
                                             const bool& aForDocumentNavigation);
 
   mozilla::ipc::IPCResult RecvDispatchSynthesizedMouseEvent(
       const WidgetMouseEvent& aEvent);
 
+  mozilla::ipc::IPCResult RecvSkipBrowsingContextDetach();
+
   mozilla::ipc::IPCResult RecvActivate();
 
   mozilla::ipc::IPCResult RecvDeactivate();
 
   mozilla::ipc::IPCResult RecvSetIsUnderHiddenEmbedderElement(
       const bool& aIsUnderHiddenEmbedderElement);
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
--- a/dom/ipc/BrowserChild.cpp
+++ b/dom/ipc/BrowserChild.cpp
@@ -1047,16 +1047,27 @@ BrowserChild::~BrowserChild() {
   nsCOMPtr<nsIWebBrowser> webBrowser = do_QueryInterface(WebNavigation());
   if (webBrowser) {
     webBrowser->SetContainerWindow(nullptr);
   }
 
   mozilla::DropJSObjects(this);
 }
 
+mozilla::ipc::IPCResult BrowserChild::RecvSkipBrowsingContextDetach() {
+  nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+  if (!docShell) {
+    return IPC_OK();
+  }
+  RefPtr<nsDocShell> docshell = nsDocShell::Cast(docShell);
+  MOZ_ASSERT(docshell);
+  docshell->SkipBrowsingContextDetach();
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult BrowserChild::RecvLoadURL(const nsCString& aURI,
                                                   const ShowInfo& aInfo) {
   if (!mDidLoadURLInit) {
     mDidLoadURLInit = true;
     if (!InitBrowserChildMessageManager()) {
       return IPC_FAIL_NO_REASON(this);
     }
 
--- a/dom/ipc/BrowserChild.h
+++ b/dom/ipc/BrowserChild.h
@@ -550,16 +550,17 @@ class BrowserChild final : public Browse
       const mozilla::NativeEventData& aKeyEventData, const bool& aIsConsumed);
 
   mozilla::ipc::IPCResult RecvPrint(const uint64_t& aOuterWindowID,
                                     const PrintData& aPrintData);
 
   mozilla::ipc::IPCResult RecvUpdateNativeWindowHandle(
       const uintptr_t& aNewHandle);
 
+  virtual mozilla::ipc::IPCResult RecvSkipBrowsingContextDetach() override;
   /**
    * Native widget remoting protocol for use with windowed plugins with e10s.
    */
   PPluginWidgetChild* AllocPPluginWidgetChild();
 
   bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor);
 
 #ifdef XP_WIN
--- a/dom/ipc/BrowserParent.cpp
+++ b/dom/ipc/BrowserParent.cpp
@@ -3739,16 +3739,22 @@ BrowserParent::StopApzAutoscroll(nsViewI
                                gfxUtils::GetContentRenderRoot());
 
       widget->StopAsyncAutoscroll(guid);
     }
   }
   return NS_OK;
 }
 
+void BrowserParent::SkipBrowsingContextDetach() {
+  RefPtr<nsFrameLoader> fl = GetFrameLoader();
+  MOZ_ASSERT(fl);
+  fl->SkipBrowsingContextDetach();
+}
+
 mozilla::ipc::IPCResult BrowserParent::RecvLookUpDictionary(
     const nsString& aText, nsTArray<FontRange>&& aFontRangeArray,
     const bool& aIsVertical, const LayoutDeviceIntPoint& aPoint) {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return IPC_OK();
   }
 
--- a/dom/ipc/BrowserParent.h
+++ b/dom/ipc/BrowserParent.h
@@ -673,16 +673,18 @@ class BrowserParent final : public PBrow
   void LiveResizeStarted() override;
   void LiveResizeStopped() override;
 
   void SetReadyToHandleInputEvents() { mIsReadyToHandleInputEvents = true; }
   bool IsReadyToHandleInputEvents() { return mIsReadyToHandleInputEvents; }
 
   void NavigateByKey(bool aForward, bool aForDocumentNavigation);
 
+  void SkipBrowsingContextDetach();
+
  protected:
   bool ReceiveMessage(
       const nsString& aMessage, bool aSync, ipc::StructuredCloneData* aData,
       mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
       nsTArray<ipc::StructuredCloneData>* aJSONRetVal = nullptr);
 
   mozilla::ipc::IPCResult RecvAsyncAuthPrompt(const nsCString& aUri,
                                               const nsString& aRealm,
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -943,16 +943,17 @@ child:
      */
     async SetWidgetNativeData(WindowsHandle aHandle);
 
     /**
      * Requests the content blocking log, which is sent back in response.
      */
     async GetContentBlockingLog() returns(nsCString log, bool success);
 
+    async SkipBrowsingContextDetach();
 parent:
     /** Records a history visit. */
     async VisitURI(URIParams aURI, URIParams? aLastVisitedURI,
                    uint32_t aFlags);
 
     /** Fetches the visited status for an array of URIs (Android-only). */
     async QueryVisitedState(URIParams[] aURIs);
 
--- a/dom/ipc/PBrowserBridge.ipdl
+++ b/dom/ipc/PBrowserBridge.ipdl
@@ -69,12 +69,14 @@ parent:
   /**
    * Sending an activate message moves focus to the iframe.
    */
   async Activate();
 
   async Deactivate();
 
   async SetIsUnderHiddenEmbedderElement(bool aIsUnderHiddenEmbedderElement);
+
+  async SkipBrowsingContextDetach();
 };
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/ipc/WindowGlobalParent.cpp
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -77,20 +77,16 @@ void WindowGlobalParent::Init(const Wind
   ContentParentId processId(0);
   if (!mInProcess) {
     processId = static_cast<ContentParent*>(Manager()->Manager())->ChildID();
   }
 
   mBrowsingContext = CanonicalBrowsingContext::Cast(aInit.browsingContext());
   MOZ_ASSERT(mBrowsingContext);
 
-  // XXX(nika): This won't be the case soon, but for now this is a good
-  // assertion as we can't switch processes. We should relax this eventually.
-  MOZ_ASSERT(mBrowsingContext->IsOwnedByProcess(processId));
-
   // Attach ourself to the browsing context.
   mBrowsingContext->RegisterWindowGlobal(this);
 
   // If there is no current window global, assume we're about to become it
   // optimistically.
   if (!mBrowsingContext->GetCurrentWindowGlobal()) {
     mBrowsingContext->SetCurrentWindowGlobal(this);
   }