/* -*- 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 "RemoteWorkerManager.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/RemoteWorkerParent.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/PBackgroundParent.h" #include "nsIXULRuntime.h" #include "RemoteWorkerServiceParent.h" namespace mozilla { using namespace ipc; namespace dom { namespace { // Raw pointer because this object is kept alive by RemoteWorkerServiceParent // actors. RemoteWorkerManager* sRemoteWorkerManager; } // namespace /* static */ already_AddRefed RemoteWorkerManager::GetOrCreate() { AssertIsOnBackgroundThread(); MOZ_ASSERT(XRE_IsParentProcess()); if (!sRemoteWorkerManager) { sRemoteWorkerManager = new RemoteWorkerManager(); } RefPtr rwm = sRemoteWorkerManager; return rwm.forget(); } RemoteWorkerManager::RemoteWorkerManager() : mParentActor(nullptr) { AssertIsOnBackgroundThread(); MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(!sRemoteWorkerManager); } RemoteWorkerManager::~RemoteWorkerManager() { AssertIsOnBackgroundThread(); MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(sRemoteWorkerManager == this); sRemoteWorkerManager = nullptr; } void RemoteWorkerManager::RegisterActor(RemoteWorkerServiceParent* aActor) { AssertIsOnBackgroundThread(); MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(aActor); if (!BackgroundParent::IsOtherProcessActor(aActor->Manager())) { MOZ_ASSERT(!mParentActor); mParentActor = aActor; MOZ_ASSERT(mPendings.IsEmpty()); return; } MOZ_ASSERT(!mChildActors.Contains(aActor)); mChildActors.AppendElement(aActor); if (!mPendings.IsEmpty()) { // Flush pending launching. for (const Pending& p : mPendings) { LaunchInternal(p.mController, aActor, p.mData); } mPendings.Clear(); // We don't need to keep this manager alive manually. The Actor is doing it // for us. Release(); } } void RemoteWorkerManager::UnregisterActor(RemoteWorkerServiceParent* aActor) { AssertIsOnBackgroundThread(); MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(aActor); if (aActor == mParentActor) { mParentActor = nullptr; } else { MOZ_ASSERT(mChildActors.Contains(aActor)); mChildActors.RemoveElement(aActor); } } void RemoteWorkerManager::Launch(RemoteWorkerController* aController, const RemoteWorkerData& aData, base::ProcessId aProcessId) { AssertIsOnBackgroundThread(); MOZ_ASSERT(XRE_IsParentProcess()); RemoteWorkerServiceParent* targetActor = SelectTargetActor(aData, aProcessId); // If there is not an available actor, let's store the data, and let's spawn a // new process. if (!targetActor) { // If this is the first time we have a pending launching, we must keep alive // the manager. if (mPendings.IsEmpty()) { AddRef(); } Pending* pending = mPendings.AppendElement(); pending->mController = aController; pending->mData = aData; LaunchNewContentProcess(); return; } LaunchInternal(aController, targetActor, aData); } void RemoteWorkerManager::LaunchInternal( RemoteWorkerController* aController, RemoteWorkerServiceParent* aTargetActor, const RemoteWorkerData& aData) { AssertIsOnBackgroundThread(); MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(aController); MOZ_ASSERT(aTargetActor); MOZ_ASSERT(aTargetActor == mParentActor || mChildActors.Contains(aTargetActor)); RemoteWorkerParent* workerActor = static_cast( aTargetActor->Manager()->SendPRemoteWorkerConstructor(aData)); if (NS_WARN_IF(!workerActor)) { AsyncCreationFailed(aController); return; } workerActor->Initialize(); // This makes the link better the 2 actors. aController->SetWorkerActor(workerActor); workerActor->SetController(aController); } void RemoteWorkerManager::AsyncCreationFailed( RemoteWorkerController* aController) { RefPtr controller = aController; nsCOMPtr r = NS_NewRunnableFunction("RemoteWorkerManager::AsyncCreationFailed", [controller]() { controller->CreationFailed(); }); NS_DispatchToCurrentThread(r.forget()); } RemoteWorkerServiceParent* RemoteWorkerManager::SelectTargetActor( const RemoteWorkerData& aData, base::ProcessId aProcessId) { AssertIsOnBackgroundThread(); MOZ_ASSERT(XRE_IsParentProcess()); // System principal workers should run on the parent process. if (aData.principalInfo().type() == PrincipalInfo::TSystemPrincipalInfo) { MOZ_ASSERT(mParentActor); return mParentActor; } // If e10s is off, use the parent process. if (!BrowserTabsRemoteAutostart()) { MOZ_ASSERT(mParentActor); return mParentActor; } // We shouldn't have to worry about content-principal parent-process workers. MOZ_ASSERT(aProcessId != base::GetCurrentProcId()); if (mChildActors.IsEmpty()) { return nullptr; } for (RemoteWorkerServiceParent* actor : mChildActors) { // Let's execute the RemoteWorker on the same process. if (actor->OtherPid() == aProcessId) { return actor; } } // Let's choose an actor, randomly. uint32_t id = uint32_t(rand()) % mChildActors.Length(); return mChildActors[id]; } void RemoteWorkerManager::LaunchNewContentProcess() { AssertIsOnBackgroundThread(); MOZ_ASSERT(XRE_IsParentProcess()); // This runnable will spawn a new process if it doesn't exist yet. nsCOMPtr r = NS_NewRunnableFunction("LaunchNewContentProcess", []() { RefPtr unused = ContentParent::GetNewOrUsedBrowserProcess( nullptr, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)); }); nsCOMPtr target = SystemGroup::EventTargetFor(TaskCategory::Other); target->Dispatch(r.forget(), NS_DISPATCH_NORMAL); } } // namespace dom } // namespace mozilla