mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-17 22:32:51 +00:00
Bug 1425975 P7 Use the mControlledClients list to drive controller start and stop logic. r=asuth
This commit is contained in:
parent
aaf7a2cb50
commit
c67746c9b2
@ -155,6 +155,7 @@
|
||||
#include "mozIAsyncFavicons.h"
|
||||
#endif
|
||||
#include "nsINetworkPredictor.h"
|
||||
#include "nsIServiceWorkerManager.h"
|
||||
|
||||
// Editor-related
|
||||
#include "nsIEditingSession.h"
|
||||
@ -3459,13 +3460,27 @@ nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal)
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (!swm) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the parent is controlled then propagate that controller to the
|
||||
// initial about:blank client as well. This will set the controller
|
||||
// in the ClientManagerService in the parent.
|
||||
RefPtr<ClientHandle> handle =
|
||||
ClientManager::CreateHandle(mInitialClientSource->Info(),
|
||||
parentInner->EventTargetFor(TaskCategory::Other));
|
||||
handle->Control(controller.ref());
|
||||
//
|
||||
// Note: If the registration is missing from the SWM we avoid setting
|
||||
// the controller on the client. We can do this synchronously
|
||||
// for now since SWM is in the child process. In the future
|
||||
// when SWM is in the parent process we will probably have to
|
||||
// always set the initial client source and then somehow clear
|
||||
// it if we find the registration is acutally gone. Its also
|
||||
// possible this race only occurs in cases where the resulting
|
||||
// window is no longer exposed. For example, in theory the SW
|
||||
// should not go away if our parent window is controlled.
|
||||
if (!swm->StartControlling(mInitialClientSource->Info(), controller.ref())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Also mark the ClientSource as controlled directly in case script
|
||||
// immediately accesses navigator.serviceWorker.controller.
|
||||
|
@ -15,6 +15,17 @@ interface nsIInterceptedChannel;
|
||||
interface nsIPrincipal;
|
||||
interface nsIRunnable;
|
||||
interface nsIURI;
|
||||
%{C++
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class ClientInfo;
|
||||
class ServiceWorkerDescriptor;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
%}
|
||||
|
||||
[ref] native const_ClientInfoRef(const mozilla::dom::ClientInfo);
|
||||
[ref] native const_ServiceWorkerDescriptorRef(const mozilla::dom::ServiceWorkerDescriptor);
|
||||
|
||||
[scriptable, uuid(52ee2c9d-ee87-4caf-9588-23ae77ff8798)]
|
||||
interface nsIServiceWorkerUnregisterCallback : nsISupports
|
||||
@ -150,6 +161,9 @@ interface nsIServiceWorkerManager : nsISupports
|
||||
*/
|
||||
[notxpcom,nostdcall] void MaybeStartControlling(in nsIDocument aDoc);
|
||||
|
||||
[notxpcom, nostdcall] bool StartControlling(in const_ClientInfoRef aClientInfo,
|
||||
in const_ServiceWorkerDescriptorRef aServiceWorker);
|
||||
|
||||
/**
|
||||
* Documents that have called MaybeStartControlling() should call this when
|
||||
* they are destroyed. This function may be called multiple times, and is
|
||||
|
@ -313,29 +313,57 @@ ServiceWorkerManager::Init(ServiceWorkerRegistrar* aRegistrar)
|
||||
mActor = static_cast<ServiceWorkerManagerChild*>(actor);
|
||||
}
|
||||
|
||||
void
|
||||
RefPtr<GenericPromise>
|
||||
ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo,
|
||||
ServiceWorkerRegistrationInfo* aRegistrationInfo)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aRegistrationInfo->GetActive());
|
||||
|
||||
RefPtr<GenericPromise> ref;
|
||||
|
||||
const ServiceWorkerDescriptor& active =
|
||||
aRegistrationInfo->GetActive()->Descriptor();
|
||||
|
||||
auto entry = mControlledClients.LookupForAdd(aClientInfo.Id());
|
||||
if (entry) {
|
||||
RefPtr<ServiceWorkerRegistrationInfo> old =
|
||||
entry.Data()->mRegistrationInfo.forget();
|
||||
|
||||
ref = Move(entry.Data()->mClientHandle->Control(active));
|
||||
entry.Data()->mRegistrationInfo = aRegistrationInfo;
|
||||
|
||||
if (old != aRegistrationInfo) {
|
||||
StopControllingRegistration(old);
|
||||
aRegistrationInfo->StartControllingClient();
|
||||
}
|
||||
|
||||
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
|
||||
|
||||
return Move(ref);
|
||||
}
|
||||
|
||||
RefPtr<ClientHandle> clientHandle =
|
||||
ClientManager::CreateHandle(aClientInfo,
|
||||
SystemGroup::EventTargetFor(TaskCategory::Other));
|
||||
|
||||
auto entry = mControlledClients.LookupForAdd(aClientInfo.Id());
|
||||
if (entry) {
|
||||
entry.Data()->mRegistrationInfo = aRegistrationInfo;
|
||||
} else {
|
||||
entry.OrInsert([&] {
|
||||
return new ControlledClientData(clientHandle, aRegistrationInfo);
|
||||
ref = Move(clientHandle->Control(active));
|
||||
|
||||
aRegistrationInfo->StartControllingClient();
|
||||
|
||||
entry.OrInsert([&] {
|
||||
return new ControlledClientData(clientHandle, aRegistrationInfo);
|
||||
});
|
||||
|
||||
RefPtr<ServiceWorkerManager> self(this);
|
||||
clientHandle->OnDetach()->Then(
|
||||
SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
|
||||
[self = Move(self), aClientInfo] {
|
||||
self->StopControllingClient(aClientInfo);
|
||||
});
|
||||
|
||||
RefPtr<ServiceWorkerManager> self(this);
|
||||
clientHandle->OnDetach()->Then(
|
||||
SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
|
||||
[self = Move(self), aClientInfo] {
|
||||
self->StopControllingClient(aClientInfo);
|
||||
});
|
||||
}
|
||||
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
|
||||
|
||||
return Move(ref);
|
||||
}
|
||||
|
||||
void
|
||||
@ -345,7 +373,13 @@ ServiceWorkerManager::StopControllingClient(const ClientInfo& aClientInfo)
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerRegistrationInfo> reg =
|
||||
entry.Data()->mRegistrationInfo.forget();
|
||||
|
||||
entry.Remove();
|
||||
|
||||
StopControllingRegistration(reg);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2309,18 +2343,27 @@ ServiceWorkerManager::RemoveScopeAndRegistration(ServiceWorkerRegistrationInfo*
|
||||
entry.Remove();
|
||||
}
|
||||
|
||||
// Verify there are no controlled documents for the purged registration.
|
||||
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||
for (auto iter = swm->mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
|
||||
ServiceWorkerRegistrationInfo* reg = iter.UserData();
|
||||
// Verify there are no controlled clients for the purged registration.
|
||||
for (auto iter = swm->mControlledClients.Iter(); !iter.Done(); iter.Next()) {
|
||||
auto& reg = iter.UserData()->mRegistrationInfo;
|
||||
if (reg->mScope.Equals(aRegistration->mScope)) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(false,
|
||||
"controlled document when removing registration");
|
||||
"controlled client when removing registration");
|
||||
iter.Remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Registration lifecycle is managed via mControlledClients now. Do not
|
||||
// assert on on mControlledDocuments as races may cause this to still be
|
||||
// set when the registration is destroyed.
|
||||
for (auto iter = swm->mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
|
||||
ServiceWorkerRegistrationInfo* reg = iter.UserData();
|
||||
if (reg->mScope.Equals(aRegistration->mScope)) {
|
||||
iter.Remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
RefPtr<ServiceWorkerRegistrationInfo> info;
|
||||
data->mInfos.Remove(aRegistration->mScope, getter_AddRefs(info));
|
||||
@ -2349,25 +2392,43 @@ ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc)
|
||||
MOZ_ASSERT(aDoc);
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
GetServiceWorkerRegistrationInfo(aDoc);
|
||||
if (registration) {
|
||||
if (registration && registration->GetActive() &&
|
||||
aDoc->GetSandboxFlags() == 0) {
|
||||
MOZ_ASSERT(!mControlledDocuments.Contains(aDoc));
|
||||
StartControllingADocument(registration, aDoc);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerManager::StartControlling(const ClientInfo& aClientInfo,
|
||||
const ServiceWorkerDescriptor& aServiceWorker)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal =
|
||||
PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo());
|
||||
NS_ENSURE_TRUE(principal, false);
|
||||
|
||||
nsCOMPtr<nsIURI> scope;
|
||||
nsresult rv =
|
||||
NS_NewURI(getter_AddRefs(scope), aServiceWorker.Scope(), nullptr, nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
GetServiceWorkerRegistrationInfo(principal, scope);
|
||||
NS_ENSURE_TRUE(registration, false);
|
||||
|
||||
StartControllingClient(aClientInfo, registration);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aDoc);
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration;
|
||||
mControlledDocuments.Remove(aDoc, getter_AddRefs(registration));
|
||||
// A document which was uncontrolled does not maintain that state itself, so
|
||||
// it will always call MaybeStopControlling() even if there isn't an
|
||||
// associated registration. So this check is required.
|
||||
if (registration) {
|
||||
StopControllingRegistration(registration);
|
||||
}
|
||||
mControlledDocuments.Remove(aDoc);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2391,50 +2452,14 @@ ServiceWorkerManager::MaybeCheckNavigationUpdate(nsIDocument* aDoc)
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<GenericPromise>
|
||||
void
|
||||
ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
|
||||
nsIDocument* aDoc)
|
||||
{
|
||||
MOZ_ASSERT(aRegistration);
|
||||
MOZ_ASSERT(aDoc);
|
||||
|
||||
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||
auto storageAllowed = nsContentUtils::StorageAllowedForDocument(aDoc);
|
||||
MOZ_DIAGNOSTIC_ASSERT(storageAllowed == nsContentUtils::StorageAccess::eAllow);
|
||||
#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||
|
||||
RefPtr<GenericPromise> ref;
|
||||
|
||||
ServiceWorkerInfo* activeWorker = aRegistration->GetActive();
|
||||
if (NS_WARN_IF(!activeWorker)) {
|
||||
ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
__func__);
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
Maybe<ClientInfo> clientInfo = aDoc->GetClientInfo();
|
||||
if (NS_WARN_IF(clientInfo.isNothing())) {
|
||||
ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
__func__);
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
aRegistration->StartControllingClient();
|
||||
mControlledDocuments.Put(aDoc, aRegistration);
|
||||
|
||||
StartControllingClient(clientInfo.ref(), aRegistration);
|
||||
|
||||
// Mark the document's ClientSource as controlled using the ClientHandle
|
||||
// interface. While we could get at the ClientSource directly from the
|
||||
// document here, our goal is to move ServiceWorkerManager to a separate
|
||||
// process. Using the ClientHandle supports this remote operation.
|
||||
RefPtr<ClientHandle> clientHandle =
|
||||
ClientManager::CreateHandle(clientInfo.ref(),
|
||||
SystemGroup::EventTargetFor(TaskCategory::Other));
|
||||
ref = Move(clientHandle->Control(activeWorker->Descriptor()));
|
||||
|
||||
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
|
||||
return Move(ref);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2771,10 +2796,7 @@ ServiceWorkerManager::DispatchFetchEvent(const OriginAttributes& aOriginAttribut
|
||||
// First, attempt to mark the reserved client controlled directly. This
|
||||
// will update the controlled status in the ClientManagerService in the
|
||||
// parent. It will also eventually propagate back to the ClientSource.
|
||||
RefPtr<ClientHandle> clientHandle =
|
||||
ClientManager::CreateHandle(clientInfo.ref(),
|
||||
SystemGroup::EventTargetFor(TaskCategory::Other));
|
||||
clientHandle->Control(serviceWorker->Descriptor());
|
||||
StartControllingClient(clientInfo.ref(), registration);
|
||||
}
|
||||
|
||||
// But we also note the reserved state on the LoadInfo. This allows the
|
||||
@ -3245,11 +3267,8 @@ ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDocument,
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
if (controllingRegistration) {
|
||||
StopControllingRegistration(controllingRegistration);
|
||||
}
|
||||
|
||||
ref = StartControllingADocument(aWorkerRegistration, aDocument);
|
||||
StartControllingADocument(aWorkerRegistration, aDocument);
|
||||
ref = StartControllingClient(clientInfo.ref(), aWorkerRegistration);
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
|
@ -344,7 +344,7 @@ private:
|
||||
void
|
||||
Init(ServiceWorkerRegistrar* aRegistrar);
|
||||
|
||||
void
|
||||
RefPtr<GenericPromise>
|
||||
StartControllingClient(const ClientInfo& aClientInfo,
|
||||
ServiceWorkerRegistrationInfo* aRegistrationInfo);
|
||||
|
||||
@ -398,7 +398,7 @@ private:
|
||||
void
|
||||
NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
|
||||
|
||||
RefPtr<GenericPromise>
|
||||
void
|
||||
StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
|
||||
nsIDocument* aDoc);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user