Bug 1522713 - Allow updating of Frameloader Remoteness via FrameLoaderOwner; r=nika
authorKyle Machulis <kyle@nonpolynomial.com>
Thu, 14 Mar 2019 01:25:07 +0000
changeset 521820 bade3af814f6ff96bfac9a35e07b6c0dd1fb8f79
parent 521819 d63e984effadd381fc8a21b188a33e4a8f9caead
child 521821 0bc302ab1f25b03e78fd4bc2443bcb0d450bfaa2
push id10867
push userdvarga@mozilla.com
push dateThu, 14 Mar 2019 15:20:45 +0000
treeherdermozilla-beta@abad13547875 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnika
bugs1522713
milestone67.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 1522713 - Allow updating of Frameloader Remoteness via FrameLoaderOwner; r=nika Adds a method for to nsFrameLoaderOwner destroying and rebuilding a FrameLoader in order to facilitate a process switch. Method works without requiring that the work be done in the frontend. Depends on D22789 Differential Revision: https://phabricator.services.mozilla.com/D22790
dom/base/nsFrameLoader.cpp
dom/base/nsFrameLoader.h
dom/base/nsFrameLoaderOwner.cpp
dom/base/nsFrameLoaderOwner.h
dom/webidl/MozFrameLoaderOwner.webidl
layout/generic/nsSubDocumentFrame.cpp
layout/generic/nsSubDocumentFrame.h
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -77,16 +77,17 @@
 #include "mozilla/GuardObjects.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/NullPrincipal.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/ChromeMessageSender.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/FrameLoaderBinding.h"
+#include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
 #include "mozilla/gfx/CrossProcessPaint.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layout/RenderFrame.h"
 #include "mozilla/ServoCSSParser.h"
 #include "mozilla/ServoStyleSet.h"
 #include "nsGenericHTMLFrameElement.h"
 #include "GeckoProfiler.h"
 
@@ -181,23 +182,57 @@ nsFrameLoader::nsFrameLoader(Element* aO
       mRemoteBrowserShown(false),
       mIsRemoteFrame(false),
       mObservingOwnerContent(false) {
   mIsRemoteFrame = ShouldUseRemoteProcess();
   MOZ_ASSERT(!mIsRemoteFrame || !aOpener,
              "Cannot pass aOpener for a remote frame!");
 }
 
+nsFrameLoader::nsFrameLoader(Element* aOwner,
+                             const mozilla::dom::RemotenessOptions& aOptions)
+    : mOwnerContent(aOwner),
+      mDetachedSubdocFrame(nullptr),
+      mOpener(nullptr),
+      mRemoteBrowser(nullptr),
+      mChildID(0),
+      mDepthTooGreat(false),
+      mIsTopLevelContent(false),
+      mDestroyCalled(false),
+      mNeedsAsyncDestroy(false),
+      mInSwap(false),
+      mInShow(false),
+      mHideCalled(false),
+      mNetworkCreated(false),
+      mLoadingOriginalSrc(false),
+      mRemoteBrowserShown(false),
+      mIsRemoteFrame(false),
+      mObservingOwnerContent(false) {
+  if (aOptions.mRemoteType.WasPassed() &&
+      (!aOptions.mRemoteType.Value().IsVoid())) {
+    mIsRemoteFrame = true;
+  }
+  bool hasOpener =
+      aOptions.mOpener.WasPassed() && !aOptions.mOpener.Value().IsNull();
+  MOZ_ASSERT(!mIsRemoteFrame || !hasOpener,
+             "Cannot pass aOpener for a remote frame!");
+  if (hasOpener) {
+    // This seems slightly unwieldy.
+    mOpener = aOptions.mOpener.Value().Value().get()->GetDOMWindow();
+  }
+}
+
 nsFrameLoader::~nsFrameLoader() {
   if (mMessageManager) {
     mMessageManager->Disconnect();
   }
   MOZ_RELEASE_ASSERT(mDestroyCalled);
 }
 
+/* static */
 nsFrameLoader* nsFrameLoader::Create(Element* aOwner,
                                      nsPIDOMWindowOuter* aOpener,
                                      bool aNetworkCreated) {
   NS_ENSURE_TRUE(aOwner, nullptr);
   Document* doc = aOwner->OwnerDoc();
 
   // We never create nsFrameLoaders for elements in resource documents.
   //
@@ -222,16 +257,26 @@ nsFrameLoader* nsFrameLoader::Create(Ele
   NS_ENSURE_TRUE(!doc->IsResourceDoc() &&
                      ((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) ||
                       doc->IsStaticDocument()),
                  nullptr);
 
   return new nsFrameLoader(aOwner, aOpener, aNetworkCreated);
 }
 
+/* static */
+nsFrameLoader* nsFrameLoader::Create(
+    mozilla::dom::Element* aOwner,
+    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.
+  return new nsFrameLoader(aOwner, aOptions);
+}
+
 void nsFrameLoader::LoadFrame(bool aOriginalSrc) {
   if (NS_WARN_IF(!mOwnerContent)) {
     return;
   }
 
   nsAutoString src;
   nsCOMPtr<nsIPrincipal> principal;
 
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -55,16 +55,18 @@ class ContentParent;
 class InProcessTabChildMessageManager;
 class MessageSender;
 class PBrowserParent;
 class ProcessMessageManager;
 class Promise;
 class TabParent;
 class MutableTabContext;
 class BrowserBridgeChild;
+class RemoteFrameChild;
+struct RemotenessOptions;
 
 namespace ipc {
 class StructuredCloneData;
 }  // namespace ipc
 
 }  // namespace dom
 
 namespace layout {
@@ -90,19 +92,25 @@ class nsFrameLoader final : public nsStu
   friend class AutoResetInShow;
   friend class AutoResetInFrameSwap;
   typedef mozilla::dom::PBrowserParent PBrowserParent;
   typedef mozilla::dom::Document Document;
   typedef mozilla::dom::TabParent TabParent;
   typedef mozilla::layout::RenderFrame RenderFrame;
 
  public:
-  static nsFrameLoader* Create(
-      mozilla::dom::Element* aOwner, nsPIDOMWindowOuter* aOpener,
-      bool aNetworkCreated);
+  // Called by Frame Elements to create a new FrameLoader.
+  static nsFrameLoader* Create(mozilla::dom::Element* aOwner,
+                               nsPIDOMWindowOuter* aOpener,
+                               bool aNetworkCreated);
+
+  // Called by nsFrameLoaderOwner::ChangeRemoteness when switching out
+  // FrameLoaders.
+  static nsFrameLoader* Create(mozilla::dom::Element* aOwner,
+                               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
   nsresult CheckForRecursiveLoad(nsIURI* aURI);
@@ -352,16 +360,18 @@ class nsFrameLoader final : public nsStu
   RefPtr<mozilla::dom::InProcessTabChildMessageManager> mChildMessageManager;
 
   virtual JSObject* WrapObject(JSContext* cx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
  private:
   nsFrameLoader(mozilla::dom::Element* aOwner, nsPIDOMWindowOuter* aOpener,
                 bool aNetworkCreated);
+  nsFrameLoader(mozilla::dom::Element* aOwner,
+                const mozilla::dom::RemotenessOptions& aOptions);
   ~nsFrameLoader();
 
   void SetOwnerContent(mozilla::dom::Element* aContent);
 
   bool ShouldUseRemoteProcess();
 
   /**
    * Is this a frame loader for an isolated <iframe mozbrowser>?
--- a/dom/base/nsFrameLoaderOwner.cpp
+++ b/dom/base/nsFrameLoaderOwner.cpp
@@ -1,16 +1,55 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 #include "nsFrameLoaderOwner.h"
 #include "nsFrameLoader.h"
+#include "mozilla/dom/FrameLoaderBinding.h"
 
 already_AddRefed<nsFrameLoader> nsFrameLoaderOwner::GetFrameLoader() {
   return do_AddRef(mFrameLoader);
 }
 
 void nsFrameLoaderOwner::SetFrameLoader(nsFrameLoader* aNewFrameLoader) {
   mFrameLoader = aNewFrameLoader;
 }
+
+void nsFrameLoaderOwner::ChangeRemoteness(
+    const mozilla::dom::RemotenessOptions& aOptions, mozilla::ErrorResult& rv) {
+  // If we already have a Frameloader, destroy it.
+  if (mFrameLoader) {
+    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->LoadFrame(false);
+
+  // Now that we've got a new FrameLoader, we need to reset our
+  // nsSubDocumentFrame use the new FrameLoader. We just unset the frameloader
+  // here, and expect that the subdocument frame will pick up the new
+  // frameloader lazily.
+  nsIFrame* ourFrame = owner->GetPrimaryFrame();
+  if (ourFrame) {
+    nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
+    if (ourFrameFrame) {
+      ourFrameFrame->UnsetFrameLoader();
+    }
+  }
+
+  // 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 AsyncEventDispatcher(owner, NS_LITERAL_STRING("XULFrameLoaderCreated"),
+                            CanBubble::eYes, ChromeOnlyDispatch::eYes))
+    ->RunDOMEventWhenSafe();
+
+}
--- a/dom/base/nsFrameLoaderOwner.h
+++ b/dom/base/nsFrameLoaderOwner.h
@@ -3,16 +3,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 #ifndef nsFrameLoaderOwner_h_
 #define nsFrameLoaderOwner_h_
 
 class nsFrameLoader;
+namespace mozilla {
+class ErrorResult;
+namespace dom {
+struct RemotenessOptions;
+}
+}  // namespace mozilla
 
 // IID for the FrameLoaderOwner interface
 #define NS_FRAMELOADEROWNER_IID                      \
   {                                                  \
     0x1b4fd25c, 0x2e57, 0x11e9, {                    \
       0x9e, 0x5a, 0x5b, 0x86, 0xe9, 0x89, 0xa5, 0xc0 \
     }                                                \
   }
@@ -27,16 +33,25 @@ class nsFrameLoader;
 class nsFrameLoaderOwner : public nsISupports {
  public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_FRAMELOADEROWNER_IID)
 
   nsFrameLoaderOwner() = default;
   already_AddRefed<nsFrameLoader> GetFrameLoader();
   void SetFrameLoader(nsFrameLoader* aNewFrameLoader);
 
+  // Destroy (if it exists) and recreate our frameloader, based on new
+  // remoteness requirements. This should follow the same path as
+  // tabbrowser.js's updateBrowserRemoteness, including running the same logic
+  // and firing the same events as unbinding a XULBrowserElement from the tree.
+  // However, this method is available from backend and does not manipulate the
+  // DOM.
+  void ChangeRemoteness(const mozilla::dom::RemotenessOptions& aOptions,
+                        mozilla::ErrorResult& rv);
+
  protected:
   virtual ~nsFrameLoaderOwner() = default;
   RefPtr<nsFrameLoader> mFrameLoader;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsFrameLoaderOwner, NS_FRAMELOADEROWNER_IID)
 
 #endif  // nsFrameLoaderOwner_h_
--- a/dom/webidl/MozFrameLoaderOwner.webidl
+++ b/dom/webidl/MozFrameLoaderOwner.webidl
@@ -1,20 +1,29 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/.
  */
 
+dictionary RemotenessOptions {
+  DOMString? remoteType;
+  FrameLoader? sameProcessAsFrameLoader;
+  WindowProxy? opener;
+};
+
 [NoInterfaceObject]
 interface MozFrameLoaderOwner {
   [ChromeOnly]
   readonly attribute FrameLoader? frameLoader;
 
   [ChromeOnly, Throws]
   void presetOpenerWindow(WindowProxy? window);
 
   [ChromeOnly, Throws]
   void swapFrameLoaders(XULFrameElement aOtherLoaderOwner);
 
   [ChromeOnly, Throws]
   void swapFrameLoaders(HTMLIFrameElement aOtherLoaderOwner);
+
+  [ChromeOnly, Throws]
+  void changeRemoteness(optional RemotenessOptions aOptions);
 };
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -809,20 +809,21 @@ void nsSubDocumentFrame::Reflow(nsPresCo
       NS_FRAME_TRACE_CALLS,
       ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%s",
        aDesiredSize.Width(), aDesiredSize.Height(), ToString(aStatus).c_str()));
 
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
 }
 
 bool nsSubDocumentFrame::ReflowFinished() {
-  if (mFrameLoader) {
+  RefPtr<nsFrameLoader> frameloader = FrameLoader();
+  if (frameloader) {
     AutoWeakFrame weakFrame(this);
 
-    mFrameLoader->UpdatePositionAndSize(this);
+    frameloader->UpdatePositionAndSize(this);
 
     if (weakFrame.IsAlive()) {
       // Make sure that we can post a reflow callback in the future.
       mPostedReflowCallback = false;
     }
   } else {
     mPostedReflowCallback = false;
   }
--- a/layout/generic/nsSubDocumentFrame.h
+++ b/layout/generic/nsSubDocumentFrame.h
@@ -108,16 +108,17 @@ class nsSubDocumentFrame final : public 
   bool PassPointerEventsToChildren();
 
   void MaybeShowViewer() {
     if (!mDidCreateDoc && !mCallingShow) {
       ShowViewer();
     }
   }
 
+  void UnsetFrameLoader() { mFrameLoader = nullptr; }
   nsFrameLoader* FrameLoader() const;
 
  protected:
   friend class AsyncFrameInit;
 
   // Helper method to look up the HTML marginwidth & marginheight attributes.
   mozilla::CSSIntSize GetMarginAttributes();