From 54f24a9804d687358e3400a9a7bbfa5a2e83027c Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Wed, 29 Aug 2018 18:00:05 -0400 Subject: [PATCH 01/17] Bug 1487249 - Part 1: Allow MessageChannel objects to be created within a thread, r=mccr8 To create a more generic interface for interacting both within the main thread of the parent process and between the parent and child processes, it would be nice to support IPDL actors within the main thread of the parent process. This requires the underlying MessageChannel actor to support intra-thread links. This change adds support for intra-thread links to the underlying MessageChannel object using ThreadLink, and an extra boolean flag. Differential Revision: https://phabricator.services.mozilla.com/D4620 --- ipc/glue/MessageChannel.cpp | 46 ++++++++++++++++++++++++++++++++++++- ipc/glue/MessageChannel.h | 28 +++++++++++++++++++--- ipc/glue/ProtocolUtils.cpp | 5 ++++ ipc/glue/ProtocolUtils.h | 9 ++++++++ 4 files changed, 84 insertions(+), 4 deletions(-) diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 1eb0976c10fc..88ed5e39d8b3 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -584,7 +584,8 @@ MessageChannel::MessageChannel(const char* aName, IToplevelProtocol* aListener) mPeerPidSet(false), mPeerPid(-1), mIsPostponingSends(false), - mBuildIDsConfirmedMatch(false) { + mBuildIDsConfirmedMatch(false), + mIsSameThreadChannel(false) { MOZ_COUNT_CTOR(ipc::MessageChannel); #ifdef OS_WIN @@ -901,6 +902,38 @@ void MessageChannel::CommonThreadOpenInit(MessageChannel* aTargetChan, mSide = aSide; } +bool MessageChannel::OpenOnSameThread(MessageChannel* aTargetChan, + mozilla::ipc::Side aSide) { + CommonThreadOpenInit(aTargetChan, aSide); + + Side oppSide = UnknownSide; + switch (aSide) { + case ChildSide: + oppSide = ParentSide; + break; + case ParentSide: + oppSide = ChildSide; + break; + case UnknownSide: + break; + } + mIsSameThreadChannel = true; + + // XXX(nika): Avoid setting up a monitor for same thread channels? We + // shouldn't need it. + mMonitor = new RefCountedMonitor(); + + mChannelState = ChannelOpening; + aTargetChan->CommonThreadOpenInit(this, oppSide); + + aTargetChan->mIsSameThreadChannel = true; + aTargetChan->mMonitor = mMonitor; + + mChannelState = ChannelConnected; + aTargetChan->mChannelState = ChannelConnected; + return true; +} + bool MessageChannel::Echo(Message* aMsg) { UniquePtr msg(aMsg); AssertWorkerThread(); @@ -1377,6 +1410,8 @@ bool MessageChannel::Send(Message* aMsg, Message* aReply) { // Sanity checks. AssertWorkerThread(); mMonitor->AssertNotCurrentThreadOwns(); + MOZ_RELEASE_ASSERT(!mIsSameThreadChannel, + "sync send over same-thread channel will deadlock!"); #ifdef OS_WIN SyncStackFrame frame(this, false); @@ -1585,6 +1620,8 @@ bool MessageChannel::Call(Message* aMsg, Message* aReply) { UniquePtr msg(aMsg); AssertWorkerThread(); mMonitor->AssertNotCurrentThreadOwns(); + MOZ_RELEASE_ASSERT(!mIsSameThreadChannel, + "intr call send over same-thread channel will deadlock!"); #ifdef OS_WIN SyncStackFrame frame(this, true); @@ -2300,6 +2337,9 @@ bool MessageChannel::WaitForSyncNotify(bool /* aHandleWindowsMessages */) { } #endif + MOZ_RELEASE_ASSERT(!mIsSameThreadChannel, + "Wait on same-thread channel will deadlock!"); + TimeDuration timeout = (kNoTimeout == mTimeoutMs) ? TimeDuration::Forever() : TimeDuration::FromMilliseconds(mTimeoutMs); @@ -2580,6 +2620,10 @@ void MessageChannel::SynchronouslyClose() { AssertWorkerThread(); mMonitor->AssertCurrentThreadOwns(); mLink->SendClose(); + + MOZ_RELEASE_ASSERT(!mIsSameThreadChannel || ChannelClosed == mChannelState, + "same-thread channel failed to synchronously close?"); + while (ChannelClosed != mChannelState) mMonitor->Wait(); } diff --git a/ipc/glue/MessageChannel.h b/ipc/glue/MessageChannel.h index ced5d011974c..0951478e303d 100644 --- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -164,6 +164,15 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver { bool Open(MessageChannel* aTargetChan, nsIEventTarget* aEventTarget, Side aSide); + // "Open" a connection to an actor on the current thread. + // + // Returns true if the transport layer was successfully connected, + // i.e., mChannelState == ChannelConnected. + // + // Same-thread channels may not perform synchronous or blocking message + // sends, to avoid deadlocks. + bool OpenOnSameThread(MessageChannel* aTargetChan, Side aSide); + // Close the underlying transport channel. void Close(); @@ -530,10 +539,19 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver { "not on worker thread!"); } - // The "link" thread is either the I/O thread (ProcessLink) or the - // other actor's work thread (ThreadLink). In either case, it is - // NOT our worker thread. + // The "link" thread is either the I/O thread (ProcessLink), the other + // actor's work thread (ThreadLink), or the worker thread (same-thread + // channels). void AssertLinkThread() const { + if (mIsSameThreadChannel) { + // If we're a same-thread channel, we have to be on our worker + // thread. + AssertWorkerThread(); + return; + } + + // If we aren't a same-thread channel, our "link" thread is _not_ our + // worker thread! MOZ_ASSERT(mWorkerThread, "Channel hasn't been opened yet"); MOZ_RELEASE_ASSERT(mWorkerThread != GetCurrentVirtualThread(), "on worker thread but should not be!"); @@ -827,6 +845,10 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver { std::vector> mPostponedSends; bool mBuildIDsConfirmedMatch; + + // If this is true, both ends of this message channel have event targets + // on the same thread. + bool mIsSameThreadChannel; }; void CancelCPOWs(); diff --git a/ipc/glue/ProtocolUtils.cpp b/ipc/glue/ProtocolUtils.cpp index c8c85f00c842..cbdac2566213 100644 --- a/ipc/glue/ProtocolUtils.cpp +++ b/ipc/glue/ProtocolUtils.cpp @@ -627,6 +627,11 @@ bool IToplevelProtocol::OpenWithAsyncPid(mozilla::ipc::Transport* aTransport, return GetIPCChannel()->Open(aTransport, aThread, aSide); } +bool IToplevelProtocol::OpenOnSameThread(MessageChannel* aChannel, Side aSide) { + SetOtherProcessId(base::GetCurrentProcId()); + return GetIPCChannel()->OpenOnSameThread(aChannel, aSide); +} + void IToplevelProtocol::Close() { GetIPCChannel()->Close(); } void IToplevelProtocol::SetReplyTimeoutMs(int32_t aTimeoutMs) { diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h index ab8b1560fd6e..180714000ff1 100644 --- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -482,6 +482,15 @@ class IToplevelProtocol : public IProtocol { MessageLoop* aThread = nullptr, mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide); + // Open a toplevel actor such that both ends of the actor's channel are on + // the same thread. This method should be called on the thread to perform + // the link. + // + // WARNING: Attempting to send a sync or intr message on the same thread + // will crash. + bool OpenOnSameThread(MessageChannel* aChannel, + mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide); + void Close(); void SetReplyTimeoutMs(int32_t aTimeoutMs); From 8791515adbca1e4009eed318f6b687b92135051f Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Wed, 29 Aug 2018 18:18:04 -0400 Subject: [PATCH 02/17] Bug 1487249 - Part 2: Add a new PInProcess actor to manage intra-thread actors, r=mccr8 This will be useful as a basis for asynchronous actors which would like to exist both when crossing the process boundary (managed by PContent), and when displaying an in-process window. Differential Revision: https://phabricator.services.mozilla.com/D4622 --- ipc/glue/InProcessChild.cpp | 13 +++ ipc/glue/InProcessChild.h | 54 +++++++++ ipc/glue/InProcessImpl.cpp | 210 +++++++++++++++++++++++++++++++++++ ipc/glue/InProcessParent.cpp | 15 +++ ipc/glue/InProcessParent.h | 60 ++++++++++ ipc/glue/PInProcess.ipdl | 23 ++++ ipc/glue/moz.build | 6 + 7 files changed, 381 insertions(+) create mode 100644 ipc/glue/InProcessChild.cpp create mode 100644 ipc/glue/InProcessChild.h create mode 100644 ipc/glue/InProcessImpl.cpp create mode 100644 ipc/glue/InProcessParent.cpp create mode 100644 ipc/glue/InProcessParent.h create mode 100644 ipc/glue/PInProcess.ipdl diff --git a/ipc/glue/InProcessChild.cpp b/ipc/glue/InProcessChild.cpp new file mode 100644 index 000000000000..d0c2e9a98392 --- /dev/null +++ b/ipc/glue/InProcessChild.cpp @@ -0,0 +1,13 @@ +/* -*- 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 "mozilla/ipc/InProcessChild.h" + +namespace mozilla { +namespace ipc { + +} // namespace ipc +} // namespace mozilla diff --git a/ipc/glue/InProcessChild.h b/ipc/glue/InProcessChild.h new file mode 100644 index 000000000000..902bd9e5127f --- /dev/null +++ b/ipc/glue/InProcessChild.h @@ -0,0 +1,54 @@ +/* -*- 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/. */ + +#ifndef mozilla_ipc_InProcessChild_h +#define mozilla_ipc_InProcessChild_h + +#include "mozilla/ipc/PInProcessChild.h" +#include "mozilla/StaticPtr.h" + +namespace mozilla { +namespace ipc { + +class InProcessParent; + +/** + * The `InProcessChild` class represents the child half of a main-thread to + * main-thread actor. + * + * The `PInProcess` actor should be used as an alternate manager to `PContent` + * for async actors which want to communicate uniformly between Content->Chrome + * and Chrome->Chrome situations. + */ +class InProcessChild : public PInProcessChild +{ +public: + friend class InProcessParent; + + NS_INLINE_DECL_REFCOUNTING(InProcessChild) + + // Get the singleton instance of this actor. + static InProcessChild* Singleton(); + + // Get the parent side of the in-process child actor |aActor|. If |aActor| is + // not an in-process actor, or is not connected, this method will return + // |nullptr|. + static IProtocol* ParentActorFor(IProtocol* aActor); + +private: + // NOTE: PInProcess lifecycle management is declared as staic methods and + // state on InProcessParent, and implemented in InProcessImpl.cpp. + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + virtual void DeallocPInProcessChild() override; + ~InProcessChild() = default; + + static StaticRefPtr sSingleton; +}; + +} // namespace ipc +} // namespace mozilla + +#endif // defined(mozilla_ipc_InProcessChild_h) diff --git a/ipc/glue/InProcessImpl.cpp b/ipc/glue/InProcessImpl.cpp new file mode 100644 index 000000000000..cc7e765c724c --- /dev/null +++ b/ipc/glue/InProcessImpl.cpp @@ -0,0 +1,210 @@ +/* -*- 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 "mozilla/ipc/InProcessParent.h" +#include "mozilla/ipc/InProcessChild.h" + +// This file contains the implementation of core InProcess lifecycle management +// facilities. + +namespace mozilla { +namespace ipc { + +StaticRefPtr InProcessParent::sSingleton; +StaticRefPtr InProcessChild::sSingleton; +bool InProcessParent::sShutdown = false; + + +////////////////////////////////////////// +// InProcess actor lifecycle management // +////////////////////////////////////////// + +/* static */ InProcessChild* +InProcessChild::Singleton() { + MOZ_ASSERT(NS_IsMainThread()); + + if (!sSingleton) { + InProcessParent::Startup(); + } + return sSingleton; +} + +/* static */ InProcessParent* +InProcessParent::Singleton() { + MOZ_ASSERT(NS_IsMainThread()); + + if (!sSingleton) { + InProcessParent::Startup(); + } + return sSingleton; +} + +/* static */ void +InProcessParent::Startup() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (sShutdown) { + NS_WARNING("Could not get in-process actor while shutting down!"); + return; + } + + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (!obs) { + sShutdown = true; + NS_WARNING("Failed to get nsIObserverService for in-process actor"); + return; + } + + RefPtr parent = new InProcessParent(); + RefPtr child = new InProcessChild(); + + // Observe the shutdown event to close & clean up after ourselves. + nsresult rv = obs->AddObserver(parent, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + // Link the two actors + if (!child->OpenOnSameThread(parent->GetIPCChannel(), ChildSide)) { + MOZ_CRASH("Failed to open InProcessChild!"); + } + + parent->SetOtherProcessId(base::GetCurrentProcId()); + + // Create references held by the IPC layer which will be freed in + // DeallocPInProcess{Parent,Child}. + parent.get()->AddRef(); + child.get()->AddRef(); + + // Stash global references to fetch the other side of the reference. + InProcessParent::sSingleton = parent.forget(); + InProcessChild::sSingleton = child.forget(); +} + + +/* static */ void +InProcessParent::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!sSingleton || sShutdown) { + return; + } + + sShutdown = true; + + RefPtr parent = sSingleton; + InProcessParent::sSingleton = nullptr; + InProcessChild::sSingleton = nullptr; + + // Calling `Close` on the actor will cause the `Dealloc` methods to be called, + // freeing the remaining references. + parent->Close(); +} + +NS_IMETHODIMP +InProcessParent::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) +{ + MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)); + InProcessParent::Shutdown(); + return NS_OK; +} + +void +InProcessParent::ActorDestroy(ActorDestroyReason aWhy) +{ + InProcessParent::Shutdown(); +} + +void +InProcessChild::ActorDestroy(ActorDestroyReason aWhy) +{ + InProcessParent::Shutdown(); +} + +void +InProcessParent::DeallocPInProcessParent() +{ + MOZ_ASSERT(!InProcessParent::sSingleton); + Release(); // Release the reference taken in InProcessParent::Startup. +} + +void +InProcessChild::DeallocPInProcessChild() +{ + MOZ_ASSERT(!InProcessChild::sSingleton); + Release(); // Release the reference taken in InProcessParent::Startup. +} + +//////////////////////////////// +// In-Process Actor Utilities // +//////////////////////////////// + +// Helper method for implementing ParentActorFor and ChildActorFor. +static IProtocol* +GetOtherInProcessActor(IProtocol* aActor) +{ + MOZ_ASSERT(aActor->GetSide() != UnknownSide, "bad unknown side"); + + // Discover the manager of aActor which is PInProcess. + IProtocol* current = aActor; + while (current) { + if (current->GetProtocolTypeId() == PInProcessMsgStart) { + break; // Found the correct actor. + } + current = current->Manager(); + } + if (!current) { + return nullptr; // Not a PInProcess actor, return |nullptr| + } + + MOZ_ASSERT(current->GetSide() == aActor->GetSide(), "side changed?"); + MOZ_ASSERT_IF(aActor->GetSide() == ParentSide, + current == InProcessParent::Singleton()); + MOZ_ASSERT_IF(aActor->GetSide() == ChildSide, + current == InProcessChild::Singleton()); + + // Check whether this is InProcessParent or InProcessChild, and get the other + // side's toplevel actor. + IProtocol* otherRoot = nullptr; + if (aActor->GetSide() == ParentSide) { + otherRoot = InProcessChild::Singleton(); + } else { + otherRoot = InProcessParent::Singleton(); + } + if (NS_WARN_IF(!otherRoot)) { + return nullptr; + } + + // Look up the actor on the other side, and return it. + IProtocol* otherActor = otherRoot->Lookup(aActor->Id()); + if (otherActor) { + MOZ_ASSERT(otherActor->GetSide() != UnknownSide, "bad unknown side"); + MOZ_ASSERT(otherActor->GetSide() != aActor->GetSide(), "Wrong side!"); + MOZ_ASSERT(otherActor->GetProtocolTypeId() == aActor->GetProtocolTypeId(), + "Wrong type of protocol!"); + } + + return otherActor; +} + +/* static */ IProtocol* +InProcessParent::ChildActorFor(IProtocol* aActor) +{ + MOZ_ASSERT(aActor && aActor->GetSide() == ParentSide); + return GetOtherInProcessActor(aActor); +} + +/* static */ IProtocol* +InProcessChild::ParentActorFor(IProtocol* aActor) +{ + MOZ_ASSERT(aActor && aActor->GetSide() == ChildSide); + return GetOtherInProcessActor(aActor); +} + +} // namespace ipc +} // namespace mozilla diff --git a/ipc/glue/InProcessParent.cpp b/ipc/glue/InProcessParent.cpp new file mode 100644 index 000000000000..72e8bd019cdc --- /dev/null +++ b/ipc/glue/InProcessParent.cpp @@ -0,0 +1,15 @@ +/* -*- 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 "mozilla/ipc/InProcessParent.h" + +namespace mozilla { +namespace ipc { + +NS_IMPL_ISUPPORTS(InProcessParent, nsIObserver) + +} // namespace ipc +} // namespace mozilla \ No newline at end of file diff --git a/ipc/glue/InProcessParent.h b/ipc/glue/InProcessParent.h new file mode 100644 index 000000000000..2bce679ca759 --- /dev/null +++ b/ipc/glue/InProcessParent.h @@ -0,0 +1,60 @@ +/* -*- 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/. */ + +#ifndef mozilla_ipc_InProcessParent_h +#define mozilla_ipc_InProcessParent_h + +#include "mozilla/ipc/PInProcessParent.h" +#include "mozilla/StaticPtr.h" + +namespace mozilla { +namespace ipc { + +class InProcessChild; + +/** + * The `InProcessParent` class represents the parent half of a main-thread to + * main-thread actor. + * + * The `PInProcess` actor should be used as an alternate manager to `PContent` + * for async actors which want to communicate uniformly between Content->Chrome + * and Chrome->Chrome situations. + */ +class InProcessParent : public nsIObserver + , public PInProcessParent +{ +public: + friend class InProcessChild; + + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + // Get the singleton instance of this actor. + static InProcessParent* Singleton(); + + // Get the child side of the in-process child actor |aActor|. If |aActor| is + // not an in-process actor, or is not connected, this method will return + // |nullptr|. + static IProtocol* ChildActorFor(IProtocol* aActor); + +private: + // Lifecycle management is implemented in InProcessImpl.cpp + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + virtual void DeallocPInProcessParent() override; + ~InProcessParent() = default; + + static void Startup(); + static void Shutdown(); + + static StaticRefPtr sSingleton; + static bool sShutdown; +}; + + +} // namespace ipc +} // namespace mozilla + +#endif // defined(mozilla_ipc_InProcessParent_h) diff --git a/ipc/glue/PInProcess.ipdl b/ipc/glue/PInProcess.ipdl new file mode 100644 index 000000000000..c00e9d2effbb --- /dev/null +++ b/ipc/glue/PInProcess.ipdl @@ -0,0 +1,23 @@ +/* -*- 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/. */ + +namespace mozilla { +namespace ipc { + +/** + * PInProcess is intended for use as an alternative actor manager to PContent + * for async actors which want to be used uniformly in both Content->Chrome and + * Chrome->Chrome circumstances. + * + * `mozilla::ipc::InProcess{Parent, Child}::Singleton()` should be used to get + * an instance of this actor. + */ +async protocol PInProcess +{ +}; + +} // namespace ipc +} // namespace mozilla diff --git a/ipc/glue/moz.build b/ipc/glue/moz.build index 1851b3b2a03b..f53dedb93a4e 100644 --- a/ipc/glue/moz.build +++ b/ipc/glue/moz.build @@ -27,6 +27,8 @@ EXPORTS.mozilla.ipc += [ 'FileDescriptorSetParent.h', 'FileDescriptorUtils.h', 'GeckoChildProcessHost.h', + 'InProcessChild.h', + 'InProcessParent.h', 'InputStreamUtils.h', 'IOThreadChild.h', 'IPCStreamAlloc.h', @@ -147,6 +149,9 @@ UNIFIED_SOURCES += [ 'CrashReporterMetadataShmem.cpp', 'FileDescriptor.cpp', 'FileDescriptorUtils.cpp', + 'InProcessChild.cpp', + 'InProcessImpl.cpp', + 'InProcessParent.cpp', 'InputStreamUtils.cpp', 'IPCMessageUtils.cpp', 'IPCStreamChild.cpp', @@ -208,6 +213,7 @@ IPDL_SOURCES = [ 'PBackgroundTest.ipdl', 'PChildToParentStream.ipdl', 'PFileDescriptorSet.ipdl', + 'PInProcess.ipdl', 'PParentToChildStream.ipdl', 'ProtocolTypes.ipdlh', 'URIParams.ipdlh', From 4e07a0c5f9bce24a978c61351aa0580a5616e920 Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Wed, 29 Aug 2018 18:21:25 -0400 Subject: [PATCH 03/17] Bug 1487249 - Part 3: Add the WindowGlobal actor representing a single window global, r=bzbarsky This actor can be used for communicating with individual frames, without depending on walking the tree in the content process. This is not yet complete. No tests have been written for it, the WindowGlobalParent objects need to be exposed to chrome JS, and a form of JS actors should be installed under them. In addition, BrowsingContextChrome objects should be updated to allow access to the current WindowGlobalParent in that context. Differential Revision: https://phabricator.services.mozilla.com/D4623 --- dom/base/nsDocument.cpp | 9 +++ dom/base/nsGlobalWindowInner.cpp | 16 +++++ dom/base/nsPIDOMWindow.h | 11 ++++ dom/ipc/ContentChild.cpp | 9 ++- dom/ipc/DOMTypes.ipdlh | 8 +++ dom/ipc/PBrowser.ipdl | 9 +++ dom/ipc/PWindowGlobal.ipdl | 35 +++++++++++ dom/ipc/TabChild.cpp | 12 ++++ dom/ipc/TabChild.h | 5 ++ dom/ipc/TabParent.cpp | 19 ++++++ dom/ipc/TabParent.h | 8 +++ dom/ipc/WindowGlobalChild.cpp | 81 ++++++++++++++++++++++++ dom/ipc/WindowGlobalChild.h | 65 +++++++++++++++++++ dom/ipc/WindowGlobalParent.cpp | 103 +++++++++++++++++++++++++++++++ dom/ipc/WindowGlobalParent.h | 95 ++++++++++++++++++++++++++++ dom/ipc/moz.build | 5 ++ ipc/glue/InProcessChild.cpp | 18 ++++++ ipc/glue/InProcessChild.h | 12 ++++ ipc/glue/InProcessImpl.cpp | 2 + ipc/glue/InProcessParent.cpp | 26 ++++++++ ipc/glue/InProcessParent.h | 16 +++++ ipc/glue/PInProcess.ipdl | 12 ++++ 22 files changed, 573 insertions(+), 3 deletions(-) create mode 100644 dom/ipc/PWindowGlobal.ipdl create mode 100644 dom/ipc/WindowGlobalChild.cpp create mode 100644 dom/ipc/WindowGlobalChild.h create mode 100644 dom/ipc/WindowGlobalParent.cpp create mode 100644 dom/ipc/WindowGlobalParent.h diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 25d4b4d9d14d..b3c8e76bd57e 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -271,6 +271,8 @@ #include "mozilla/DocLoadingTimelineMarker.h" +#include "mozilla/dom/WindowGlobalChild.h" + #include "nsISpeculativeConnect.h" #include "mozilla/MediaManager.h" @@ -2897,6 +2899,13 @@ void nsIDocument::SetDocumentURI(nsIURI* aURI) { if (!equalBases) { RefreshLinkHrefs(); } + + // Tell our WindowGlobalParent that the document's URI has been changed. + nsPIDOMWindowInner* inner = GetInnerWindow(); + WindowGlobalChild* wgc = inner ? inner->GetWindowGlobalChild() : nullptr; + if (wgc) { + Unused << wgc->SendUpdateDocumentURI(mDocumentURI); + } } static void GetFormattedTimeString(PRTime aTime, diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp index 66dc41af86bf..bb66bbfbbbc2 100644 --- a/dom/base/nsGlobalWindowInner.cpp +++ b/dom/base/nsGlobalWindowInner.cpp @@ -260,6 +260,8 @@ #include "mozilla/dom/ClientSource.h" #include "mozilla/dom/ClientState.h" +#include "mozilla/dom/WindowGlobalChild.h" + // Apple system headers seem to have a check() macro. #ifdef check class nsIScriptTimeoutHandler; @@ -1266,6 +1268,11 @@ void nsGlobalWindowInner::FreeInnerObjects(bool aForDocumentOpen) { } } + if (mWindowGlobalChild && !mWindowGlobalChild->IsClosed()) { + mWindowGlobalChild->Send__delete__(mWindowGlobalChild); + } + mWindowGlobalChild = nullptr; + mIntlUtils = nullptr; } @@ -1635,6 +1642,15 @@ void nsGlobalWindowInner::InnerSetNewDocument(JSContext* aCx, // out of sync. ClearDocumentDependentSlots(aCx); + // FIXME: Currently, devtools can crete a fallback webextension window global + // in the content process which does not have a corresponding TabChild actor. + // This means we have no actor to be our parent. (Bug 1498293) + MOZ_DIAGNOSTIC_ASSERT(!mWindowGlobalChild, + "Shouldn't have created WindowGlobalChild yet!"); + if (XRE_IsParentProcess() || mTabChild) { + mWindowGlobalChild = WindowGlobalChild::Create(this); + } + #ifdef DEBUG mLastOpenedURI = aDocument->GetDocumentURI(); #endif diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index 490a7f9f4cf9..983270c99b62 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -68,6 +68,7 @@ class ServiceWorker; class ServiceWorkerDescriptor; class Timeout; class TimeoutManager; +class WindowGlobalChild; class CustomElementRegistry; enum class CallerType : uint32_t; } // namespace dom @@ -378,6 +379,10 @@ class nsPIDOMWindowInner : public mozIDOMWindow { return mDoc; } + mozilla::dom::WindowGlobalChild* GetWindowGlobalChild() { + return mWindowGlobalChild; + } + virtual PopupControlState GetPopupControlState() const = 0; // Determine if the window is suspended or frozen. Outer windows @@ -695,6 +700,12 @@ class nsPIDOMWindowInner : public mozIDOMWindow { // also set as permissions, but it could happen that we need to access them // synchronously in this context, and for this, we need a copy here. nsTArray mStorageAccessGranted; + + // The WindowGlobalChild actor for this window. + // + // This will be non-null during the full lifetime of the window, initialized + // during SetNewDocument, and cleared during FreeInnerObjects. + RefPtr mWindowGlobalChild; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 4e3501534f8b..1ff1a8cad0f3 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -890,9 +890,6 @@ nsresult ContentChild::ProvideWindowCommon( TabContext newTabContext = aTabOpener ? *aTabOpener : TabContext(); RefPtr newChild = new TabChild(this, tabId, tabGroup, newTabContext, aChromeFlags); - if (NS_FAILED(newChild->Init(aParent))) { - return NS_ERROR_ABORT; - } if (aTabOpener) { MOZ_ASSERT(ipcContext->type() == IPCTabContext::TPopupIPCTabContext); @@ -908,6 +905,12 @@ nsresult ContentChild::ProvideWindowCommon( RefPtr(newChild).forget().take(), tabId, TabId(0), *ipcContext, aChromeFlags, GetID(), IsForBrowser()); + // Now that |newChild| has had its IPC link established, call |Init| to set it + // up. + if (NS_FAILED(newChild->Init(aParent))) { + return NS_ERROR_ABORT; + } + nsCOMPtr parentTopInnerWindow; if (aParent) { nsCOMPtr parentTopWindow = diff --git a/dom/ipc/DOMTypes.ipdlh b/dom/ipc/DOMTypes.ipdlh index c792f7fec573..f0af011e0e3f 100644 --- a/dom/ipc/DOMTypes.ipdlh +++ b/dom/ipc/DOMTypes.ipdlh @@ -26,6 +26,8 @@ using CSSSize from "Units.h"; using mozilla::LayoutDeviceIntPoint from "Units.h"; using hal::ScreenOrientation from "mozilla/HalScreenConfiguration.h"; using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h"; +using refcounted class nsIPrincipal from "mozilla/dom/PermissionMessageUtils.h"; +using mozilla::dom::BrowsingContextId from "mozilla/dom/ipc/IdType.h"; namespace mozilla { @@ -183,5 +185,11 @@ struct PerformanceInfo CategoryDispatch[] items; }; +struct WindowGlobalInit +{ + nsIPrincipal principal; + BrowsingContextId browsingContextId; +}; + } // namespace dom } // namespace mozilla diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 4b27470a5fd2..05ed7186fe54 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -18,6 +18,7 @@ include protocol PParentToChildStream; include protocol PFileDescriptorSet; include protocol PIPCBlobInputStream; include protocol PPaymentRequest; +include protocol PWindowGlobal; include DOMTypes; include IPCBlob; @@ -86,6 +87,7 @@ using class mozilla::NativeEventData from "ipc/nsGUIEventIPC.h"; using mozilla::FontRange from "ipc/nsGUIEventIPC.h"; using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.h"; using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h"; +using mozilla::dom::BrowsingContextId from "mozilla/dom/ipc/IdType.h"; namespace mozilla { namespace dom { @@ -118,6 +120,7 @@ nested(upto inside_cpow) sync protocol PBrowser manages PIndexedDBPermissionRequest; manages PPluginWidget; manages PPaymentRequest; + manages PWindowGlobal; both: async AsyncMessage(nsString aMessage, CpowEntry[] aCpows, @@ -144,6 +147,12 @@ parent: async PPaymentRequest(); + /** + * Construct a new WindowGlobal actor for a window global in the given + * BrowsingContext and with the given principal. + */ + async PWindowGlobal(WindowGlobalInit init); + /** * Sends an NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW to be adopted by the * widget's shareable window on the chrome side. Only used on Windows. diff --git a/dom/ipc/PWindowGlobal.ipdl b/dom/ipc/PWindowGlobal.ipdl new file mode 100644 index 000000000000..5a78829f5e44 --- /dev/null +++ b/dom/ipc/PWindowGlobal.ipdl @@ -0,0 +1,35 @@ +/* -*- 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 protocol PBrowser; +include protocol PInProcess; + +include DOMTypes; + +using refcounted class nsIURI from "mozilla/ipc/URIUtils.h"; + +namespace mozilla { +namespace dom { + +/** + * A PWindowGlobal actor has a lifetime matching that of a single Window Global, + * specifically a |nsGlobalWindowInner|. These actors will form a parent/child + * link either between the chrome/content process, or will be in-process, for + * documents which are loaded in the chrome process. + */ +async protocol PWindowGlobal +{ + manager PBrowser or PInProcess; + +parent: + /// Update the URI of the document in this WindowGlobal. + async UpdateDocumentURI(nsIURI aUri); + + async __delete__(); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index ba3d3fb33e56..bd4a5e420638 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -122,6 +122,7 @@ #include "mozilla/Telemetry.h" #include "nsDocShellLoadState.h" #include "nsWebBrowser.h" +#include "mozilla/dom/WindowGlobalChild.h" #ifdef XP_WIN #include "mozilla/plugins/PluginWidgetChild.h" @@ -3137,6 +3138,17 @@ bool TabChild::DeallocPPaymentRequestChild(PPaymentRequestChild* actor) { return true; } +PWindowGlobalChild* TabChild::AllocPWindowGlobalChild(const WindowGlobalInit&) { + MOZ_CRASH("We should never be manually allocating PWindowGlobalChild actors"); + return nullptr; +} + +bool TabChild::DeallocPWindowGlobalChild(PWindowGlobalChild* aActor) { + // This reference was added in WindowGlobalChild::Create. + static_cast(aActor)->Release(); + return true; +} + ScreenIntSize TabChild::GetInnerSize() { LayoutDeviceIntSize innerSize = RoundedToInt(mUnscaledInnerSize * mPuppetWidget->GetDefaultScale()); diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 916a4a10b029..f295240573f6 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -657,6 +657,11 @@ class TabChild final : public TabChildBase, protected: virtual ~TabChild(); + virtual PWindowGlobalChild* AllocPWindowGlobalChild( + const WindowGlobalInit& aInit) override; + + virtual bool DeallocPWindowGlobalChild(PWindowGlobalChild* aActor) override; + virtual mozilla::ipc::IPCResult RecvDestroy() override; virtual mozilla::ipc::IPCResult RecvSetDocShellIsActive( diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index c96f3dfc67fa..c47f09c6a8e8 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -100,6 +100,7 @@ #include "ProcessPriorityManager.h" #include "nsString.h" #include "IHistory.h" +#include "mozilla/dom/WindowGlobalParent.h" #ifdef XP_WIN #include "mozilla/plugins/PluginWidgetParent.h" @@ -988,6 +989,24 @@ bool TabParent::DeallocPIndexedDBPermissionRequestParent( aActor); } +IPCResult TabParent::RecvPWindowGlobalConstructor( + PWindowGlobalParent* aActor, const WindowGlobalInit& aInit) { + static_cast(aActor)->Init(); + return IPC_OK(); +} + +PWindowGlobalParent* TabParent::AllocPWindowGlobalParent( + const WindowGlobalInit& aInit) { + // Reference freed in DeallocPWindowGlobalParent. + return do_AddRef(new WindowGlobalParent(aInit, /* inproc */ false)).take(); +} + +bool TabParent::DeallocPWindowGlobalParent(PWindowGlobalParent* aActor) { + // Free reference from AllocPWindowGlobalParent. + static_cast(aActor)->Release(); + return true; +} + void TabParent::SendMouseEvent(const nsAString& aType, float aX, float aY, int32_t aButton, int32_t aClickCount, int32_t aModifiers, diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 41e3800ef453..f8eb2946bb47 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -305,6 +305,14 @@ class TabParent final : public PBrowserParent, */ a11y::DocAccessibleParent* GetTopLevelDocAccessible() const; + virtual PWindowGlobalParent* AllocPWindowGlobalParent( + const WindowGlobalInit& aInit) override; + + virtual bool DeallocPWindowGlobalParent(PWindowGlobalParent* aActor) override; + + virtual mozilla::ipc::IPCResult RecvPWindowGlobalConstructor( + PWindowGlobalParent* aActor, const WindowGlobalInit& aInit) override; + void LoadURL(nsIURI* aURI); void InitRendering(); diff --git a/dom/ipc/WindowGlobalChild.cpp b/dom/ipc/WindowGlobalChild.cpp new file mode 100644 index 000000000000..dd2026bb39ae --- /dev/null +++ b/dom/ipc/WindowGlobalChild.cpp @@ -0,0 +1,81 @@ +/* -*- 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/WindowGlobalChild.h" +#include "mozilla/ipc/InProcessChild.h" +#include "mozilla/dom/BrowsingContext.h" + +namespace mozilla { +namespace dom { + +WindowGlobalChild::WindowGlobalChild(nsGlobalWindowInner* aWindow, dom::BrowsingContext* aBrowsingContext) + : mWindowGlobal(aWindow) + , mBrowsingContext(aBrowsingContext) + , mIPCClosed(false) +{ +} + +already_AddRefed +WindowGlobalChild::Create(nsGlobalWindowInner* aWindow) +{ + nsCOMPtr principal = aWindow->GetPrincipal(); + MOZ_ASSERT(principal); + + RefPtr docshell = nsDocShell::Cast(aWindow->GetDocShell()); + MOZ_ASSERT(docshell); + + // Initalize our WindowGlobalChild object. + RefPtr bc = docshell->GetBrowsingContext(); + RefPtr wgc = new WindowGlobalChild(aWindow, bc); + + WindowGlobalInit init(principal, BrowsingContextId(wgc->BrowsingContext()->Id())); + + // Send the link constructor over PInProcessChild or PBrowser. + if (XRE_IsParentProcess()) { + InProcessChild* ipc = InProcessChild::Singleton(); + if (!ipc) { + return nullptr; + } + + // Note: ref is released in DeallocPWindowGlobalChild + ipc->SendPWindowGlobalConstructor(do_AddRef(wgc).take(), init); + } else { + RefPtr tabChild = TabChild::GetFrom(static_cast(aWindow)); + MOZ_ASSERT(tabChild); + + // Note: ref is released in DeallocPWindowGlobalChild + tabChild->SendPWindowGlobalConstructor(do_AddRef(wgc).take(), init); + } + + return wgc.forget(); +} + +already_AddRefed +WindowGlobalChild::GetOtherSide() +{ + if (mIPCClosed) { + return nullptr; + } + IProtocol* otherSide = InProcessChild::ParentActorFor(this); + return do_AddRef(static_cast(otherSide)); +} + +void +WindowGlobalChild::ActorDestroy(ActorDestroyReason aWhy) +{ + mIPCClosed = true; +} + +WindowGlobalChild::~WindowGlobalChild() +{ +} + +NS_IMPL_CYCLE_COLLECTION(WindowGlobalChild, mWindowGlobal, mBrowsingContext) +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WindowGlobalChild, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WindowGlobalChild, Release) + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/WindowGlobalChild.h b/dom/ipc/WindowGlobalChild.h new file mode 100644 index 000000000000..cce3f12e73f5 --- /dev/null +++ b/dom/ipc/WindowGlobalChild.h @@ -0,0 +1,65 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_WindowGlobalChild_h +#define mozilla_dom_WindowGlobalChild_h + +#include "mozilla/RefPtr.h" +#include "mozilla/dom/PWindowGlobalChild.h" + +class nsGlobalWindowInner; +class nsDocShell; + +namespace mozilla { +namespace dom { + +class BrowsingContext; +class WindowGlobalParent; + +/** + * Actor for a single nsGlobalWindowInner. This actor is used to communicate + * information to the parent process asynchronously. + */ +class WindowGlobalChild : public PWindowGlobalChild +{ +public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WindowGlobalChild) + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(WindowGlobalChild) + + dom::BrowsingContext* BrowsingContext() { return mBrowsingContext; } + nsGlobalWindowInner* WindowGlobal() { return mWindowGlobal; } + + // Has this actor been shut down + bool IsClosed() { return mIPCClosed; } + + // Check if this actor is managed by PInProcess, as-in the document is loaded + // in the chrome process. + bool IsInProcess() { return XRE_IsParentProcess(); } + + // Get the other side of this actor if it is an in-process actor. Returns + // |nullptr| if the actor has been torn down, or is not in-process. + already_AddRefed GetOtherSide(); + + // Create and initialize the WindowGlobalChild object. + static already_AddRefed + Create(nsGlobalWindowInner* aWindow); + +protected: + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + +private: + WindowGlobalChild(nsGlobalWindowInner* aWindow, dom::BrowsingContext* aBc); + ~WindowGlobalChild(); + + RefPtr mWindowGlobal; + RefPtr mBrowsingContext; + bool mIPCClosed; +}; + +} // namespace dom +} // namespace mozilla + +#endif // !defined(mozilla_dom_WindowGlobalChild_h) diff --git a/dom/ipc/WindowGlobalParent.cpp b/dom/ipc/WindowGlobalParent.cpp new file mode 100644 index 000000000000..c2fbfb81e73e --- /dev/null +++ b/dom/ipc/WindowGlobalParent.cpp @@ -0,0 +1,103 @@ +/* -*- 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/WindowGlobalParent.h" +#include "mozilla/ipc/InProcessParent.h" +#include "mozilla/dom/BrowsingContext.h" + +using namespace mozilla::ipc; + +namespace mozilla { +namespace dom { + +WindowGlobalParent::WindowGlobalParent(const WindowGlobalInit& aInit, + bool aInProcess) + : mBrowsingContextId(aInit.browsingContextId()) + , mDocumentPrincipal(aInit.principal()) + , mInProcess(aInProcess) + , mIPCClosed(false) +{ + MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), "Parent process only"); + MOZ_RELEASE_ASSERT(mDocumentPrincipal, "Must have a valid principal"); + MOZ_RELEASE_ASSERT(mBrowsingContextId != 0, "Must be made in BrowsingContext"); +} + +void +WindowGlobalParent::Init() +{ + MOZ_ASSERT(Manager(), "Should have a manager!"); + MOZ_ASSERT(!mFrameLoader, "Cannot Init() a WindowGlobalParent twice!"); + + // Determine what toplevel frame element our WindowGlobalParent is being + // embedded in. + RefPtr frameElement; + if (mInProcess) { + // In the in-process case, we can get it from the other side's + // WindowGlobalChild. + MOZ_ASSERT(Manager()->GetProtocolTypeId() == PInProcessMsgStart); + RefPtr otherSide = GetOtherSide(); + if (otherSide && otherSide->WindowGlobal()) { + // Get the toplevel window from the other side. + RefPtr docShell = nsDocShell::Cast(otherSide->WindowGlobal()->GetDocShell()); + if (docShell) { + docShell->GetTopFrameElement(getter_AddRefs(frameElement)); + } + } + } else { + // In the cross-process case, we can get the frame element from our manager. + MOZ_ASSERT(Manager()->GetProtocolTypeId() == PBrowserMsgStart); + frameElement = static_cast(Manager())->GetOwnerElement(); + } + + // Extract the nsFrameLoader from the current frame element. We may not have a + // nsFrameLoader if we are a chrome document. + nsCOMPtr flOwner = do_QueryInterface(frameElement); + if (flOwner) { + mFrameLoader = flOwner->GetFrameLoader(); + } +} + +already_AddRefed +WindowGlobalParent::BrowsingContext() +{ + return dom::BrowsingContext::Get(mBrowsingContextId); +} + +already_AddRefed +WindowGlobalParent::GetOtherSide() +{ + if (mIPCClosed) { + return nullptr; + } + IProtocol* otherSide = InProcessParent::ChildActorFor(this); + return do_AddRef(static_cast(otherSide)); +} + +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(); +} + +void +WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) +{ + mIPCClosed = true; +} + +WindowGlobalParent::~WindowGlobalParent() +{ +} + +NS_IMPL_CYCLE_COLLECTION(WindowGlobalParent, mFrameLoader) +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WindowGlobalParent, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WindowGlobalParent, Release) + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/WindowGlobalParent.h b/dom/ipc/WindowGlobalParent.h new file mode 100644 index 000000000000..7bc6786d4352 --- /dev/null +++ b/dom/ipc/WindowGlobalParent.h @@ -0,0 +1,95 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_WindowGlobalParent_h +#define mozilla_dom_WindowGlobalParent_h + +#include "mozilla/RefPtr.h" +#include "mozilla/dom/PWindowGlobalParent.h" + +class nsIPrincipal; +class nsIURI; +class nsFrameLoader; + +namespace mozilla { +namespace dom { + +class BrowsingContext; +class WindowGlobalChild; + +/** + * A handle in the parent process to a specific nsGlobalWindowInner object. + */ +class WindowGlobalParent final : public PWindowGlobalParent +{ +public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WindowGlobalParent) + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(WindowGlobalParent) + + // Has this actor been shut down + bool IsClosed() { return mIPCClosed; } + + // Check if this actor is managed by PInProcess, as-in the document is loaded + // in-process. + bool IsInProcess() { return mInProcess; } + + // Get the other side of this actor if it is an in-process actor. Returns + // |nullptr| if the actor has been torn down, or is not in-process. + already_AddRefed GetOtherSide(); + + // The principal of this WindowGlobal. This value will not change over the + // lifetime of the WindowGlobal object, even to reflect changes in + // |document.domain|. + nsIPrincipal* DocumentPrincipal() { return mDocumentPrincipal; } + + // The BrowsingContext which this WindowGlobal has been loaded into. + already_AddRefed BrowsingContext(); + + // Get the root nsFrameLoader object for the tree of BrowsingContext nodes + // which this WindowGlobal is a part of. This will be the nsFrameLoader + // holding the TabParent for remote tabs, and the root content frameloader for + // non-remote tabs. + nsFrameLoader* GetRootFrameLoader() { return mFrameLoader; } + + // The current URI which loaded in the document. + nsIURI* GetDocumentURI() { return mDocumentURI; } + + // Create a WindowGlobalParent from over IPC. This method should not be called + // from outside of the IPC constructors. + WindowGlobalParent(const WindowGlobalInit& aInit, bool aInProcess); + + // Initialize the mFrameLoader fields for a created WindowGlobalParent. Must + // be called after setting the Manager actor. + void Init(); + +protected: + // IPC messages + mozilla::ipc::IPCResult RecvUpdateDocumentURI(nsIURI* aURI) override; + + void ActorDestroy(ActorDestroyReason aWhy) override; + +private: + ~WindowGlobalParent(); + + // XXX(nika): We should store the BrowsingContext here. Unfortunately, the + // parent process is not sent down the BrowsingContext object until + // potentially after the WindowGlobalChild has been created. We should change + // this in the future. + uint64_t mBrowsingContextId; + + // NOTE: This document principal doesn't reflect possible |document.domain| + // mutations which may have been made in the actual document. + nsCOMPtr mDocumentPrincipal; + nsCOMPtr mDocumentURI; + RefPtr mFrameLoader; + bool mInProcess; + bool mIPCClosed; +}; + +} // namespace dom +} // namespace mozilla + +#endif // !defined(mozilla_dom_WindowGlobalParent_h) diff --git a/dom/ipc/moz.build b/dom/ipc/moz.build index 0b6936b5ba5d..a61e3f4257b4 100644 --- a/dom/ipc/moz.build +++ b/dom/ipc/moz.build @@ -49,6 +49,8 @@ EXPORTS.mozilla.dom += [ 'TabParent.h', 'URLClassifierChild.h', 'URLClassifierParent.h', + 'WindowGlobalChild.h', + 'WindowGlobalParent.h', ] EXPORTS.mozilla += [ @@ -83,6 +85,8 @@ UNIFIED_SOURCES += [ 'TabMessageUtils.cpp', 'TabParent.cpp', 'URLClassifierParent.cpp', + 'WindowGlobalChild.cpp', + 'WindowGlobalParent.cpp', ] # ContentChild.cpp cannot be compiled in unified mode on linux due to Time conflict @@ -110,6 +114,7 @@ IPDL_SOURCES += [ 'PURLClassifier.ipdl', 'PURLClassifierInfo.ipdlh', 'PURLClassifierLocal.ipdl', + 'PWindowGlobal.ipdl', 'ServiceWorkerConfiguration.ipdlh', ] diff --git a/ipc/glue/InProcessChild.cpp b/ipc/glue/InProcessChild.cpp index d0c2e9a98392..53d6451b5c53 100644 --- a/ipc/glue/InProcessChild.cpp +++ b/ipc/glue/InProcessChild.cpp @@ -5,9 +5,27 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/ipc/InProcessChild.h" +#include "mozilla/dom/WindowGlobalChild.h" + +using namespace mozilla::dom; namespace mozilla { namespace ipc { +PWindowGlobalChild* +InProcessChild::AllocPWindowGlobalChild(const WindowGlobalInit& aInit) +{ + MOZ_ASSERT_UNREACHABLE("PWindowGlobalChild should not be created manually"); + return nullptr; +} + +bool +InProcessChild::DeallocPWindowGlobalChild(PWindowGlobalChild* aActor) +{ + // Free IPC-held reference + static_cast(aActor)->Release(); + return true; +} + } // namespace ipc } // namespace mozilla diff --git a/ipc/glue/InProcessChild.h b/ipc/glue/InProcessChild.h index 902bd9e5127f..e0001beaeac0 100644 --- a/ipc/glue/InProcessChild.h +++ b/ipc/glue/InProcessChild.h @@ -11,6 +11,11 @@ #include "mozilla/StaticPtr.h" namespace mozilla { +namespace dom { +class PWindowGlobalParent; +class PWindowGlobalChild; +} // namespace dom + namespace ipc { class InProcessParent; @@ -38,6 +43,13 @@ public: // |nullptr|. static IProtocol* ParentActorFor(IProtocol* aActor); +protected: + virtual mozilla::dom::PWindowGlobalChild* + AllocPWindowGlobalChild(const WindowGlobalInit& aInit) override; + + virtual bool + DeallocPWindowGlobalChild(mozilla::dom::PWindowGlobalChild* aActor) override; + private: // NOTE: PInProcess lifecycle management is declared as staic methods and // state on InProcessParent, and implemented in InProcessImpl.cpp. diff --git a/ipc/glue/InProcessImpl.cpp b/ipc/glue/InProcessImpl.cpp index cc7e765c724c..f882bedeae0e 100644 --- a/ipc/glue/InProcessImpl.cpp +++ b/ipc/glue/InProcessImpl.cpp @@ -6,6 +6,8 @@ #include "mozilla/ipc/InProcessParent.h" #include "mozilla/ipc/InProcessChild.h" +#include "nsIObserverService.h" +#include "mozilla/Services.h" // This file contains the implementation of core InProcess lifecycle management // facilities. diff --git a/ipc/glue/InProcessParent.cpp b/ipc/glue/InProcessParent.cpp index 72e8bd019cdc..34eb615c393e 100644 --- a/ipc/glue/InProcessParent.cpp +++ b/ipc/glue/InProcessParent.cpp @@ -5,11 +5,37 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/ipc/InProcessParent.h" +#include "mozilla/dom/WindowGlobalParent.h" + +using namespace mozilla::dom; namespace mozilla { namespace ipc { NS_IMPL_ISUPPORTS(InProcessParent, nsIObserver) +IPCResult +InProcessParent::RecvPWindowGlobalConstructor(PWindowGlobalParent* aActor, + const WindowGlobalInit& aInit) +{ + static_cast(aActor)->Init(); + return IPC_OK(); +} + +PWindowGlobalParent* +InProcessParent::AllocPWindowGlobalParent(const WindowGlobalInit& aInit) +{ + // Reference freed in DeallocPWindowGlobalParent. + return do_AddRef(new WindowGlobalParent(aInit, /* inproc */ true)).take(); +} + +bool +InProcessParent::DeallocPWindowGlobalParent(PWindowGlobalParent* aActor) +{ + // Free IPC-held reference. + static_cast(aActor)->Release(); + return true; +} + } // namespace ipc } // namespace mozilla \ No newline at end of file diff --git a/ipc/glue/InProcessParent.h b/ipc/glue/InProcessParent.h index 2bce679ca759..145adccd14e6 100644 --- a/ipc/glue/InProcessParent.h +++ b/ipc/glue/InProcessParent.h @@ -11,6 +11,11 @@ #include "mozilla/StaticPtr.h" namespace mozilla { +namespace dom { +class PWindowGlobalParent; +class PWindowGlobalChild; +} // namespace dom + namespace ipc { class InProcessChild; @@ -40,6 +45,17 @@ public: // |nullptr|. static IProtocol* ChildActorFor(IProtocol* aActor); +protected: + virtual mozilla::dom::PWindowGlobalParent* + AllocPWindowGlobalParent(const WindowGlobalInit& aInit) override; + + virtual bool + DeallocPWindowGlobalParent(mozilla::dom::PWindowGlobalParent* aActor) override; + + virtual IPCResult + RecvPWindowGlobalConstructor(mozilla::dom::PWindowGlobalParent* aActor, + const WindowGlobalInit& aInit) override; + private: // Lifecycle management is implemented in InProcessImpl.cpp virtual void ActorDestroy(ActorDestroyReason aWhy) override; diff --git a/ipc/glue/PInProcess.ipdl b/ipc/glue/PInProcess.ipdl index c00e9d2effbb..9121d10e5964 100644 --- a/ipc/glue/PInProcess.ipdl +++ b/ipc/glue/PInProcess.ipdl @@ -4,6 +4,10 @@ * 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 protocol PWindowGlobal; + +include DOMTypes; + namespace mozilla { namespace ipc { @@ -17,6 +21,14 @@ namespace ipc { */ async protocol PInProcess { + manages PWindowGlobal; + +parent: + /** + * Construct a new WindowGlobal actor for a window global in the given + * BrowsingContext and with the given principal. + */ + async PWindowGlobal(WindowGlobalInit init); }; } // namespace ipc From b4954ede435db3e15ff39dff05d350c21021021c Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Fri, 19 Oct 2018 18:00:59 -0400 Subject: [PATCH 04/17] Bug 1500944 - Part 1: Store the set of active WindowGlobalParent objects in ChromeBrowsingContext, r=farre This allows getting the set of all window globals for a given browsing context. This is less useful at the moment as the active window global is not exposed as such. That will be added as a follow-up. Differential Revision: https://phabricator.services.mozilla.com/D9393 --- docshell/base/BrowsingContext.cpp | 2 +- docshell/base/BrowsingContext.h | 7 +++-- docshell/base/ChromeBrowsingContext.cpp | 24 +++++++++++++- docshell/base/ChromeBrowsingContext.h | 22 ++++++++++--- dom/ipc/TabParent.cpp | 2 +- dom/ipc/WindowGlobalParent.cpp | 42 +++++++++++++++++-------- dom/ipc/WindowGlobalParent.h | 13 +++----- ipc/glue/InProcessParent.cpp | 2 +- 8 files changed, 80 insertions(+), 34 deletions(-) diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp index a6b597f5ce3d..cd7132557962 100644 --- a/docshell/base/BrowsingContext.cpp +++ b/docshell/base/BrowsingContext.cpp @@ -4,7 +4,7 @@ * 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 "BrowsingContext.h" +#include "mozilla/dom/BrowsingContext.h" #include "mozilla/dom/ChromeBrowsingContext.h" #include "mozilla/dom/BrowsingContextBinding.h" diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h index ad541b8fc548..afd7b823d8a7 100644 --- a/docshell/base/BrowsingContext.h +++ b/docshell/base/BrowsingContext.h @@ -4,8 +4,8 @@ * 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 BrowsingContext_h -#define BrowsingContext_h +#ifndef mozilla_dom_BrowsingContext_h +#define mozilla_dom_BrowsingContext_h #include "mozilla/LinkedList.h" #include "mozilla/Maybe.h" @@ -143,4 +143,5 @@ class BrowsingContext : public nsWrapperCache, } // namespace dom } // namespace mozilla -#endif + +#endif // !defined(mozilla_dom_BrowsingContext_h) diff --git a/docshell/base/ChromeBrowsingContext.cpp b/docshell/base/ChromeBrowsingContext.cpp index e2826da14653..dc54950bd3d7 100644 --- a/docshell/base/ChromeBrowsingContext.cpp +++ b/docshell/base/ChromeBrowsingContext.cpp @@ -4,7 +4,8 @@ * 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 "ChromeBrowsingContext.h" +#include "mozilla/dom/ChromeBrowsingContext.h" +#include "mozilla/dom/WindowGlobalParent.h" namespace mozilla { namespace dom { @@ -56,5 +57,26 @@ ChromeBrowsingContext::ChromeBrowsingContext(BrowsingContext* aParent, return static_cast(aContext); } +void ChromeBrowsingContext::RegisterWindowGlobal(WindowGlobalParent* aGlobal) { + MOZ_ASSERT(!mWindowGlobals.Contains(aGlobal), "Global already registered!"); + mWindowGlobals.PutEntry(aGlobal); +} + +void ChromeBrowsingContext::UnregisterWindowGlobal( + WindowGlobalParent* aGlobal) { + MOZ_ASSERT(mWindowGlobals.Contains(aGlobal), "Global not registered!"); + mWindowGlobals.RemoveEntry(aGlobal); +} + +void ChromeBrowsingContext::Traverse(nsCycleCollectionTraversalCallback& cb) { + ChromeBrowsingContext* tmp = this; + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobals); +} + +void ChromeBrowsingContext::Unlink() { + ChromeBrowsingContext* tmp = this; + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobals); +} + } // namespace dom } // namespace mozilla diff --git a/docshell/base/ChromeBrowsingContext.h b/docshell/base/ChromeBrowsingContext.h index b50eb2cee287..275cd981a91d 100644 --- a/docshell/base/ChromeBrowsingContext.h +++ b/docshell/base/ChromeBrowsingContext.h @@ -4,19 +4,23 @@ * 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 ChromeBrowsingContext_h -#define ChromeBrowsingContext_h +#ifndef mozilla_dom_ChromeBrowsingContext_h +#define mozilla_dom_ChromeBrowsingContext_h #include "mozilla/dom/BrowsingContext.h" #include "mozilla/RefPtr.h" #include "nsCycleCollectionParticipant.h" #include "nsWrapperCache.h" +#include "nsTHashtable.h" +#include "nsHashKeys.h" class nsIDocShell; namespace mozilla { namespace dom { +class WindowGlobalParent; + // ChromeBrowsingContext is a BrowsingContext living in the parent // process, with whatever extra data that a BrowsingContext in the // parent needs. @@ -31,9 +35,13 @@ class ChromeBrowsingContext final : public BrowsingContext { return mProcessId == aProcessId; } + // Called by WindowGlobalParent to register and unregister window globals. + void RegisterWindowGlobal(WindowGlobalParent* aGlobal); + void UnregisterWindowGlobal(WindowGlobalParent* aGlobal); + protected: - void Traverse(nsCycleCollectionTraversalCallback& cb) {} - void Unlink() {} + void Traverse(nsCycleCollectionTraversalCallback& cb); + void Unlink(); using Type = BrowsingContext::Type; ChromeBrowsingContext(BrowsingContext* aParent, BrowsingContext* aOpener, @@ -46,8 +54,12 @@ class ChromeBrowsingContext final : public BrowsingContext { // XXX(farre): Store a ContentParent pointer here rather than mProcessId? // Indicates which process owns the docshell. uint64_t mProcessId; + + // All live window globals within this browsing context. + nsTHashtable> mWindowGlobals; }; } // namespace dom } // namespace mozilla -#endif + +#endif // !defined(mozilla_dom_ChromeBrowsingContext_h) diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index c47f09c6a8e8..d4cf3ee77b08 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -991,7 +991,7 @@ bool TabParent::DeallocPIndexedDBPermissionRequestParent( IPCResult TabParent::RecvPWindowGlobalConstructor( PWindowGlobalParent* aActor, const WindowGlobalInit& aInit) { - static_cast(aActor)->Init(); + static_cast(aActor)->Init(aInit); return IPC_OK(); } diff --git a/dom/ipc/WindowGlobalParent.cpp b/dom/ipc/WindowGlobalParent.cpp index c2fbfb81e73e..19dcac8c9cb4 100644 --- a/dom/ipc/WindowGlobalParent.cpp +++ b/dom/ipc/WindowGlobalParent.cpp @@ -6,7 +6,7 @@ #include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/ipc/InProcessParent.h" -#include "mozilla/dom/BrowsingContext.h" +#include "mozilla/dom/ChromeBrowsingContext.h" using namespace mozilla::ipc; @@ -15,22 +15,43 @@ namespace dom { WindowGlobalParent::WindowGlobalParent(const WindowGlobalInit& aInit, bool aInProcess) - : mBrowsingContextId(aInit.browsingContextId()) - , mDocumentPrincipal(aInit.principal()) + : mDocumentPrincipal(aInit.principal()) , mInProcess(aInProcess) - , mIPCClosed(false) + , mIPCClosed(true) // Closed until WGP::Init { MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), "Parent process only"); MOZ_RELEASE_ASSERT(mDocumentPrincipal, "Must have a valid principal"); - MOZ_RELEASE_ASSERT(mBrowsingContextId != 0, "Must be made in BrowsingContext"); + + // NOTE: mBrowsingContext initialized in Init() + MOZ_RELEASE_ASSERT(aInit.browsingContextId() != 0, + "Must be made in BrowsingContext"); } void -WindowGlobalParent::Init() +WindowGlobalParent::Init(const WindowGlobalInit& aInit) { MOZ_ASSERT(Manager(), "Should have a manager!"); MOZ_ASSERT(!mFrameLoader, "Cannot Init() a WindowGlobalParent twice!"); + MOZ_ASSERT(mIPCClosed, "IPC shouldn't be open yet"); + mIPCClosed = false; + + // Determine which content process the window global is coming from. + ContentParentId processId(0); + if (!mInProcess) { + processId = static_cast(Manager()->Manager())->ChildID(); + } + + mBrowsingContext = ChromeBrowsingContext::Get(aInit.browsingContextId()); + 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); + // Determine what toplevel frame element our WindowGlobalParent is being // embedded in. RefPtr frameElement; @@ -60,12 +81,6 @@ WindowGlobalParent::Init() } } -already_AddRefed -WindowGlobalParent::BrowsingContext() -{ - return dom::BrowsingContext::Get(mBrowsingContextId); -} - already_AddRefed WindowGlobalParent::GetOtherSide() { @@ -89,13 +104,14 @@ void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) { mIPCClosed = true; + mBrowsingContext->UnregisterWindowGlobal(this); } WindowGlobalParent::~WindowGlobalParent() { } -NS_IMPL_CYCLE_COLLECTION(WindowGlobalParent, mFrameLoader) +NS_IMPL_CYCLE_COLLECTION(WindowGlobalParent, mFrameLoader, mBrowsingContext) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WindowGlobalParent, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WindowGlobalParent, Release) diff --git a/dom/ipc/WindowGlobalParent.h b/dom/ipc/WindowGlobalParent.h index 7bc6786d4352..1bed980da5d3 100644 --- a/dom/ipc/WindowGlobalParent.h +++ b/dom/ipc/WindowGlobalParent.h @@ -17,7 +17,7 @@ class nsFrameLoader; namespace mozilla { namespace dom { -class BrowsingContext; +class ChromeBrowsingContext; class WindowGlobalChild; /** @@ -46,7 +46,7 @@ public: nsIPrincipal* DocumentPrincipal() { return mDocumentPrincipal; } // The BrowsingContext which this WindowGlobal has been loaded into. - already_AddRefed BrowsingContext(); + ChromeBrowsingContext* BrowsingContext() { return mBrowsingContext; } // Get the root nsFrameLoader object for the tree of BrowsingContext nodes // which this WindowGlobal is a part of. This will be the nsFrameLoader @@ -63,7 +63,7 @@ public: // Initialize the mFrameLoader fields for a created WindowGlobalParent. Must // be called after setting the Manager actor. - void Init(); + void Init(const WindowGlobalInit& aInit); protected: // IPC messages @@ -74,17 +74,12 @@ protected: private: ~WindowGlobalParent(); - // XXX(nika): We should store the BrowsingContext here. Unfortunately, the - // parent process is not sent down the BrowsingContext object until - // potentially after the WindowGlobalChild has been created. We should change - // this in the future. - uint64_t mBrowsingContextId; - // NOTE: This document principal doesn't reflect possible |document.domain| // mutations which may have been made in the actual document. nsCOMPtr mDocumentPrincipal; nsCOMPtr mDocumentURI; RefPtr mFrameLoader; + RefPtr mBrowsingContext; bool mInProcess; bool mIPCClosed; }; diff --git a/ipc/glue/InProcessParent.cpp b/ipc/glue/InProcessParent.cpp index 34eb615c393e..4d56eb9d070e 100644 --- a/ipc/glue/InProcessParent.cpp +++ b/ipc/glue/InProcessParent.cpp @@ -18,7 +18,7 @@ IPCResult InProcessParent::RecvPWindowGlobalConstructor(PWindowGlobalParent* aActor, const WindowGlobalInit& aInit) { - static_cast(aActor)->Init(); + static_cast(aActor)->Init(aInit); return IPC_OK(); } From 91dd6c616f8af9f68ab7cc3ad9262816b9523348 Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Fri, 19 Oct 2018 19:02:56 -0400 Subject: [PATCH 05/17] Bug 1500944 - Part 2: Expose WindowGlobal actors to Chrome JS, r=farre This serves 2 purposes: 1. Provides an object corresponding to an inner window which Chrome JS can hold onto. 2. Provides the object to JS which Chrome JS per-window actors will be attached to. 3. Provides useful information to Chrome JS in the parent process. Differential Revision: https://phabricator.services.mozilla.com/D9394 --- docshell/base/BrowsingContext.h | 4 +-- docshell/base/ChromeBrowsingContext.cpp | 13 ++++++++ docshell/base/ChromeBrowsingContext.h | 5 ++++ dom/chrome-webidl/BrowsingContext.webidl | 5 ++++ dom/chrome-webidl/WindowGlobalActors.webidl | 33 +++++++++++++++++++++ dom/chrome-webidl/moz.build | 1 + dom/ipc/WindowGlobalChild.cpp | 21 +++++++++++-- dom/ipc/WindowGlobalChild.h | 12 ++++++-- dom/ipc/WindowGlobalParent.cpp | 23 ++++++++++++-- dom/ipc/WindowGlobalParent.h | 12 ++++++-- 10 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 dom/chrome-webidl/WindowGlobalActors.webidl diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h index afd7b823d8a7..71730b64f9fe 100644 --- a/docshell/base/BrowsingContext.h +++ b/docshell/base/BrowsingContext.h @@ -112,8 +112,8 @@ class BrowsingContext : public nsWrapperCache, nsTArray>& aBrowsingContexts); nsISupports* GetParentObject() const; - virtual JSObject* WrapObject(JSContext* aCx, - JS::Handle aGivenProto) override; + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; MOZ_DECLARE_WEAKREFERENCE_TYPENAME(BrowsingContext) NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BrowsingContext) diff --git a/docshell/base/ChromeBrowsingContext.cpp b/docshell/base/ChromeBrowsingContext.cpp index dc54950bd3d7..f111cc0463bc 100644 --- a/docshell/base/ChromeBrowsingContext.cpp +++ b/docshell/base/ChromeBrowsingContext.cpp @@ -57,6 +57,14 @@ ChromeBrowsingContext::ChromeBrowsingContext(BrowsingContext* aParent, return static_cast(aContext); } +void ChromeBrowsingContext::GetWindowGlobals( + nsTArray>& aWindows) { + aWindows.SetCapacity(mWindowGlobals.Count()); + for (auto iter = mWindowGlobals.Iter(); !iter.Done(); iter.Next()) { + aWindows.AppendElement(iter.Get()->GetKey()); + } +} + void ChromeBrowsingContext::RegisterWindowGlobal(WindowGlobalParent* aGlobal) { MOZ_ASSERT(!mWindowGlobals.Contains(aGlobal), "Global already registered!"); mWindowGlobals.PutEntry(aGlobal); @@ -68,6 +76,11 @@ void ChromeBrowsingContext::UnregisterWindowGlobal( mWindowGlobals.RemoveEntry(aGlobal); } +JSObject* ChromeBrowsingContext::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return ChromeBrowsingContext_Binding::Wrap(aCx, this, aGivenProto); +} + void ChromeBrowsingContext::Traverse(nsCycleCollectionTraversalCallback& cb) { ChromeBrowsingContext* tmp = this; NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobals); diff --git a/docshell/base/ChromeBrowsingContext.h b/docshell/base/ChromeBrowsingContext.h index 275cd981a91d..bc38edde2bec 100644 --- a/docshell/base/ChromeBrowsingContext.h +++ b/docshell/base/ChromeBrowsingContext.h @@ -35,10 +35,15 @@ class ChromeBrowsingContext final : public BrowsingContext { return mProcessId == aProcessId; } + void GetWindowGlobals(nsTArray>& aWindows); + // Called by WindowGlobalParent to register and unregister window globals. void RegisterWindowGlobal(WindowGlobalParent* aGlobal); void UnregisterWindowGlobal(WindowGlobalParent* aGlobal); + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + protected: void Traverse(nsCycleCollectionTraversalCallback& cb); void Unlink(); diff --git a/dom/chrome-webidl/BrowsingContext.webidl b/dom/chrome-webidl/BrowsingContext.webidl index 236d51a9ce7e..92c0850d2dcc 100644 --- a/dom/chrome-webidl/BrowsingContext.webidl +++ b/dom/chrome-webidl/BrowsingContext.webidl @@ -17,3 +17,8 @@ interface BrowsingContext { readonly attribute BrowsingContext? opener; }; + +[Exposed=Window, ChromeOnly] +interface ChromeBrowsingContext : BrowsingContext { + sequence getWindowGlobals(); +}; diff --git a/dom/chrome-webidl/WindowGlobalActors.webidl b/dom/chrome-webidl/WindowGlobalActors.webidl new file mode 100644 index 000000000000..4b847720d380 --- /dev/null +++ b/dom/chrome-webidl/WindowGlobalActors.webidl @@ -0,0 +1,33 @@ +/* -*- 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/. */ + +interface Principal; +interface URI; +interface nsIDocShell; + +[Exposed=Window, ChromeOnly] +interface WindowGlobalParent { + readonly attribute boolean isClosed; + readonly attribute boolean isInProcess; + readonly attribute ChromeBrowsingContext browsingContext; + + // XXX(nika): We would want to expose this, but |FrameLoader| isn't exposed on System. + // readonly attribute FrameLoader? rootFrameLoader; // Embedded (browser) only + + readonly attribute WindowGlobalChild? childActor; // in-process only + + // Information about the currently loaded document. + readonly attribute Principal documentPrincipal; + readonly attribute URI? documentURI; +}; + +[Exposed=Window, ChromeOnly] +interface WindowGlobalChild { + readonly attribute boolean isClosed; + readonly attribute boolean isInProcess; + readonly attribute BrowsingContext browsingContext; + + readonly attribute WindowGlobalParent? parentActor; // in-process only +}; diff --git a/dom/chrome-webidl/moz.build b/dom/chrome-webidl/moz.build index 19947457e13d..8a93ef30f47c 100644 --- a/dom/chrome-webidl/moz.build +++ b/dom/chrome-webidl/moz.build @@ -54,6 +54,7 @@ WEBIDL_FILES = [ 'TelemetryStopwatch.webidl', 'WebExtensionContentScript.webidl', 'WebExtensionPolicy.webidl', + 'WindowGlobalActors.webidl', 'XULFrameElement.webidl', 'XULMenuElement.webidl', 'XULScrollElement.webidl', diff --git a/dom/ipc/WindowGlobalChild.cpp b/dom/ipc/WindowGlobalChild.cpp index dd2026bb39ae..f19106c6ae01 100644 --- a/dom/ipc/WindowGlobalChild.cpp +++ b/dom/ipc/WindowGlobalChild.cpp @@ -7,6 +7,7 @@ #include "mozilla/dom/WindowGlobalChild.h" #include "mozilla/ipc/InProcessChild.h" #include "mozilla/dom/BrowsingContext.h" +#include "mozilla/dom/WindowGlobalActorsBinding.h" namespace mozilla { namespace dom { @@ -54,7 +55,7 @@ WindowGlobalChild::Create(nsGlobalWindowInner* aWindow) } already_AddRefed -WindowGlobalChild::GetOtherSide() +WindowGlobalChild::GetParentActor() { if (mIPCClosed) { return nullptr; @@ -73,7 +74,23 @@ WindowGlobalChild::~WindowGlobalChild() { } -NS_IMPL_CYCLE_COLLECTION(WindowGlobalChild, mWindowGlobal, mBrowsingContext) +JSObject* +WindowGlobalChild::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) +{ + return WindowGlobalChild_Binding::Wrap(aCx, this, aGivenProto); +} + +nsISupports* +WindowGlobalChild::GetParentObject() +{ + return xpc::NativeGlobal(xpc::PrivilegedJunkScope()); +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WindowGlobalChild, + mWindowGlobal, + mBrowsingContext) + NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WindowGlobalChild, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WindowGlobalChild, Release) diff --git a/dom/ipc/WindowGlobalChild.h b/dom/ipc/WindowGlobalChild.h index cce3f12e73f5..3dde74ef713b 100644 --- a/dom/ipc/WindowGlobalChild.h +++ b/dom/ipc/WindowGlobalChild.h @@ -9,6 +9,7 @@ #include "mozilla/RefPtr.h" #include "mozilla/dom/PWindowGlobalChild.h" +#include "nsWrapperCache.h" class nsGlobalWindowInner; class nsDocShell; @@ -23,11 +24,12 @@ class WindowGlobalParent; * Actor for a single nsGlobalWindowInner. This actor is used to communicate * information to the parent process asynchronously. */ -class WindowGlobalChild : public PWindowGlobalChild +class WindowGlobalChild : public nsWrapperCache + , public PWindowGlobalChild { public: NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WindowGlobalChild) - NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(WindowGlobalChild) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WindowGlobalChild) dom::BrowsingContext* BrowsingContext() { return mBrowsingContext; } nsGlobalWindowInner* WindowGlobal() { return mWindowGlobal; } @@ -41,12 +43,16 @@ public: // Get the other side of this actor if it is an in-process actor. Returns // |nullptr| if the actor has been torn down, or is not in-process. - already_AddRefed GetOtherSide(); + already_AddRefed GetParentActor(); // Create and initialize the WindowGlobalChild object. static already_AddRefed Create(nsGlobalWindowInner* aWindow); + nsISupports* GetParentObject(); + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + protected: virtual void ActorDestroy(ActorDestroyReason aWhy) override; diff --git a/dom/ipc/WindowGlobalParent.cpp b/dom/ipc/WindowGlobalParent.cpp index 19dcac8c9cb4..480f6c4d81e7 100644 --- a/dom/ipc/WindowGlobalParent.cpp +++ b/dom/ipc/WindowGlobalParent.cpp @@ -7,6 +7,7 @@ #include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/ipc/InProcessParent.h" #include "mozilla/dom/ChromeBrowsingContext.h" +#include "mozilla/dom/WindowGlobalActorsBinding.h" using namespace mozilla::ipc; @@ -59,7 +60,7 @@ WindowGlobalParent::Init(const WindowGlobalInit& aInit) // In the in-process case, we can get it from the other side's // WindowGlobalChild. MOZ_ASSERT(Manager()->GetProtocolTypeId() == PInProcessMsgStart); - RefPtr otherSide = GetOtherSide(); + RefPtr otherSide = GetChildActor(); if (otherSide && otherSide->WindowGlobal()) { // Get the toplevel window from the other side. RefPtr docShell = nsDocShell::Cast(otherSide->WindowGlobal()->GetDocShell()); @@ -82,7 +83,7 @@ WindowGlobalParent::Init(const WindowGlobalInit& aInit) } already_AddRefed -WindowGlobalParent::GetOtherSide() +WindowGlobalParent::GetChildActor() { if (mIPCClosed) { return nullptr; @@ -111,7 +112,23 @@ WindowGlobalParent::~WindowGlobalParent() { } -NS_IMPL_CYCLE_COLLECTION(WindowGlobalParent, mFrameLoader, mBrowsingContext) +JSObject* +WindowGlobalParent::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) +{ + return WindowGlobalParent_Binding::Wrap(aCx, this, aGivenProto); +} + +nsISupports* +WindowGlobalParent::GetParentObject() +{ + return xpc::NativeGlobal(xpc::PrivilegedJunkScope()); +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WindowGlobalParent, + mFrameLoader, + mBrowsingContext) + NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WindowGlobalParent, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WindowGlobalParent, Release) diff --git a/dom/ipc/WindowGlobalParent.h b/dom/ipc/WindowGlobalParent.h index 1bed980da5d3..6ab6ac8f5985 100644 --- a/dom/ipc/WindowGlobalParent.h +++ b/dom/ipc/WindowGlobalParent.h @@ -9,6 +9,7 @@ #include "mozilla/RefPtr.h" #include "mozilla/dom/PWindowGlobalParent.h" +#include "nsWrapperCache.h" class nsIPrincipal; class nsIURI; @@ -23,11 +24,12 @@ class WindowGlobalChild; /** * A handle in the parent process to a specific nsGlobalWindowInner object. */ -class WindowGlobalParent final : public PWindowGlobalParent +class WindowGlobalParent final : public nsWrapperCache + , public PWindowGlobalParent { public: NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WindowGlobalParent) - NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(WindowGlobalParent) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WindowGlobalParent) // Has this actor been shut down bool IsClosed() { return mIPCClosed; } @@ -38,7 +40,7 @@ public: // Get the other side of this actor if it is an in-process actor. Returns // |nullptr| if the actor has been torn down, or is not in-process. - already_AddRefed GetOtherSide(); + already_AddRefed GetChildActor(); // The principal of this WindowGlobal. This value will not change over the // lifetime of the WindowGlobal object, even to reflect changes in @@ -65,6 +67,10 @@ public: // be called after setting the Manager actor. void Init(const WindowGlobalInit& aInit); + nsISupports* GetParentObject(); + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + protected: // IPC messages mozilla::ipc::IPCResult RecvUpdateDocumentURI(nsIURI* aURI) override; From 554bfd28119f0f36491017d704d17f19ffe01b6d Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Fri, 19 Oct 2018 20:02:37 -0400 Subject: [PATCH 06/17] Bug 1500948 - Expose BrowsingContext on nsFrameLoader objects, r=farre This should make BrowsingContext more usable by making it much easier to obtain for a given frame or browser. BrowsingContext and nsFrameLoader should have the same lifetime. Differential Revision: https://phabricator.services.mozilla.com/D9395 --- dom/base/nsFrameLoader.cpp | 11 +++++++++++ dom/base/nsFrameLoader.h | 3 +++ dom/ipc/PBrowser.ipdl | 2 ++ dom/ipc/TabChild.cpp | 5 +++++ dom/ipc/TabParent.cpp | 9 +++++++++ dom/ipc/TabParent.h | 9 +++++++++ dom/webidl/FrameLoader.webidl | 6 ++++++ 7 files changed, 45 insertions(+) diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 03bb8cc5a15d..06d6b1a9bf30 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -100,6 +100,7 @@ #include "mozilla/dom/PromiseNativeHandler.h" #include "mozilla/dom/ParentSHistory.h" #include "mozilla/dom/ChildSHistory.h" +#include "mozilla/dom/ChromeBrowsingContext.h" #include "mozilla/dom/HTMLBodyElement.h" @@ -3022,6 +3023,16 @@ already_AddRefed nsFrameLoader::LoadContext() { return loadContext.forget(); } +already_AddRefed nsFrameLoader::GetBrowsingContext() { + RefPtr browsingContext; + if (IsRemoteFrame() && (mRemoteBrowser || TryRemoteBrowser())) { + browsingContext = mRemoteBrowser->GetBrowsingContext(); + } else if (GetDocShell(IgnoreErrors())) { + browsingContext = nsDocShell::Cast(mDocShell)->GetBrowsingContext(); + } + return browsingContext.forget(); +} + void nsFrameLoader::InitializeBrowserAPI() { if (!OwnerIsMozBrowserFrame()) { return; diff --git a/dom/base/nsFrameLoader.h b/dom/base/nsFrameLoader.h index 7705009faa36..dc797cfe9713 100644 --- a/dom/base/nsFrameLoader.h +++ b/dom/base/nsFrameLoader.h @@ -48,6 +48,7 @@ namespace mozilla { class OriginAttributes; namespace dom { +class BrowsingContext; class ChromeMessageSender; class ContentParent; class InProcessTabChildMessageManager; @@ -123,6 +124,8 @@ class nsFrameLoader final : public nsStubMutationObserver, already_AddRefed LoadContext(); + already_AddRefed GetBrowsingContext(); + /** * Start loading the frame. This method figures out what to load * from the owner content in the frame loader. diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 05ed7186fe54..83e3c040fa47 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -588,6 +588,8 @@ parent: sync SetPrefersReducedMotionOverrideForTest(bool aValue); sync ResetPrefersReducedMotionOverrideForTest(); + async RootBrowsingContext(BrowsingContextId aId); + child: /** * Notify the remote browser that it has been Show()n on this diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index bd4a5e420638..f1a385febeb7 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -538,6 +538,11 @@ nsresult TabChild::Init(mozIDOMWindowProxy* aParent) { loadContext->SetRemoteTabs(mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW); + // Send our browsing context to the parent process. + RefPtr browsingContext = + nsDocShell::Cast(docShell)->GetBrowsingContext(); + SendRootBrowsingContext(BrowsingContextId(browsingContext->Id())); + // 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. diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index d4cf3ee77b08..be18d986c53c 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -101,6 +101,7 @@ #include "nsString.h" #include "IHistory.h" #include "mozilla/dom/WindowGlobalParent.h" +#include "mozilla/dom/ChromeBrowsingContext.h" #ifdef XP_WIN #include "mozilla/plugins/PluginWidgetParent.h" @@ -3390,6 +3391,14 @@ mozilla::ipc::IPCResult TabParent::RecvGetSystemFont(nsCString* aFontName) { return IPC_OK(); } +mozilla::ipc::IPCResult TabParent::RecvRootBrowsingContext( + const BrowsingContextId& aId) { + MOZ_ASSERT(!mBrowsingContext, "May only set browsing context once!"); + mBrowsingContext = ChromeBrowsingContext::Get(aId); + MOZ_ASSERT(mBrowsingContext, "Invalid ID!"); + return IPC_OK(); +} + NS_IMETHODIMP FakeChannel::OnAuthAvailable(nsISupports* aContext, nsIAuthInformation* aAuthInfo) { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index f8eb2946bb47..7784bf794bf8 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -67,6 +67,7 @@ class DataSourceSurface; namespace dom { +class ChromeBrowsingContext; class ClonedMessageData; class nsIContentParent; class Element; @@ -125,6 +126,8 @@ class TabParent final : public PBrowserParent, nsIXULBrowserWindow* GetXULBrowserWindow(); + ChromeBrowsingContext* GetBrowsingContext() { return mBrowsingContext; } + void Destroy(); void RemoveWindowListeners(); @@ -584,6 +587,9 @@ class TabParent final : public PBrowserParent, virtual mozilla::ipc::IPCResult RecvShowCanvasPermissionPrompt( const nsCString& aFirstPartyURI) override; + virtual mozilla::ipc::IPCResult RecvRootBrowsingContext( + const BrowsingContextId& aId) override; + mozilla::ipc::IPCResult RecvSetSystemFont( const nsCString& aFontName) override; mozilla::ipc::IPCResult RecvGetSystemFont(nsCString* aFontName) override; @@ -661,6 +667,9 @@ class TabParent final : public PBrowserParent, // dispatch message manager messages during this time. RefPtr mFrameLoader; + // The root browsing context loaded in this TabParent. + RefPtr mBrowsingContext; + TabId mTabId; // When loading a new tab or window via window.open, the child is diff --git a/dom/webidl/FrameLoader.webidl b/dom/webidl/FrameLoader.webidl index 375550b29606..8fa9e5f33822 100644 --- a/dom/webidl/FrameLoader.webidl +++ b/dom/webidl/FrameLoader.webidl @@ -33,6 +33,12 @@ interface FrameLoader { */ readonly attribute LoadContext loadContext; + /** + * Get the root BrowsingContext within the frame. + * This may be null immediately after creating a remote frame. + */ + readonly attribute BrowsingContext? browsingContext; + /** * Get the ParentSHistory for the nsFrameLoader. May return null if this * frameloader is not for a toplevel frame. From f18502de7a2b8d276823612e8bfe822070d44d07 Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Sat, 20 Oct 2018 16:04:00 -0400 Subject: [PATCH 07/17] Bug 1500949 - Include innerWindowId/outerWindowId in PWindowGlobal, r=farre This will be useful as both an ID for PWindowGlobal, as well as a mechanism for taking advantage of already synchronized information. As an example, LoadInfo objects contain the inner window IDs of the window requesting the load, which can now be used to obtain a reference to the corresponding WindowGlobalParent in the parent process. Differential Revision: https://phabricator.services.mozilla.com/D9396 --- dom/chrome-webidl/WindowGlobalActors.webidl | 10 ++++++ dom/ipc/DOMTypes.ipdlh | 2 ++ dom/ipc/WindowGlobalChild.cpp | 37 +++++++++++++++++++-- dom/ipc/WindowGlobalChild.h | 14 ++++++++ dom/ipc/WindowGlobalParent.cpp | 26 +++++++++++++++ dom/ipc/WindowGlobalParent.h | 14 ++++++++ 6 files changed, 100 insertions(+), 3 deletions(-) diff --git a/dom/chrome-webidl/WindowGlobalActors.webidl b/dom/chrome-webidl/WindowGlobalActors.webidl index 4b847720d380..5a8590df8547 100644 --- a/dom/chrome-webidl/WindowGlobalActors.webidl +++ b/dom/chrome-webidl/WindowGlobalActors.webidl @@ -13,6 +13,9 @@ interface WindowGlobalParent { readonly attribute boolean isInProcess; readonly attribute ChromeBrowsingContext browsingContext; + readonly attribute unsigned long long innerWindowId; + readonly attribute unsigned long long outerWindowId; + // XXX(nika): We would want to expose this, but |FrameLoader| isn't exposed on System. // readonly attribute FrameLoader? rootFrameLoader; // Embedded (browser) only @@ -21,6 +24,8 @@ interface WindowGlobalParent { // Information about the currently loaded document. readonly attribute Principal documentPrincipal; readonly attribute URI? documentURI; + + static WindowGlobalParent? getByInnerWindowId(unsigned long long innerWindowId); }; [Exposed=Window, ChromeOnly] @@ -29,5 +34,10 @@ interface WindowGlobalChild { readonly attribute boolean isInProcess; readonly attribute BrowsingContext browsingContext; + readonly attribute unsigned long long innerWindowId; + readonly attribute unsigned long long outerWindowId; + readonly attribute WindowGlobalParent? parentActor; // in-process only + + static WindowGlobalChild? getByInnerWindowId(unsigned long long innerWIndowId); }; diff --git a/dom/ipc/DOMTypes.ipdlh b/dom/ipc/DOMTypes.ipdlh index f0af011e0e3f..d3a98a9e4939 100644 --- a/dom/ipc/DOMTypes.ipdlh +++ b/dom/ipc/DOMTypes.ipdlh @@ -189,6 +189,8 @@ struct WindowGlobalInit { nsIPrincipal principal; BrowsingContextId browsingContextId; + uint64_t innerWindowId; + uint64_t outerWindowId; }; } // namespace dom diff --git a/dom/ipc/WindowGlobalChild.cpp b/dom/ipc/WindowGlobalChild.cpp index f19106c6ae01..301819aef2a9 100644 --- a/dom/ipc/WindowGlobalChild.cpp +++ b/dom/ipc/WindowGlobalChild.cpp @@ -12,10 +12,16 @@ namespace mozilla { namespace dom { -WindowGlobalChild::WindowGlobalChild(nsGlobalWindowInner* aWindow, dom::BrowsingContext* aBrowsingContext) +typedef nsRefPtrHashtable WGCByIdMap; +static StaticAutoPtr gWindowGlobalChildById; + +WindowGlobalChild::WindowGlobalChild(nsGlobalWindowInner* aWindow, + dom::BrowsingContext* aBrowsingContext) : mWindowGlobal(aWindow) , mBrowsingContext(aBrowsingContext) - , mIPCClosed(false) + , mInnerWindowId(aWindow->WindowID()) + , mOuterWindowId(aWindow->GetOuterWindow()->WindowID()) + , mIPCClosed(true) { } @@ -32,7 +38,10 @@ WindowGlobalChild::Create(nsGlobalWindowInner* aWindow) RefPtr bc = docshell->GetBrowsingContext(); RefPtr wgc = new WindowGlobalChild(aWindow, bc); - WindowGlobalInit init(principal, BrowsingContextId(wgc->BrowsingContext()->Id())); + WindowGlobalInit init(principal, + BrowsingContextId(wgc->BrowsingContext()->Id()), + wgc->mInnerWindowId, + wgc->mOuterWindowId); // Send the link constructor over PInProcessChild or PBrowser. if (XRE_IsParentProcess()) { @@ -50,10 +59,29 @@ WindowGlobalChild::Create(nsGlobalWindowInner* aWindow) // Note: ref is released in DeallocPWindowGlobalChild tabChild->SendPWindowGlobalConstructor(do_AddRef(wgc).take(), init); } + wgc->mIPCClosed = false; + + // Register this WindowGlobal in the gWindowGlobalParentsById map. + if (!gWindowGlobalChildById) { + gWindowGlobalChildById = new WGCByIdMap(); + ClearOnShutdown(&gWindowGlobalChildById); + } + auto entry = gWindowGlobalChildById->LookupForAdd(wgc->mInnerWindowId); + MOZ_RELEASE_ASSERT(!entry, "Duplicate WindowGlobalChild entry for ID!"); + entry.OrInsert([&] { return wgc; }); return wgc.forget(); } +/* static */ already_AddRefed +WindowGlobalChild::GetByInnerWindowId(uint64_t aInnerWindowId) +{ + if (!gWindowGlobalChildById) { + return nullptr; + } + return gWindowGlobalChildById->Get(aInnerWindowId); +} + already_AddRefed WindowGlobalChild::GetParentActor() { @@ -68,10 +96,13 @@ void WindowGlobalChild::ActorDestroy(ActorDestroyReason aWhy) { mIPCClosed = true; + gWindowGlobalChildById->Remove(mInnerWindowId); } WindowGlobalChild::~WindowGlobalChild() { + MOZ_ASSERT(!gWindowGlobalChildById || + !gWindowGlobalChildById->Contains(mInnerWindowId)); } JSObject* diff --git a/dom/ipc/WindowGlobalChild.h b/dom/ipc/WindowGlobalChild.h index 3dde74ef713b..ed2bd58bd859 100644 --- a/dom/ipc/WindowGlobalChild.h +++ b/dom/ipc/WindowGlobalChild.h @@ -31,6 +31,14 @@ public: NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WindowGlobalChild) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WindowGlobalChild) + static already_AddRefed + GetByInnerWindowId(uint64_t aInnerWindowId); + + static already_AddRefed + GetByInnerWindowId(const GlobalObject& aGlobal, uint64_t aInnerWindowId) { + return GetByInnerWindowId(aInnerWindowId); + } + dom::BrowsingContext* BrowsingContext() { return mBrowsingContext; } nsGlobalWindowInner* WindowGlobal() { return mWindowGlobal; } @@ -41,6 +49,10 @@ public: // in the chrome process. bool IsInProcess() { return XRE_IsParentProcess(); } + // The Window ID for this WindowGlobal + uint64_t InnerWindowId() { return mInnerWindowId; } + uint64_t OuterWindowId() { return mOuterWindowId; } + // Get the other side of this actor if it is an in-process actor. Returns // |nullptr| if the actor has been torn down, or is not in-process. already_AddRefed GetParentActor(); @@ -62,6 +74,8 @@ private: RefPtr mWindowGlobal; RefPtr mBrowsingContext; + uint64_t mInnerWindowId; + uint64_t mOuterWindowId; bool mIPCClosed; }; diff --git a/dom/ipc/WindowGlobalParent.cpp b/dom/ipc/WindowGlobalParent.cpp index 480f6c4d81e7..12263ca7c331 100644 --- a/dom/ipc/WindowGlobalParent.cpp +++ b/dom/ipc/WindowGlobalParent.cpp @@ -14,9 +14,14 @@ using namespace mozilla::ipc; namespace mozilla { namespace dom { +typedef nsRefPtrHashtable WGPByIdMap; +static StaticAutoPtr gWindowGlobalParentsById; + WindowGlobalParent::WindowGlobalParent(const WindowGlobalInit& aInit, bool aInProcess) : mDocumentPrincipal(aInit.principal()) + , mInnerWindowId(aInit.innerWindowId()) + , mOuterWindowId(aInit.outerWindowId()) , mInProcess(aInProcess) , mIPCClosed(true) // Closed until WGP::Init { @@ -37,6 +42,15 @@ WindowGlobalParent::Init(const WindowGlobalInit& aInit) MOZ_ASSERT(mIPCClosed, "IPC shouldn't be open yet"); mIPCClosed = false; + // Register this WindowGlobal in the gWindowGlobalParentsById map. + if (!gWindowGlobalParentsById) { + gWindowGlobalParentsById = new WGPByIdMap(); + ClearOnShutdown(&gWindowGlobalParentsById); + } + auto entry = gWindowGlobalParentsById->LookupForAdd(mInnerWindowId); + MOZ_RELEASE_ASSERT(!entry, "Duplicate WindowGlobalParent entry for ID!"); + entry.OrInsert([&] { return this; }); + // Determine which content process the window global is coming from. ContentParentId processId(0); if (!mInProcess) { @@ -82,6 +96,15 @@ WindowGlobalParent::Init(const WindowGlobalInit& aInit) } } +/* static */ already_AddRefed +WindowGlobalParent::GetByInnerWindowId(uint64_t aInnerWindowId) +{ + if (!gWindowGlobalParentsById) { + return nullptr; + } + return gWindowGlobalParentsById->Get(aInnerWindowId); +} + already_AddRefed WindowGlobalParent::GetChildActor() { @@ -105,11 +128,14 @@ void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) { mIPCClosed = true; + gWindowGlobalParentsById->Remove(mInnerWindowId); mBrowsingContext->UnregisterWindowGlobal(this); } WindowGlobalParent::~WindowGlobalParent() { + MOZ_ASSERT(!gWindowGlobalParentsById || + !gWindowGlobalParentsById->Contains(mInnerWindowId)); } JSObject* diff --git a/dom/ipc/WindowGlobalParent.h b/dom/ipc/WindowGlobalParent.h index 6ab6ac8f5985..aaae9bff08f3 100644 --- a/dom/ipc/WindowGlobalParent.h +++ b/dom/ipc/WindowGlobalParent.h @@ -31,6 +31,14 @@ public: NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WindowGlobalParent) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WindowGlobalParent) + static already_AddRefed + GetByInnerWindowId(uint64_t aInnerWindowId); + + static already_AddRefed + GetByInnerWindowId(const GlobalObject& aGlobal, uint64_t aInnerWindowId) { + return GetByInnerWindowId(aInnerWindowId); + } + // Has this actor been shut down bool IsClosed() { return mIPCClosed; } @@ -59,6 +67,10 @@ public: // The current URI which loaded in the document. nsIURI* GetDocumentURI() { return mDocumentURI; } + // Window IDs for inner/outer windows. + uint64_t OuterWindowId() { return mOuterWindowId; } + uint64_t InnerWindowId() { return mInnerWindowId; } + // Create a WindowGlobalParent from over IPC. This method should not be called // from outside of the IPC constructors. WindowGlobalParent(const WindowGlobalInit& aInit, bool aInProcess); @@ -86,6 +98,8 @@ private: nsCOMPtr mDocumentURI; RefPtr mFrameLoader; RefPtr mBrowsingContext; + uint64_t mInnerWindowId; + uint64_t mOuterWindowId; bool mInProcess; bool mIPCClosed; }; From 1114608d6f4419cc76e0ee283c14b9a63a5b9f9d Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Sat, 20 Oct 2018 19:39:02 -0400 Subject: [PATCH 08/17] Bug 1500950 - Expose rootFrameLoader on WindowGlobalParent, r=farre This attribute was not exposed due to Bug 1489301. Differential Revision: https://phabricator.services.mozilla.com/D9404 --- dom/chrome-webidl/WindowGlobalActors.webidl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dom/chrome-webidl/WindowGlobalActors.webidl b/dom/chrome-webidl/WindowGlobalActors.webidl index 5a8590df8547..9bbd502c74cd 100644 --- a/dom/chrome-webidl/WindowGlobalActors.webidl +++ b/dom/chrome-webidl/WindowGlobalActors.webidl @@ -16,8 +16,7 @@ interface WindowGlobalParent { readonly attribute unsigned long long innerWindowId; readonly attribute unsigned long long outerWindowId; - // XXX(nika): We would want to expose this, but |FrameLoader| isn't exposed on System. - // readonly attribute FrameLoader? rootFrameLoader; // Embedded (browser) only + readonly attribute FrameLoader? rootFrameLoader; // Embedded (browser) only readonly attribute WindowGlobalChild? childActor; // in-process only From c82214c322570c7a81d0bde774b1e575e84e616f Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Thu, 22 Nov 2018 13:40:32 -0500 Subject: [PATCH 09/17] Bug 1509362 - Don't crash when constructing actor during content shutdown, r=jld When shutting down a content process, we call `Close` on the `IToplevelProtocol`. This causes the MessageChannel to be `Close`-ed, which in turn sends a `GOODBYE_MESSAGE`: https://searchfox.org/mozilla-central/rev/876022232b15425bb9efde189caf747823b39567/ipc/glue/MessageChannel.cpp#2852 This message is intercepted on the I/O thread in the content process, before any code is informed in content, and used to set the `mChannelState` property to `ChannelClosing`: https://searchfox.org/mozilla-central/rev/876022232b15425bb9efde189caf747823b39567/ipc/glue/MessageChannel.cpp#1176 Once this state has been set, which is performed as soon as the message is received, whether or not other messages have been processed yet, no messages can be sent back to the parent process. This is usually what causes the 'Too late to send/recv' message spam in the console, as we're still trying to send messages at this time. Usually this is fine - the message send fails, but we gracefully recover, and the process begins shutting down like normal. Unfortunately, child actor constructors currently have code automatically generated in them which causes a process crash if the send fails. As it's impossible for the main thread to know that the channel has been closed ahead of time (due to this happening out-of-band), we can then cause random content process crashes during shutdown due to actor construction. Unfortunately, we can't just destroy the actor, as our caller may (and often do) depend on the actor reference they gave us still being valid after calling Send*Constructor. Fortunately, if a message send failed, it means we're in the process of being shut down. This patch handles this by ignoring ctor send errors, and treating them like messages which successfully were queued to send, but got lost due to the other side hanging up. The actor will be gracefully destroyed in DestroySubtree when its manager is destroyed. Differential Revision: https://phabricator.services.mozilla.com/D12695 --- ipc/ipdl/ipdl/lower.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index 94a0493b7b90..ddaa98fdf22b 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -3910,13 +3910,31 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): msgvar, stmts = self.makeMessage(md, errfnSendCtor) sendok, sendstmts = self.sendAsync(md, msgvar) + + warnif = StmtIf(ExprNot(sendok)) + warnif.addifstmt(_printWarningMessage('Error sending constructor')) + method.addstmts( + # Build our constructor message & verify it. stmts + self.genVerifyMessage(md.decl.type.verify, md.params, errfnSendCtor, ExprVar('msg__')) + + # Notify the other side about the newly created actor. + # + # If the MessageChannel is closing, and we haven't been told yet, + # this send may fail. This error is ignored to treat it like a + # message being lost due to the other side shutting down before + # processing it. + # + # NOTE: We don't free the actor here, as our caller may be + # depending on it being alive after calling SendConstructor. + sendstmts - + self.failCtorIf(md, ExprNot(sendok)) - + [StmtReturn(actor.var())]) + + # Warn if the message failed to send, and return our newly created + # actor. + + [warnif, + StmtReturn(actor.var())]) lbl = CaseLabel(md.pqReplyId()) case = StmtBlock() From 614b663032d15d36f862fa9bcda338b0e2b2e486 Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Tue, 27 Nov 2018 15:03:05 -0500 Subject: [PATCH 10/17] Bug 1510460 - Record the current WindowGlobal on ChromeBrowsingContext, r=farre Differential Revision: https://phabricator.services.mozilla.com/D13156 --- docshell/base/ChromeBrowsingContext.cpp | 14 ++++++++++++++ docshell/base/ChromeBrowsingContext.h | 7 +++++++ dom/base/nsGlobalWindowOuter.cpp | 5 +++++ dom/chrome-webidl/BrowsingContext.webidl | 2 ++ dom/chrome-webidl/WindowGlobalActors.webidl | 4 ++++ dom/ipc/PWindowGlobal.ipdl | 3 +++ dom/ipc/WindowGlobalChild.cpp | 6 ++++++ dom/ipc/WindowGlobalChild.h | 2 ++ dom/ipc/WindowGlobalParent.cpp | 13 +++++++++++++ dom/ipc/WindowGlobalParent.h | 3 +++ 10 files changed, 59 insertions(+) diff --git a/docshell/base/ChromeBrowsingContext.cpp b/docshell/base/ChromeBrowsingContext.cpp index f111cc0463bc..872ea8b810cf 100644 --- a/docshell/base/ChromeBrowsingContext.cpp +++ b/docshell/base/ChromeBrowsingContext.cpp @@ -74,6 +74,20 @@ void ChromeBrowsingContext::UnregisterWindowGlobal( WindowGlobalParent* aGlobal) { MOZ_ASSERT(mWindowGlobals.Contains(aGlobal), "Global not registered!"); mWindowGlobals.RemoveEntry(aGlobal); + + // Our current window global should be in our mWindowGlobals set. If it's not + // anymore, clear that reference. + if (aGlobal == mCurrentWindowGlobal) { + mCurrentWindowGlobal = nullptr; + } +} + +void ChromeBrowsingContext::SetCurrentWindowGlobal( + WindowGlobalParent* aGlobal) { + MOZ_ASSERT(mWindowGlobals.Contains(aGlobal), "Global not registered!"); + + // TODO: This should probably assert that the processes match. + mCurrentWindowGlobal = aGlobal; } JSObject* ChromeBrowsingContext::WrapObject(JSContext* aCx, diff --git a/docshell/base/ChromeBrowsingContext.h b/docshell/base/ChromeBrowsingContext.h index bc38edde2bec..92a8cfa0d8d1 100644 --- a/docshell/base/ChromeBrowsingContext.h +++ b/docshell/base/ChromeBrowsingContext.h @@ -41,6 +41,12 @@ class ChromeBrowsingContext final : public BrowsingContext { void RegisterWindowGlobal(WindowGlobalParent* aGlobal); void UnregisterWindowGlobal(WindowGlobalParent* aGlobal); + // The current active WindowGlobal. + WindowGlobalParent* GetCurrentWindowGlobal() const { + return mCurrentWindowGlobal; + } + void SetCurrentWindowGlobal(WindowGlobalParent* aGlobal); + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; @@ -62,6 +68,7 @@ class ChromeBrowsingContext final : public BrowsingContext { // All live window globals within this browsing context. nsTHashtable> mWindowGlobals; + RefPtr mCurrentWindowGlobal; }; } // namespace dom diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp index 126f4b9d145c..e201005ae2df 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -68,6 +68,7 @@ #include "mozilla/intl/LocaleService.h" #include "WindowDestroyedEvent.h" #include "nsDocShellLoadState.h" +#include "mozilla/dom/WindowGlobalChild.h" // Helper Classes #include "nsJSUtils.h" @@ -1962,6 +1963,10 @@ nsresult nsGlobalWindowOuter::SetNewDocument(nsIDocument* aDocument, newInnerWindow->MigrateStateForDocumentOpen(currentInner); } + // Tell the WindowGlobalParent that it should become the current window global + // for our BrowsingContext if it isn't already. + mInnerWindow->GetWindowGlobalChild()->SendBecomeCurrentWindowGlobal(); + // We no longer need the old inner window. Start its destruction if // its not being reused and clear our reference. if (doomCurrentInner) { diff --git a/dom/chrome-webidl/BrowsingContext.webidl b/dom/chrome-webidl/BrowsingContext.webidl index 92c0850d2dcc..c398e42850f9 100644 --- a/dom/chrome-webidl/BrowsingContext.webidl +++ b/dom/chrome-webidl/BrowsingContext.webidl @@ -21,4 +21,6 @@ interface BrowsingContext { [Exposed=Window, ChromeOnly] interface ChromeBrowsingContext : BrowsingContext { sequence getWindowGlobals(); + + readonly attribute WindowGlobalParent? currentWindowGlobal; }; diff --git a/dom/chrome-webidl/WindowGlobalActors.webidl b/dom/chrome-webidl/WindowGlobalActors.webidl index 9bbd502c74cd..b889e7132350 100644 --- a/dom/chrome-webidl/WindowGlobalActors.webidl +++ b/dom/chrome-webidl/WindowGlobalActors.webidl @@ -13,6 +13,8 @@ interface WindowGlobalParent { readonly attribute boolean isInProcess; readonly attribute ChromeBrowsingContext browsingContext; + readonly attribute boolean isCurrentGlobal; + readonly attribute unsigned long long innerWindowId; readonly attribute unsigned long long outerWindowId; @@ -33,6 +35,8 @@ interface WindowGlobalChild { readonly attribute boolean isInProcess; readonly attribute BrowsingContext browsingContext; + readonly attribute boolean isCurrentGlobal; + readonly attribute unsigned long long innerWindowId; readonly attribute unsigned long long outerWindowId; diff --git a/dom/ipc/PWindowGlobal.ipdl b/dom/ipc/PWindowGlobal.ipdl index 5a78829f5e44..d36781f8bcb2 100644 --- a/dom/ipc/PWindowGlobal.ipdl +++ b/dom/ipc/PWindowGlobal.ipdl @@ -28,6 +28,9 @@ parent: /// Update the URI of the document in this WindowGlobal. async UpdateDocumentURI(nsIURI aUri); + /// Notify the parent that this PWindowGlobal is now the current global. + async BecomeCurrentWindowGlobal(); + async __delete__(); }; diff --git a/dom/ipc/WindowGlobalChild.cpp b/dom/ipc/WindowGlobalChild.cpp index 301819aef2a9..8313b86410a6 100644 --- a/dom/ipc/WindowGlobalChild.cpp +++ b/dom/ipc/WindowGlobalChild.cpp @@ -82,6 +82,12 @@ WindowGlobalChild::GetByInnerWindowId(uint64_t aInnerWindowId) return gWindowGlobalChildById->Get(aInnerWindowId); } +bool +WindowGlobalChild::IsCurrentGlobal() +{ + return !mIPCClosed && mWindowGlobal->IsCurrentInnerWindow(); +} + already_AddRefed WindowGlobalChild::GetParentActor() { diff --git a/dom/ipc/WindowGlobalChild.h b/dom/ipc/WindowGlobalChild.h index ed2bd58bd859..2edbf4ccdcf4 100644 --- a/dom/ipc/WindowGlobalChild.h +++ b/dom/ipc/WindowGlobalChild.h @@ -53,6 +53,8 @@ public: uint64_t InnerWindowId() { return mInnerWindowId; } uint64_t OuterWindowId() { return mOuterWindowId; } + bool IsCurrentGlobal(); + // Get the other side of this actor if it is an in-process actor. Returns // |nullptr| if the actor has been torn down, or is not in-process. already_AddRefed GetParentActor(); diff --git a/dom/ipc/WindowGlobalParent.cpp b/dom/ipc/WindowGlobalParent.cpp index 12263ca7c331..1aee4527108f 100644 --- a/dom/ipc/WindowGlobalParent.cpp +++ b/dom/ipc/WindowGlobalParent.cpp @@ -124,6 +124,19 @@ WindowGlobalParent::RecvUpdateDocumentURI(nsIURI* aURI) return IPC_OK(); } +IPCResult +WindowGlobalParent::RecvBecomeCurrentWindowGlobal() +{ + mBrowsingContext->SetCurrentWindowGlobal(this); + return IPC_OK(); +} + +bool +WindowGlobalParent::IsCurrentGlobal() +{ + return !mIPCClosed && mBrowsingContext->GetCurrentWindowGlobal() == this; +} + void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) { diff --git a/dom/ipc/WindowGlobalParent.h b/dom/ipc/WindowGlobalParent.h index aaae9bff08f3..e31e6ca767c2 100644 --- a/dom/ipc/WindowGlobalParent.h +++ b/dom/ipc/WindowGlobalParent.h @@ -71,6 +71,8 @@ public: uint64_t OuterWindowId() { return mOuterWindowId; } uint64_t InnerWindowId() { return mInnerWindowId; } + bool IsCurrentGlobal(); + // Create a WindowGlobalParent from over IPC. This method should not be called // from outside of the IPC constructors. WindowGlobalParent(const WindowGlobalInit& aInit, bool aInProcess); @@ -86,6 +88,7 @@ public: protected: // IPC messages mozilla::ipc::IPCResult RecvUpdateDocumentURI(nsIURI* aURI) override; + mozilla::ipc::IPCResult RecvBecomeCurrentWindowGlobal() override; void ActorDestroy(ActorDestroyReason aWhy) override; From fae9f6ec4465e860d29f4e68588e8c956caaab12 Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Thu, 29 Nov 2018 20:27:08 -0500 Subject: [PATCH 11/17] Bug 1511237 - Store a TabChild reference in cached docshells, r=bzbarsky, r=smaug Differential Revision: https://phabricator.services.mozilla.com/D13489 --- docshell/base/nsDocShell.cpp | 21 +++++++++++++++++++-- docshell/base/nsDocShell.h | 3 +++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index c6b93dde7355..096b8ce8de37 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -3193,6 +3193,22 @@ nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) { } } + // If we're in the content process and have had a TreeOwner set on us, extract + // our TabChild actor. If we've already had our TabChild set, assert that it + // hasn't changed. + if (mTreeOwner && XRE_IsContentProcess()) { + nsCOMPtr newTabChild = do_GetInterface(mTreeOwner); + MOZ_ASSERT(newTabChild, "No TabChild actor for tree owner in Content!"); + + if (mTabChild) { + nsCOMPtr oldTabChild = do_QueryReferent(mTabChild); + MOZ_RELEASE_ASSERT(oldTabChild == newTabChild, + "Cannot cahnge TabChild during nsDocShell lifetime!"); + } else { + mTabChild = do_GetWeakReference(newTabChild); + } + } + // Our tree owner has changed. Recompute scriptability. // // Note that this is near-redundant with the recomputation in @@ -5045,6 +5061,8 @@ nsDocShell::Destroy() { SetTreeOwner(nullptr); + mTabChild = nullptr; + mOnePermittedSandboxedNavigator = nullptr; // required to break ref cycle @@ -13321,8 +13339,7 @@ nsDocShell::GetScriptableTabChild(nsITabChild** aTabChild) { } already_AddRefed nsDocShell::GetTabChild() { - nsCOMPtr owner(mTreeOwner); - nsCOMPtr tc = do_GetInterface(owner); + nsCOMPtr tc = do_QueryReferent(mTabChild); return tc.forget(); } diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 225dbabfba1c..9bf4948c70e9 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -921,6 +921,9 @@ class nsDocShell final : public nsDocLoader, nsCOMPtr mCommandManager; RefPtr mBrowsingContext; + // Weak reference to our TabChild actor. + nsWeakPtr mTabChild; + // Dimensions of the docshell nsIntRect mBounds; From e1e08d3d11cd7a21b7516d96cde8fdf79e7e3468 Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Tue, 4 Dec 2018 17:18:31 -0500 Subject: [PATCH 12/17] Bug 1512085 - Don't overlap IDs between content and middleman process, r=bhackett Differential Revision: https://phabricator.services.mozilla.com/D13763 --- dom/base/nsContentUtils.cpp | 7 +++++++ dom/ipc/ContentChild.cpp | 7 +++++++ ipc/glue/ProtocolUtils.cpp | 26 ++++++++++++++++++++------ ipc/glue/ProtocolUtils.h | 5 +++-- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 06792ae417ed..8b26cee6dfc5 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -235,6 +235,7 @@ #include "mozilla/HangAnnotations.h" #include "mozilla/Encoding.h" #include "nsXULElement.h" +#include "mozilla/RecordReplay.h" #include "nsIBidiKeyboard.h" @@ -10096,6 +10097,12 @@ static const uint64_t kIdBits = 64 - kIdProcessBits; MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits)); uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1); + // Set the high bit for middleman processes so it doesn't conflict with the + // content process's generated IDs. + if (recordreplay::IsMiddleman()) { + bits |= uint64_t(1) << (kIdBits - 1); + } + return (processBits << kIdBits) | bits; } diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 1ff1a8cad0f3..fc32a8a706aa 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -2955,6 +2955,13 @@ uint64_t NextWindowID() { MOZ_RELEASE_ASSERT(windowID < (uint64_t(1) << kWindowIDWindowBits)); uint64_t windowBits = windowID & ((uint64_t(1) << kWindowIDWindowBits) - 1); + // Make sure that the middleman process doesn't generate WindowIDs which + // conflict with the process it's wrapping (which shares a ContentParentID + // with it). + if (recordreplay::IsMiddleman()) { + windowBits |= uint64_t(1) << (kWindowIDWindowBits - 1); + } + return (processBits << kWindowIDWindowBits) | windowBits; } diff --git a/ipc/glue/ProtocolUtils.cpp b/ipc/glue/ProtocolUtils.cpp index cbdac2566213..5e9b6a2612b8 100644 --- a/ipc/glue/ProtocolUtils.cpp +++ b/ipc/glue/ProtocolUtils.cpp @@ -642,13 +642,29 @@ bool IToplevelProtocol::IsOnCxxStack() const { return GetIPCChannel()->IsOnCxxStack(); } +int32_t IToplevelProtocol::ToplevelState::NextId() { + // Genreate the next ID to use for a shared memory or protocol. Parent and + // Child sides of the protocol use different pools, and actors created in the + // middleman need to use a distinct pool as well. + int32_t tag = 0; + if (recordreplay::IsMiddleman()) { + tag |= 1 << 0; + } + if (mProtocol->GetSide() == ParentSide) { + tag |= 1 << 1; + } + + // Compute the ID to use with the low two bits as our tag, and the remaining + // bits as a monotonic. + return (++mLastLocalId << 2) | tag; +} + int32_t IToplevelProtocol::ToplevelState::Register(IProtocol* aRouted) { if (aRouted->Id() != kNullActorId && aRouted->Id() != kFreedActorId) { // If there's already an ID, just return that. return aRouted->Id(); } - int32_t id = - mProtocol->GetSide() == ParentSide ? ++mLastRouteId : --mLastRouteId; + int32_t id = NextId(); mActorMap.AddWithID(aRouted, id); aRouted->SetId(id); @@ -687,8 +703,7 @@ IToplevelProtocol::ToplevelState::ToplevelState(const char* aName, Side aSide) : ProtocolState(), mProtocol(aProtocol), - mLastRouteId(aSide == ParentSide ? kFreedActorId : kNullActorId), - mLastShmemId(aSide == ParentSide ? kFreedActorId : kNullActorId), + mLastLocalId(0), mEventTargetMutex("ProtocolEventTargetMutex"), mChannel(aName, aProtocol) {} @@ -701,8 +716,7 @@ Shmem::SharedMemory* IToplevelProtocol::ToplevelState::CreateSharedMemory( if (!segment) { return nullptr; } - int32_t id = - mProtocol->GetSide() == ParentSide ? ++mLastShmemId : --mLastShmemId; + int32_t id = NextId(); Shmem shmem(Shmem::PrivateIPDLCaller(), segment.get(), id); base::ProcessId pid = diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h index 180714000ff1..a40cf68a9d8c 100644 --- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -438,11 +438,12 @@ class IToplevelProtocol : public IProtocol { MessageChannel* GetIPCChannel() override; private: + int32_t NextId(); + IToplevelProtocol* const mProtocol; + int32_t mLastLocalId; IDMap mActorMap; - int32_t mLastRouteId; IDMap mShmemMap; - Shmem::id_t mLastShmemId; Mutex mEventTargetMutex; IDMap> mEventTargetMap; From 2f74f21ff6eb474c31cd9014fa6d0f921141a41c Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Tue, 4 Dec 2018 18:51:29 +0000 Subject: [PATCH 13/17] Bug 1507806 - Use level 1 headings for major groups in Preferences. r=jaws Differential Revision: https://phabricator.services.mozilla.com/D13736 --HG-- extra : rebase_source : 1133912f66de475be190e3342784707436d946c6 --- .../preferences/in-content/containers.xul | 2 +- browser/components/preferences/in-content/home.xul | 2 +- browser/components/preferences/in-content/main.xul | 14 +++++++------- .../components/preferences/in-content/privacy.xul | 8 ++++---- .../components/preferences/in-content/search.xul | 2 +- .../preferences/in-content/searchResults.xul | 2 +- browser/components/preferences/in-content/sync.xul | 2 +- .../shared/incontentprefs/preferences.inc.css | 11 +++++++---- 8 files changed, 23 insertions(+), 20 deletions(-) diff --git a/browser/components/preferences/in-content/containers.xul b/browser/components/preferences/in-content/containers.xul index a4bac91ce3c7..a6ac341fadda 100644 --- a/browser/components/preferences/in-content/containers.xul +++ b/browser/components/preferences/in-content/containers.xul @@ -17,7 +17,7 @@ class="header" hidden="true" data-category="paneContainers"> -