From 47768c1ef1f79d5ac25f3b3b5955ed00aefd5de2 Mon Sep 17 00:00:00 2001 From: Eden Chuang Date: Tue, 10 Mar 2020 06:41:31 +0000 Subject: [PATCH] Bug 1407276 - Avoid creating InterceptedHttpChannel if the service worker has no fetch event handler r=dom-workers-and-storage-reviewers,asuth Differential Revision: https://phabricator.services.mozilla.com/D64092 --HG-- extra : moz-landing-system : lando --- .../IPCServiceWorkerDescriptor.ipdlh | 1 + .../ServiceWorkerDescriptor.cpp | 12 ++++- dom/serviceworkers/ServiceWorkerDescriptor.h | 4 ++ dom/serviceworkers/ServiceWorkerInfo.h | 1 + .../ServiceWorkerInterceptController.cpp | 30 +++++++++-- dom/serviceworkers/ServiceWorkerManager.cpp | 50 ++++++++++++++++++- dom/serviceworkers/ServiceWorkerManager.h | 9 +++- 7 files changed, 100 insertions(+), 7 deletions(-) diff --git a/dom/serviceworkers/IPCServiceWorkerDescriptor.ipdlh b/dom/serviceworkers/IPCServiceWorkerDescriptor.ipdlh index ec6df6d9e52e..25700ee032de 100644 --- a/dom/serviceworkers/IPCServiceWorkerDescriptor.ipdlh +++ b/dom/serviceworkers/IPCServiceWorkerDescriptor.ipdlh @@ -21,6 +21,7 @@ struct IPCServiceWorkerDescriptor nsCString scope; nsCString scriptURL; ServiceWorkerState state; + bool handlesFetch; }; } // namespace dom diff --git a/dom/serviceworkers/ServiceWorkerDescriptor.cpp b/dom/serviceworkers/ServiceWorkerDescriptor.cpp index 158a3ca7a776..ee3ed3985466 100644 --- a/dom/serviceworkers/ServiceWorkerDescriptor.cpp +++ b/dom/serviceworkers/ServiceWorkerDescriptor.cpp @@ -29,6 +29,8 @@ ServiceWorkerDescriptor::ServiceWorkerDescriptor( mData->scope() = aScope; mData->scriptURL() = aScriptURL; mData->state() = aState; + // Set HandlesFetch as true in default + mData->handlesFetch() = true; } ServiceWorkerDescriptor::ServiceWorkerDescriptor( @@ -37,7 +39,7 @@ ServiceWorkerDescriptor::ServiceWorkerDescriptor( const nsACString& aScriptURL, ServiceWorkerState aState) : mData(MakeUnique( aId, aRegistrationId, aRegistrationVersion, aPrincipalInfo, - nsCString(aScriptURL), nsCString(aScope), aState)) {} + nsCString(aScriptURL), nsCString(aScope), aState, true)) {} ServiceWorkerDescriptor::ServiceWorkerDescriptor( const IPCServiceWorkerDescriptor& aDescriptor) @@ -118,6 +120,14 @@ void ServiceWorkerDescriptor::SetRegistrationVersion(uint64_t aVersion) { mData->registrationVersion() = aVersion; } +bool ServiceWorkerDescriptor::HandlesFetch() const { + return mData->handlesFetch(); +} + +void ServiceWorkerDescriptor::SetHandlesFetch(bool aHandlesFetch) { + mData->handlesFetch() = aHandlesFetch; +} + bool ServiceWorkerDescriptor::Matches( const ServiceWorkerDescriptor& aDescriptor) const { return Id() == aDescriptor.Id() && Scope() == aDescriptor.Scope() && diff --git a/dom/serviceworkers/ServiceWorkerDescriptor.h b/dom/serviceworkers/ServiceWorkerDescriptor.h index 6aea1c8166e9..cb99ff5fe24a 100644 --- a/dom/serviceworkers/ServiceWorkerDescriptor.h +++ b/dom/serviceworkers/ServiceWorkerDescriptor.h @@ -82,6 +82,10 @@ class ServiceWorkerDescriptor final { void SetRegistrationVersion(uint64_t aVersion); + bool HandlesFetch() const; + + void SetHandlesFetch(bool aHandlesFetch); + // Try to determine if two workers match each other. This is less strict // than an operator==() call since it ignores mutable values like State(). bool Matches(const ServiceWorkerDescriptor& aDescriptor) const; diff --git a/dom/serviceworkers/ServiceWorkerInfo.h b/dom/serviceworkers/ServiceWorkerInfo.h index 4455e631a623..e62b79fed4c9 100644 --- a/dom/serviceworkers/ServiceWorkerInfo.h +++ b/dom/serviceworkers/ServiceWorkerInfo.h @@ -126,6 +126,7 @@ class ServiceWorkerInfo final : public nsIServiceWorkerInfo { MOZ_ASSERT(NS_IsMainThread()); MOZ_DIAGNOSTIC_ASSERT(mHandlesFetch == Unknown); mHandlesFetch = aHandlesFetch ? Enabled : Disabled; + mDescriptor.SetHandlesFetch(aHandlesFetch); } void SetRegistrationVersion(uint64_t aVersion); diff --git a/dom/serviceworkers/ServiceWorkerInterceptController.cpp b/dom/serviceworkers/ServiceWorkerInterceptController.cpp index a5fe267272a4..460b0499ecff 100644 --- a/dom/serviceworkers/ServiceWorkerInterceptController.cpp +++ b/dom/serviceworkers/ServiceWorkerInterceptController.cpp @@ -25,6 +25,8 @@ ServiceWorkerInterceptController::ShouldPrepareForIntercept( nsCOMPtr loadInfo = aChannel->LoadInfo(); + RefPtr swm = ServiceWorkerManager::GetInstance(); + // For subresource requests we base our decision solely on the client's // controller value. Any settings that would have blocked service worker // access should have been set before the initial navigation created the @@ -32,7 +34,30 @@ ServiceWorkerInterceptController::ShouldPrepareForIntercept( if (!nsContentUtils::IsNonSubresourceRequest(aChannel)) { const Maybe& controller = loadInfo->GetController(); - *aShouldIntercept = controller.isSome(); + // For child intercept, only checking the loadInfo controller existence. + if (!ServiceWorkerParentInterceptEnabled()) { + *aShouldIntercept = controller.isSome(); + return NS_OK; + } + + // If the controller doesn't handle fetch events, return false + if (controller.isSome()) { + *aShouldIntercept = controller.ref().HandlesFetch(); + + // The service worker has no fetch event handler, try to schedule a + // soft-update through ServiceWorkerRegistrationInfo. + // Get ServiceWorkerRegistrationInfo by the ServiceWorkerInfo's principal + // and scope + if (!*aShouldIntercept && swm) { + RefPtr registration = + swm->GetRegistration(controller.ref().GetPrincipal().get(), + controller.ref().Scope()); + MOZ_ASSERT(registration); + registration->MaybeScheduleTimeCheckAndUpdate(); + } + } else { + *aShouldIntercept = false; + } return NS_OK; } @@ -40,8 +65,7 @@ ServiceWorkerInterceptController::ShouldPrepareForIntercept( aURI, loadInfo->GetOriginAttributes()); // First check with the ServiceWorkerManager for a matching service worker. - RefPtr swm = ServiceWorkerManager::GetInstance(); - if (!swm || !swm->IsAvailable(principal, aURI)) { + if (!swm || !swm->IsAvailable(principal, aURI, aChannel)) { return NS_OK; } diff --git a/dom/serviceworkers/ServiceWorkerManager.cpp b/dom/serviceworkers/ServiceWorkerManager.cpp index 9d02edc1689b..5328568bda79 100644 --- a/dom/serviceworkers/ServiceWorkerManager.cpp +++ b/dom/serviceworkers/ServiceWorkerManager.cpp @@ -2313,13 +2313,59 @@ void ServiceWorkerManager::DispatchFetchEvent(nsIInterceptedChannel* aChannel, aRv = uploadChannel->EnsureUploadStreamIsCloneable(permissionsRunnable); } -bool ServiceWorkerManager::IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI) { +bool ServiceWorkerManager::IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI, + nsIChannel* aChannel) { MOZ_ASSERT(aPrincipal); MOZ_ASSERT(aURI); + MOZ_ASSERT(aChannel); RefPtr registration = GetServiceWorkerRegistrationInfo(aPrincipal, aURI); - return registration && registration->GetActive(); + + // For child interception, just check the availability. + if (!ServiceWorkerParentInterceptEnabled()) { + return registration && registration->GetActive(); + } + + if (!registration || !registration->GetActive()) { + return false; + } + + // Checking if the matched service worker handles fetch events or not. + // If it does, directly return true and handle the client controlling logic + // in DispatchFetchEvent(). otherwise, do followings then return false. + // 1. Set the matched service worker as the controller of LoadInfo and + // correspoinding ClinetInfo + // 2. Maybe schedule a soft update + if (!registration->GetActive()->HandlesFetch()) { + // Checkin if the channel is not storage allowed first. + if (StorageAllowedForChannel(aChannel) != StorageAccess::eAllow) { + return false; + } + + // ServiceWorkerInterceptController::ShouldPrepareForIntercept() handles the + // subresource cases. Must be non-subresource case here. + MOZ_ASSERT(nsContentUtils::IsNonSubresourceRequest(aChannel)); + + nsCOMPtr loadInfo = aChannel->LoadInfo(); + + Maybe clientInfo = loadInfo->GetReservedClientInfo(); + if (clientInfo.isNothing()) { + clientInfo = loadInfo->GetInitialClientInfo(); + } + + if (clientInfo.isSome()) { + StartControllingClient(clientInfo.ref(), registration); + } + loadInfo->SetController(registration->GetActive()->Descriptor()); + + // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm 17.1 + // try schedule a soft-update for non-subresource case. + registration->MaybeScheduleTimeCheckAndUpdate(); + return false; + } + // Found a matching service worker which handles fetch events, return true. + return true; } nsresult ServiceWorkerManager::GetClientRegistration( diff --git a/dom/serviceworkers/ServiceWorkerManager.h b/dom/serviceworkers/ServiceWorkerManager.h index ffbfdc7c61fa..4a7a339bb834 100644 --- a/dom/serviceworkers/ServiceWorkerManager.h +++ b/dom/serviceworkers/ServiceWorkerManager.h @@ -116,7 +116,14 @@ class ServiceWorkerManager final : public nsIServiceWorkerManager, NS_DECL_NSISERVICEWORKERMANAGER NS_DECL_NSIOBSERVER - bool IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI); + // Return true if the given principal and URI matches a registered service + // worker which handles fetch event. + // If there is a matched service worker but doesn't handle fetch events, this + // method will try to set the matched service worker as the controller of the + // passed in channel. Then also schedule a soft-update job for the service + // worker. + bool IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI, + nsIChannel* aChannel); // Return true if the given content process could potentially be executing // service worker code with the given principal. At the current time, this