Bug 1562292: Part 1f - Implement BrowsingContext::LoadURI. r=nika
authorKris Maglione <maglione.k@gmail.com>
Thu, 01 Aug 2019 18:00:32 -0700
changeset 488066 40b61344f0da14f9700a75a8a1c67458e440b68a
parent 488065 3bffe760e2db5ff009f0d798cbb63b83a68de51b
child 488067 ce60426d7e3152c92c9df31e1657bee4dc928d1b
push id36435
push usercbrindusan@mozilla.com
push dateThu, 15 Aug 2019 09:46:49 +0000
treeherdermozilla-central@0db07ff50ab5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnika
bugs1562292
milestone70.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 1562292: Part 1f - Implement BrowsingContext::LoadURI. r=nika In order to do cross-process targeted window.open() and link click operations, we need a way to load URIs in the current DocShell of a BrowsingContext, whichever process it lives in. This patch does this in the simplest possible way, bouncing the URL, along with the target and accessor contexts, up to the parent and down to the current owning child process. It does some basic sanity checks in the parent, which should probably be expanded in the future, and should really ideally try to initiate the load in the parent as soon as possible. But for now, it does what we need. Differential Revision: https://phabricator.services.mozilla.com/D40497
docshell/base/BrowsingContext.cpp
docshell/base/BrowsingContext.h
dom/ipc/DOMTypes.ipdlh
dom/ipc/PWindowGlobal.ipdl
dom/ipc/WindowGlobalChild.cpp
dom/ipc/WindowGlobalChild.h
dom/ipc/WindowGlobalParent.cpp
dom/ipc/WindowGlobalParent.h
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -778,16 +778,39 @@ void BrowsingContext::Location(JSContext
   aError.MightThrowJSException();
   sSingleton.GetProxyObject(aCx, &mLocation, /* aTransplantTo = */ nullptr,
                             aLocation);
   if (!aLocation) {
     aError.StealExceptionFromJSContext(aCx);
   }
 }
 
+void BrowsingContext::LoadURI(BrowsingContext* aAccessor,
+                              nsDocShellLoadState* aLoadState) {
+  MOZ_DIAGNOSTIC_ASSERT(!IsDiscarded());
+  MOZ_DIAGNOSTIC_ASSERT(!aAccessor || !aAccessor->IsDiscarded());
+
+  if (mDocShell) {
+    mDocShell->LoadURI(aLoadState);
+  } else if (!aAccessor && XRE_IsParentProcess()) {
+    Unused << Canonical()->GetCurrentWindowGlobal()->SendLoadURIInChild(
+        aLoadState);
+  } else {
+    MOZ_DIAGNOSTIC_ASSERT(aAccessor);
+    MOZ_DIAGNOSTIC_ASSERT(aAccessor->Group() == Group());
+
+    nsCOMPtr<nsPIDOMWindowOuter> win(aAccessor->GetDOMWindow());
+    MOZ_DIAGNOSTIC_ASSERT(win);
+    if (WindowGlobalChild* wgc =
+            win->GetCurrentInnerWindow()->GetWindowGlobalChild()) {
+      wgc->SendLoadURI(this, aLoadState);
+    }
+  }
+}
+
 void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) {
   // FIXME We need to set mClosed, but only once we're sending the
   //       DOMWindowClose event (which happens in the process where the
   //       document for this browsing context is loaded).
   //       See https://bugzilla.mozilla.org/show_bug.cgi?id=1516343.
   if (ContentChild* cc = ContentChild::GetSingleton()) {
     cc->SendWindowClose(this, aCallerType == CallerType::System);
   } else if (ContentParent* cp = Canonical()->GetContentParent()) {
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -15,16 +15,17 @@
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDocShell.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 #include "nsILoadInfo.h"
 
+class nsDocShellLoadState;
 class nsGlobalWindowOuter;
 class nsIPrincipal;
 class nsOuterWindowProxy;
 class PickleIterator;
 
 namespace IPC {
 class Message;
 }  // namespace IPC
@@ -174,16 +175,21 @@ class BrowsingContext : public nsWrapper
 
   // Remove all children from the current BrowsingContext and cache
   // them to allow them to be attached again.
   void CacheChildren(bool aFromIPC = false);
 
   // Restore cached browsing contexts.
   void RestoreChildren(Children&& aChildren, bool aFromIPC = false);
 
+  // Triggers a load in the process which currently owns this BrowsingContext.
+  // aAccessor is the context which initiated the load, and may be null only for
+  // in-process BrowsingContexts.
+  void LoadURI(BrowsingContext* aAccessor, nsDocShellLoadState* aLoadState);
+
   // Determine if the current BrowsingContext was 'cached' by the logic in
   // CacheChildren.
   bool IsCached();
 
   // Check that this browsing context is targetable for navigations (i.e. that
   // it is neither closed, cached, nor discarded).
   bool IsTargetable();
 
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
 /* 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 "mozilla/GfxMessageUtils.h";
+include "mozilla/dom/DocShellMessageUtils.h";
 include "mozilla/layers/LayersMessageUtils.h";
 
 include IPCBlob;
 include IPCStream;
 include ProtocolTypes;
 
 using struct mozilla::void_t
   from "ipc/IPCMessageUtils.h";
--- a/dom/ipc/PWindowGlobal.ipdl
+++ b/dom/ipc/PWindowGlobal.ipdl
@@ -1,24 +1,27 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
 /* 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 "mozilla/dom/DocShellMessageUtils.h";
+
 include protocol PBrowser;
 include protocol PInProcess;
 include protocol PBrowserBridge;
 
 include DOMTypes;
 
 using JSWindowActorMessageKind from "mozilla/dom/JSWindowActor.h";
 using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
 using moveonly mozilla::gfx::PaintFragment from "mozilla/gfx/CrossProcessPaint.h";
 using nscolor from "nsColor.h";
+using refcounted class nsDocShellLoadState from "nsDocShellLoadState.h";
 
 namespace mozilla {
 namespace dom {
 
 struct JSWindowActorMessageMeta {
   nsString actorName;
   nsString messageName;
   uint64_t queryId;
@@ -45,20 +48,27 @@ child:
 
   async DrawSnapshot(IntRect? aRect, float aScale, nscolor aBackgroundColor) returns (PaintFragment retval);
 
   /**
    * Returns the serialized security info associated with this window.
    */
   async GetSecurityInfo() returns(nsCString? serializedSecInfo);
 
+  async LoadURIInChild(nsDocShellLoadState aLoadState);
+
 both:
   async RawMessage(JSWindowActorMessageMeta aMetadata, ClonedMessageData aData);
 
 parent:
+  // Load the given URI load state into the current owner process of the given
+  // BrowsingContext. aTargetBC must be in the same BrowsingContextGroup as this
+  // window global.
+  async LoadURI(BrowsingContext aTargetBC, nsDocShellLoadState aLoadState);
+
   /// Update the URI of the document in this WindowGlobal.
   async UpdateDocumentURI(nsIURI aUri);
 
   /// Send down initial document bit to the parent.
   async SetIsInitialDocument(bool aIsInitialDocument);
 
   /// Tell the parent if this WindowGlobal has any "beforeunload" event
   /// listeners.
--- a/dom/ipc/WindowGlobalChild.cpp
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -223,16 +223,22 @@ void WindowGlobalChild::Destroy() {
 
     for (auto& windowActor : windowActors) {
       windowActor->StartDestroy();
     }
     SendDestroy();
   }
 }
 
+mozilla::ipc::IPCResult WindowGlobalChild::RecvLoadURIInChild(
+    nsDocShellLoadState* aLoadState) {
+  mWindowGlobal->GetDocShell()->LoadURI(aLoadState);
+  return IPC_OK();
+}
+
 static nsresult ChangeFrameRemoteness(WindowGlobalChild* aWgc,
                                       BrowsingContext* aBc,
                                       const nsString& aRemoteType,
                                       uint64_t aPendingSwitchId,
                                       BrowserBridgeChild** aBridge) {
   MOZ_ASSERT(XRE_IsContentProcess(), "This doesn't make sense in the parent");
 
   // Get the target embedder's FrameLoaderOwner, and make sure we're in the
--- a/dom/ipc/WindowGlobalChild.h
+++ b/dom/ipc/WindowGlobalChild.h
@@ -109,16 +109,18 @@ class WindowGlobalChild final : public W
  protected:
   const nsAString& GetRemoteType() override;
   JSWindowActor::Type GetSide() override { return JSWindowActor::Type::Child; }
 
   // IPC messages
   mozilla::ipc::IPCResult RecvRawMessage(const JSWindowActorMessageMeta& aMeta,
                                          const ClonedMessageData& aData);
 
+  mozilla::ipc::IPCResult RecvLoadURIInChild(nsDocShellLoadState* aLoadState);
+
   mozilla::ipc::IPCResult RecvChangeFrameRemoteness(
       dom::BrowsingContext* aBc, const nsString& aRemoteType,
       uint64_t aPendingSwitchId, ChangeFrameRemotenessResolver&& aResolver);
 
   mozilla::ipc::IPCResult RecvDrawSnapshot(const Maybe<IntRect>& aRect,
                                            const float& aScale,
                                            const nscolor& aBackgroundColor,
                                            DrawSnapshotResolver&& aResolve);
--- a/dom/ipc/WindowGlobalParent.cpp
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -168,16 +168,48 @@ bool WindowGlobalParent::IsProcessRoot()
   auto* embedder = BrowsingContext()->GetEmbedderWindowGlobal();
   if (NS_WARN_IF(!embedder)) {
     return false;
   }
 
   return ContentParentId() != embedder->ContentParentId();
 }
 
+mozilla::ipc::IPCResult WindowGlobalParent::RecvLoadURI(
+    dom::BrowsingContext* aTargetBC,
+    nsDocShellLoadState* aLoadState) {
+  if (!aTargetBC || aTargetBC->IsDiscarded()) {
+    MOZ_LOG(
+        BrowsingContext::GetLog(), LogLevel::Debug,
+        ("ParentIPC: Trying to send a message with dead or detached context"));
+    return IPC_OK();
+  }
+
+  // FIXME: For cross-process loads, we should double check CanAccess() for the
+  // source browsing context in the parent process.
+
+  if (aTargetBC->Group() != BrowsingContext()->Group()) {
+    return IPC_FAIL(this, "Illegal cross-group BrowsingContext load");
+  }
+
+  // FIXME: We should really initiate the load in the parent before bouncing
+  // back down to the child.
+
+  WindowGlobalParent* wgp = aTargetBC->Canonical()->GetCurrentWindowGlobal();
+  if (!wgp) {
+    MOZ_LOG(
+        BrowsingContext::GetLog(), LogLevel::Debug,
+        ("ParentIPC: Target BrowsingContext has no WindowGlobalParent"));
+    return IPC_OK();
+  }
+
+  Unused << wgp->SendLoadURIInChild(aLoadState);
+  return IPC_OK();
+}
+
 IPCResult WindowGlobalParent::RecvUpdateDocumentURI(nsIURI* aURI) {
   // XXX(nika): Assert that the URI change was one which makes sense (either
   // about:blank -> a real URI, or a legal push/popstate URI change?)
   mDocumentURI = aURI;
   return IPC_OK();
 }
 
 IPCResult WindowGlobalParent::RecvSetHasBeforeUnload(bool aHasBeforeUnload) {
--- a/dom/ipc/WindowGlobalParent.h
+++ b/dom/ipc/WindowGlobalParent.h
@@ -134,16 +134,18 @@ class WindowGlobalParent final : public 
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
  protected:
   const nsAString& GetRemoteType() override;
   JSWindowActor::Type GetSide() override { return JSWindowActor::Type::Parent; }
 
   // IPC messages
+  mozilla::ipc::IPCResult RecvLoadURI(dom::BrowsingContext* aTargetBC,
+                                      nsDocShellLoadState* aLoadState);
   mozilla::ipc::IPCResult RecvUpdateDocumentURI(nsIURI* aURI);
   mozilla::ipc::IPCResult RecvSetIsInitialDocument(bool aIsInitialDocument) {
     mIsInitialDocument = aIsInitialDocument;
     return IPC_OK();
   }
   mozilla::ipc::IPCResult RecvSetHasBeforeUnload(bool aHasBeforeUnload);
   mozilla::ipc::IPCResult RecvBecomeCurrentWindowGlobal();
   mozilla::ipc::IPCResult RecvDestroy();