gecko-dev/dom/serviceworkers/ServiceWorkerManagerService.cpp
Andrew Sutherland 4945715675 Bug 1589708 - Restore propagateUnregister as API to wipe registrations. r=perry
Owing to a lack of test coverage, the changes in bug 1469048 regressed our
de facto privacy/devtools API at nsIServiceWorkerManager.propagateUnregister
to no longer wipe the given registration.  The intent of that bug's changes
was to stop the now-moot propagation, but that particular intentional mis-use
of the API was missed.

This patch fixes the problem but maintains the parent-intercept behavior of
only calling ServiceWorkerRegistrar::UnregisterServiceWorker only once.

Differential Revision: https://phabricator.services.mozilla.com/D49996

--HG--
extra : moz-landing-system : lando
2019-10-22 01:48:37 +00:00

306 lines
8.1 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 "ServiceWorkerManagerService.h"
#include "ServiceWorkerManagerParent.h"
#include "ServiceWorkerRegistrar.h"
#include "ServiceWorkerUpdaterParent.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/Unused.h"
#include "nsAutoPtr.h"
namespace mozilla {
using namespace ipc;
namespace dom {
namespace {
ServiceWorkerManagerService* sInstance = nullptr;
} // namespace
ServiceWorkerManagerService::ServiceWorkerManagerService() {
AssertIsOnBackgroundThread();
// sInstance is a raw ServiceWorkerManagerService*.
MOZ_ASSERT(!sInstance);
sInstance = this;
}
ServiceWorkerManagerService::~ServiceWorkerManagerService() {
AssertIsOnBackgroundThread();
MOZ_ASSERT(sInstance == this);
MOZ_ASSERT(mAgents.Count() == 0);
sInstance = nullptr;
}
/* static */
already_AddRefed<ServiceWorkerManagerService>
ServiceWorkerManagerService::Get() {
AssertIsOnBackgroundThread();
RefPtr<ServiceWorkerManagerService> instance = sInstance;
return instance.forget();
}
/* static */
already_AddRefed<ServiceWorkerManagerService>
ServiceWorkerManagerService::GetOrCreate() {
AssertIsOnBackgroundThread();
RefPtr<ServiceWorkerManagerService> instance = sInstance;
if (!instance) {
instance = new ServiceWorkerManagerService();
}
return instance.forget();
}
void ServiceWorkerManagerService::RegisterActor(
ServiceWorkerManagerParent* aParent) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aParent);
MOZ_ASSERT(!mAgents.Contains(aParent));
mAgents.PutEntry(aParent);
}
void ServiceWorkerManagerService::UnregisterActor(
ServiceWorkerManagerParent* aParent) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aParent);
MOZ_ASSERT(mAgents.Contains(aParent));
mAgents.RemoveEntry(aParent);
}
void ServiceWorkerManagerService::PropagateRegistration(
uint64_t aParentID, ServiceWorkerRegistrationData& aData) {
AssertIsOnBackgroundThread();
if (ServiceWorkerParentInterceptEnabled()) {
return;
}
DebugOnly<bool> parentFound = false;
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != aParentID) {
Unused << parent->SendNotifyRegister(aData);
#ifdef DEBUG
} else {
parentFound = true;
#endif
}
}
// Send permissions fot the newly registered service worker to all of the
// content processes.
PrincipalInfo pi = aData.principal();
NS_DispatchToMainThread(NS_NewRunnableFunction(
"dom::ServiceWorkerManagerService::PropagateRegistration", [pi]() {
nsTArray<ContentParent*> cps;
ContentParent::GetAll(cps);
for (auto* cp : cps) {
nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(pi);
if (principal) {
cp->TransmitPermissionsForPrincipal(principal);
}
}
}));
#ifdef DEBUG
MOZ_ASSERT(parentFound);
#endif
}
void ServiceWorkerManagerService::PropagateSoftUpdate(
uint64_t aParentID, const OriginAttributes& aOriginAttributes,
const nsAString& aScope) {
AssertIsOnBackgroundThread();
if (ServiceWorkerParentInterceptEnabled()) {
return;
}
DebugOnly<bool> parentFound = false;
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
MOZ_ASSERT(parent);
nsString scope(aScope);
Unused << parent->SendNotifySoftUpdate(aOriginAttributes, scope);
#ifdef DEBUG
if (parent->ID() == aParentID) {
parentFound = true;
}
#endif
}
#ifdef DEBUG
MOZ_ASSERT(parentFound);
#endif
}
void ServiceWorkerManagerService::PropagateUnregister(
uint64_t aParentID, const PrincipalInfo& aPrincipalInfo,
const nsAString& aScope) {
AssertIsOnBackgroundThread();
RefPtr<dom::ServiceWorkerRegistrar> service =
dom::ServiceWorkerRegistrar::Get();
MOZ_ASSERT(service);
// It's possible that we don't have any ServiceWorkerManager managing this
// scope but we still need to unregister it from the ServiceWorkerRegistrar.
service->UnregisterServiceWorker(aPrincipalInfo,
NS_ConvertUTF16toUTF8(aScope));
// There is no longer any point to propagating because the only sender is the
// one and only ServiceWorkerManager, but it is necessary for us to have run
// the unregister call above because until Bug 1183245 is fixed,
// nsIServiceWorkerManager.propagateUnregister() is a de facto API for
// clearing ServiceWorker registrations by Sanitizer.jsm via
// ServiceWorkerCleanUp.jsm, as well as devtools "unregister" affordance and
// the no-longer-relevant about:serviceworkers UI.
if (ServiceWorkerParentInterceptEnabled()) {
return;
}
DebugOnly<bool> parentFound = false;
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != aParentID) {
nsString scope(aScope);
Unused << parent->SendNotifyUnregister(aPrincipalInfo, scope);
#ifdef DEBUG
} else {
parentFound = true;
#endif
}
}
#ifdef DEBUG
MOZ_ASSERT(parentFound);
#endif
}
void ServiceWorkerManagerService::PropagateRemove(uint64_t aParentID,
const nsACString& aHost) {
AssertIsOnBackgroundThread();
if (ServiceWorkerParentInterceptEnabled()) {
return;
}
DebugOnly<bool> parentFound = false;
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != aParentID) {
nsCString host(aHost);
Unused << parent->SendNotifyRemove(host);
#ifdef DEBUG
} else {
parentFound = true;
#endif
}
}
#ifdef DEBUG
MOZ_ASSERT(parentFound);
#endif
}
void ServiceWorkerManagerService::PropagateRemoveAll(uint64_t aParentID) {
AssertIsOnBackgroundThread();
if (ServiceWorkerParentInterceptEnabled()) {
return;
}
RefPtr<dom::ServiceWorkerRegistrar> service =
dom::ServiceWorkerRegistrar::Get();
MOZ_ASSERT(service);
service->RemoveAll();
DebugOnly<bool> parentFound = false;
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != aParentID) {
Unused << parent->SendNotifyRemoveAll();
#ifdef DEBUG
} else {
parentFound = true;
#endif
}
}
#ifdef DEBUG
MOZ_ASSERT(parentFound);
#endif
}
void ServiceWorkerManagerService::ProcessUpdaterActor(
ServiceWorkerUpdaterParent* aActor,
const OriginAttributes& aOriginAttributes, const nsACString& aScope,
uint64_t aParentId) {
AssertIsOnBackgroundThread();
MOZ_DIAGNOSTIC_ASSERT(!ServiceWorkerParentInterceptEnabled());
nsAutoCString suffix;
aOriginAttributes.CreateSuffix(suffix);
nsCString scope(aScope);
scope.Append(suffix);
for (uint32_t i = 0; i < mPendingUpdaterActors.Length(); ++i) {
// We already have an actor doing this update on another process.
if (mPendingUpdaterActors[i].mScope.Equals(scope) &&
mPendingUpdaterActors[i].mParentId != aParentId) {
Unused << aActor->SendProceed(false);
return;
}
}
if (aActor->Proceed(this)) {
PendingUpdaterActor* pua = mPendingUpdaterActors.AppendElement();
pua->mActor = aActor;
pua->mScope = scope;
pua->mParentId = aParentId;
}
}
void ServiceWorkerManagerService::UpdaterActorDestroyed(
ServiceWorkerUpdaterParent* aActor) {
for (uint32_t i = 0; i < mPendingUpdaterActors.Length(); ++i) {
// We already have an actor doing the update for this scope.
if (mPendingUpdaterActors[i].mActor == aActor) {
mPendingUpdaterActors.RemoveElementAt(i);
return;
}
}
MOZ_CRASH("The actor should be found");
}
} // namespace dom
} // namespace mozilla