fune/dom/clients/manager/ClientSourceParent.cpp
Emilio Cobos Álvarez 25c0d10932 Bug 1624819 - Remove TaskCategory and other quantum dom remnants. r=smaug,media-playback-reviewers,credential-management-reviewers,cookie-reviewers,places-reviewers,win-reviewers,valentin,mhowell,sgalich,alwu
Sorry this is not a particularly easy patch to review. But it should be
mostly straight-forward.

I kept Document::Dispatch mostly for convenience, but could be
cleaned-up too / changed by SchedulerGroup::Dispatch. Similarly maybe
that can just be NS_DispatchToMainThread if we add an NS_IsMainThread
check there or something (to preserve shutdown semantics).

Differential Revision: https://phabricator.services.mozilla.com/D190450
2023-10-10 08:51:12 +00:00

294 lines
9.9 KiB
C++

/* -*- 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 "ClientSourceParent.h"
#include "ClientHandleParent.h"
#include "ClientManagerService.h"
#include "ClientSourceOpParent.h"
#include "ClientValidation.h"
#include "mozilla/dom/ClientIPCTypes.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/PClientManagerParent.h"
#include "mozilla/dom/ServiceWorkerManager.h"
#include "mozilla/dom/ServiceWorkerUtils.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/SchedulerGroup.h"
#include "mozilla/Unused.h"
namespace mozilla::dom {
using mozilla::ipc::AssertIsOnBackgroundThread;
using mozilla::ipc::BackgroundParent;
using mozilla::ipc::IPCResult;
using mozilla::ipc::PrincipalInfo;
namespace {
// It would be nice to use a lambda instead of this class, but we cannot
// move capture in lambdas yet and ContentParent cannot be AddRef'd off
// the main thread.
class KillContentParentRunnable final : public Runnable {
RefPtr<ThreadsafeContentParentHandle> mHandle;
public:
explicit KillContentParentRunnable(
RefPtr<ThreadsafeContentParentHandle>&& aHandle)
: Runnable("KillContentParentRunnable"), mHandle(std::move(aHandle)) {
MOZ_ASSERT(mHandle);
}
NS_IMETHOD
Run() override {
AssertIsOnMainThread();
if (RefPtr<ContentParent> contentParent = mHandle->GetContentParent()) {
contentParent->KillHard("invalid ClientSourceParent actor");
}
return NS_OK;
}
};
} // anonymous namespace
void ClientSourceParent::KillInvalidChild() {
// Try to get the content process before we destroy the actor below.
RefPtr<ThreadsafeContentParentHandle> process =
BackgroundParent::GetContentParentHandle(Manager()->Manager());
// First, immediately teardown the ClientSource actor. No matter what
// we want to start this process as soon as possible.
Unused << ClientSourceParent::Send__delete__(this);
// If we are running in non-e10s, then there is nothing else to do here.
// There is no child process and we don't want to crash the entire browser
// in release builds. In general, though, this should not happen in non-e10s
// so we do assert this condition.
if (!process) {
MOZ_DIAGNOSTIC_ASSERT(false, "invalid ClientSourceParent in non-e10s");
return;
}
// In e10s mode we also want to kill the child process. Validation failures
// typically mean someone sent us bogus data over the IPC link. We can't
// trust that process any more. We have to do this on the main thread, so
// there is a small window of time before we kill the process. This is why
// we start the actor destruction immediately above.
nsCOMPtr<nsIRunnable> r = new KillContentParentRunnable(std::move(process));
MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
}
mozilla::ipc::IPCResult ClientSourceParent::RecvWorkerSyncPing() {
AssertIsOnBackgroundThread();
// Do nothing here. This is purely a sync message allowing the child to
// confirm that the actor has been created on the parent process.
return IPC_OK();
}
IPCResult ClientSourceParent::RecvTeardown() {
Unused << Send__delete__(this);
return IPC_OK();
}
IPCResult ClientSourceParent::RecvExecutionReady(
const ClientSourceExecutionReadyArgs& aArgs) {
// Now that we have the creation URL for the Client we can do some validation
// to make sure the child actor is not giving us garbage. Since we validate
// on the child side as well we treat a failure here as fatal.
if (!ClientIsValidCreationURL(mClientInfo.PrincipalInfo(), aArgs.url())) {
KillInvalidChild();
return IPC_OK();
}
mClientInfo.SetURL(aArgs.url());
mClientInfo.SetFrameType(aArgs.frameType());
mExecutionReady = true;
for (ClientHandleParent* handle : mHandleList) {
Unused << handle->SendExecutionReady(mClientInfo.ToIPC());
}
mExecutionReadyPromise.ResolveIfExists(true, __func__);
return IPC_OK();
};
IPCResult ClientSourceParent::RecvFreeze() {
#ifdef FUZZING_SNAPSHOT
if (mFrozen) {
return IPC_FAIL(this, "Freezing when already frozen");
}
#endif
MOZ_DIAGNOSTIC_ASSERT(!mFrozen);
mFrozen = true;
return IPC_OK();
}
IPCResult ClientSourceParent::RecvThaw() {
#ifdef FUZZING_SNAPSHOT
if (!mFrozen) {
return IPC_FAIL(this, "Thawing when not already frozen");
}
#endif
MOZ_DIAGNOSTIC_ASSERT(mFrozen);
mFrozen = false;
return IPC_OK();
}
IPCResult ClientSourceParent::RecvInheritController(
const ClientControlledArgs& aArgs) {
mController.reset();
mController.emplace(aArgs.serviceWorker());
// We must tell the parent-side SWM about this controller inheritance.
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
"ClientSourceParent::RecvInheritController",
[clientInfo = mClientInfo, controller = mController.ref()]() {
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
NS_ENSURE_TRUE_VOID(swm);
swm->NoteInheritedController(clientInfo, controller);
});
MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
return IPC_OK();
}
IPCResult ClientSourceParent::RecvNoteDOMContentLoaded() {
if (mController.isSome()) {
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction("ClientSourceParent::RecvNoteDOMContentLoaded",
[clientInfo = mClientInfo]() {
RefPtr<ServiceWorkerManager> swm =
ServiceWorkerManager::GetInstance();
NS_ENSURE_TRUE_VOID(swm);
swm->MaybeCheckNavigationUpdate(clientInfo);
});
MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
}
return IPC_OK();
}
void ClientSourceParent::ActorDestroy(ActorDestroyReason aReason) {
DebugOnly<bool> removed = mService->RemoveSource(this);
MOZ_ASSERT(removed);
for (ClientHandleParent* handle : mHandleList.Clone()) {
// This should trigger DetachHandle() to be called removing
// the entry from the mHandleList.
Unused << ClientHandleParent::Send__delete__(handle);
}
MOZ_DIAGNOSTIC_ASSERT(mHandleList.IsEmpty());
}
PClientSourceOpParent* ClientSourceParent::AllocPClientSourceOpParent(
const ClientOpConstructorArgs& aArgs) {
MOZ_ASSERT_UNREACHABLE(
"ClientSourceOpParent should be explicitly constructed.");
return nullptr;
}
bool ClientSourceParent::DeallocPClientSourceOpParent(
PClientSourceOpParent* aActor) {
delete aActor;
return true;
}
ClientSourceParent::ClientSourceParent(
const ClientSourceConstructorArgs& aArgs,
const Maybe<ContentParentId>& aContentParentId)
: mClientInfo(aArgs.id(), aArgs.type(), aArgs.principalInfo(),
aArgs.creationTime()),
mContentParentId(aContentParentId),
mService(ClientManagerService::GetOrCreateInstance()),
mExecutionReady(false),
mFrozen(false) {}
ClientSourceParent::~ClientSourceParent() {
MOZ_DIAGNOSTIC_ASSERT(mHandleList.IsEmpty());
mExecutionReadyPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
}
void ClientSourceParent::Init() {
// Ensure the principal is reasonable before adding ourself to the service.
// Since we validate the principal on the child side as well, any failure
// here is treated as fatal.
if (NS_WARN_IF(!ClientIsValidPrincipalInfo(mClientInfo.PrincipalInfo()))) {
mService->ForgetFutureSource(mClientInfo.ToIPC());
KillInvalidChild();
return;
}
// Its possible for AddSource() to fail if there is already an entry for
// our UUID. This should not normally happen, but could if someone is
// spoofing IPC messages.
if (NS_WARN_IF(!mService->AddSource(this))) {
KillInvalidChild();
return;
}
}
const ClientInfo& ClientSourceParent::Info() const { return mClientInfo; }
bool ClientSourceParent::IsFrozen() const { return mFrozen; }
bool ClientSourceParent::ExecutionReady() const { return mExecutionReady; }
RefPtr<GenericNonExclusivePromise> ClientSourceParent::ExecutionReadyPromise() {
// Only call if ClientSourceParent::ExecutionReady() is false; otherwise,
// the promise will never resolve
MOZ_ASSERT(!mExecutionReady);
return mExecutionReadyPromise.Ensure(__func__);
}
const Maybe<ServiceWorkerDescriptor>& ClientSourceParent::GetController()
const {
return mController;
}
void ClientSourceParent::ClearController() { mController.reset(); }
void ClientSourceParent::AttachHandle(ClientHandleParent* aClientHandle) {
MOZ_DIAGNOSTIC_ASSERT(aClientHandle);
MOZ_ASSERT(!mHandleList.Contains(aClientHandle));
mHandleList.AppendElement(aClientHandle);
}
void ClientSourceParent::DetachHandle(ClientHandleParent* aClientHandle) {
MOZ_DIAGNOSTIC_ASSERT(aClientHandle);
MOZ_ASSERT(mHandleList.Contains(aClientHandle));
mHandleList.RemoveElement(aClientHandle);
}
RefPtr<ClientOpPromise> ClientSourceParent::StartOp(
ClientOpConstructorArgs&& aArgs) {
RefPtr<ClientOpPromise::Private> promise =
new ClientOpPromise::Private(__func__);
// If we are being controlled, remember that data before propagating
// on to the ClientSource. This must be set prior to triggering
// the controllerchange event from the ClientSource since some tests
// expect matchAll() to find the controlled client immediately after.
// If the control operation fails, then we reset the controller value
// to reflect the final state.
if (aArgs.type() == ClientOpConstructorArgs::TClientControlledArgs) {
mController.reset();
mController.emplace(aArgs.get_ClientControlledArgs().serviceWorker());
}
// Constructor failure will reject the promise via ActorDestroy().
ClientSourceOpParent* actor =
new ClientSourceOpParent(std::move(aArgs), promise);
Unused << SendPClientSourceOpConstructor(actor, actor->Args());
return promise;
}
} // namespace mozilla::dom