/* -*- 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/ClearOnShutdown.h" #include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/ipc/InProcessChild.h" #include "mozilla/dom/BrowsingContext.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/MozFrameLoaderOwnerBinding.h" #include "mozilla/dom/BrowserChild.h" #include "mozilla/dom/BrowserBridgeChild.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/WindowGlobalActorsBinding.h" #include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/ipc/InProcessChild.h" #include "nsDocShell.h" #include "nsFrameLoaderOwner.h" #include "nsGlobalWindowInner.h" #include "nsFrameLoaderOwner.h" #include "nsQueryObject.h" #include "mozilla/dom/JSWindowActorBinding.h" #include "mozilla/dom/JSWindowActorChild.h" #include "mozilla/dom/JSWindowActorService.h" #include "nsIHttpChannelInternal.h" using namespace mozilla::ipc; using namespace mozilla::dom::ipc; namespace mozilla { namespace dom { typedef nsRefPtrHashtable WGCByIdMap; static StaticAutoPtr gWindowGlobalChildById; WindowGlobalChild::WindowGlobalChild(nsGlobalWindowInner* aWindow, dom::BrowsingContext* aBrowsingContext) : mWindowGlobal(aWindow), mBrowsingContext(aBrowsingContext), mInnerWindowId(aWindow->WindowID()), mOuterWindowId(aWindow->GetOuterWindow()->WindowID()), mIPCClosed(true) {} 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(); // When creating a new window global child we also need to look at the // channel's Cross-Origin-Opener-Policy and set it on the browsing context // so it's available in the parent process. nsCOMPtr chan = do_QueryInterface(aWindow->GetDocument()->GetChannel()); nsILoadInfo::CrossOriginOpenerPolicy policy; if (chan && NS_SUCCEEDED(chan->GetCrossOriginOpenerPolicy(&policy))) { bc->SetOpenerPolicy(policy); } RefPtr wgc = new WindowGlobalChild(aWindow, bc); // If we have already closed our browsing context, return a pre-closed // WindowGlobalChild actor. if (bc->GetClosed()) { wgc->ActorDestroy(FailedConstructor); return wgc.forget(); } WindowGlobalInit init(principal, aWindow->GetDocumentURI(), bc, wgc->mInnerWindowId, wgc->mOuterWindowId); // 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 browserChild = BrowserChild::GetFrom(static_cast(aWindow)); MOZ_ASSERT(browserChild); // Note: ref is released in DeallocPWindowGlobalChild browserChild->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); } bool WindowGlobalChild::IsCurrentGlobal() { return !mIPCClosed && mWindowGlobal->IsCurrentInnerWindow(); } already_AddRefed WindowGlobalChild::GetParentActor() { if (mIPCClosed) { return nullptr; } IProtocol* otherSide = InProcessChild::ParentActorFor(this); return do_AddRef(static_cast(otherSide)); } already_AddRefed WindowGlobalChild::GetBrowserChild() { if (IsInProcess() || mIPCClosed) { return nullptr; } return do_AddRef(static_cast(Manager())); } void WindowGlobalChild::Destroy() { // Perform async IPC shutdown unless we're not in-process, and our // BrowserChild is in the process of being destroyed, which will destroy us as // well. RefPtr browserChild = GetBrowserChild(); if (!browserChild || !browserChild->IsDestroyed()) { // Make a copy so that we can avoid potential iterator invalidation when // calling the user-provided Destroy() methods. nsTArray> windowActors(mWindowActors.Count()); for (auto iter = mWindowActors.Iter(); !iter.Done(); iter.Next()) { windowActors.AppendElement(iter.UserData()); } for (auto& windowActor : windowActors) { windowActor->StartDestroy(); } SendDestroy(); } mIPCClosed = true; } static nsresult ChangeFrameRemoteness(WindowGlobalChild* aWgc, BrowsingContext* aBc, const nsString& aRemoteType, uint64_t aPendingSwitchId, BrowserBridgeChild** aBridge) { MOZ_ASSERT(XRE_IsContentProcess(), "This doesn't make sense in the parent"); // Get the target embedder's FrameLoaderOwner, and make sure we're in the // right place. RefPtr embedderElt = aBc->GetEmbedderElement(); if (!embedderElt) { return NS_ERROR_NOT_AVAILABLE; } if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != aWgc->WindowGlobal())) { return NS_ERROR_UNEXPECTED; } RefPtr flo = do_QueryObject(embedderElt); MOZ_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner!"); MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); // Actually perform the remoteness swap. RemotenessOptions options; options.mRemoteType.Construct(aRemoteType); options.mPendingSwitchID.Construct(aPendingSwitchId); ErrorResult error; flo->ChangeRemoteness(options, error); if (NS_WARN_IF(error.Failed())) { return error.StealNSResult(); } // Make sure we successfully created either an in-process nsDocShell or a // cross-process BrowserBridgeChild. If we didn't, produce an error. RefPtr frameLoader = flo->GetFrameLoader(); if (NS_WARN_IF(!frameLoader)) { return NS_ERROR_FAILURE; } RefPtr bbc; if (frameLoader->IsRemoteFrame()) { bbc = frameLoader->GetBrowserBridgeChild(); if (NS_WARN_IF(!bbc)) { return NS_ERROR_FAILURE; } } else { nsDocShell* ds = frameLoader->GetDocShell(error); if (NS_WARN_IF(error.Failed())) { return error.StealNSResult(); } if (NS_WARN_IF(!ds)) { return NS_ERROR_FAILURE; } } bbc.forget(aBridge); return NS_OK; } IPCResult WindowGlobalChild::RecvChangeFrameRemoteness( dom::BrowsingContext* aBc, const nsString& aRemoteType, uint64_t aPendingSwitchId, ChangeFrameRemotenessResolver&& aResolver) { MOZ_ASSERT(XRE_IsContentProcess(), "This doesn't make sense in the parent"); RefPtr bbc; nsresult rv = ChangeFrameRemoteness(this, aBc, aRemoteType, aPendingSwitchId, getter_AddRefs(bbc)); // To make the type system happy, we've gotta do some gymnastics. aResolver(Tuple(rv, bbc)); return IPC_OK(); } IPCResult WindowGlobalChild::RecvRawMessage( const JSWindowActorMessageMeta& aMeta, const ClonedMessageData& aData) { StructuredCloneData data; data.BorrowFromClonedMessageDataForChild(aData); ReceiveRawMessage(aMeta, std::move(data)); return IPC_OK(); } void WindowGlobalChild::ReceiveRawMessage(const JSWindowActorMessageMeta& aMeta, StructuredCloneData&& aData) { RefPtr actor = GetActor(aMeta.actorName(), IgnoreErrors()); if (actor) { actor->ReceiveRawMessage(aMeta, std::move(aData)); } } nsIURI* WindowGlobalChild::GetDocumentURI() { return mWindowGlobal->GetDocumentURI(); } const nsAString& WindowGlobalChild::GetRemoteType() { if (XRE_IsContentProcess()) { return ContentChild::GetSingleton()->GetRemoteType(); } return VoidString(); } already_AddRefed WindowGlobalChild::GetActor( const nsAString& aName, ErrorResult& aRv) { if (mIPCClosed) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } // Check if this actor has already been created, and return it if it has. if (mWindowActors.Contains(aName)) { return do_AddRef(mWindowActors.GetWeak(aName)); } // Otherwise, we want to create a new instance of this actor. JS::RootedObject obj(RootingCx()); ConstructActor(aName, &obj, aRv); if (aRv.Failed()) { return nullptr; } // Unwrap our actor to a JSWindowActorChild object. RefPtr actor; if (NS_FAILED(UNWRAP_OBJECT(JSWindowActorChild, &obj, actor))) { return nullptr; } MOZ_RELEASE_ASSERT(!actor->Manager(), "mManager was already initialized once!"); actor->Init(aName, this); mWindowActors.Put(aName, actor); return actor.forget(); } void WindowGlobalChild::ActorDestroy(ActorDestroyReason aWhy) { mIPCClosed = true; gWindowGlobalChildById->Remove(mInnerWindowId); // Destroy our JSWindowActors, and reject any pending queries. nsRefPtrHashtable windowActors; mWindowActors.SwapElements(windowActors); for (auto iter = windowActors.Iter(); !iter.Done(); iter.Next()) { iter.Data()->RejectPendingQueries(); iter.Data()->AfterDestroy(); } windowActors.Clear(); } WindowGlobalChild::~WindowGlobalChild() { MOZ_ASSERT(!gWindowGlobalChildById || !gWindowGlobalChildById->Contains(mInnerWindowId)); MOZ_ASSERT(!mWindowActors.Count()); } 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_INHERITED(WindowGlobalChild, WindowGlobalActor, mWindowGlobal, mBrowsingContext, mWindowActors) NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WindowGlobalChild, WindowGlobalActor) NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalChild) NS_INTERFACE_MAP_END_INHERITING(WindowGlobalActor) NS_IMPL_ADDREF_INHERITED(WindowGlobalChild, WindowGlobalActor) NS_IMPL_RELEASE_INHERITED(WindowGlobalChild, WindowGlobalActor) } // namespace dom } // namespace mozilla