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
This commit is contained in:
Eden Chuang 2020-03-10 06:41:31 +00:00
Родитель 90a12ad8b8
Коммит 47768c1ef1
7 изменённых файлов: 100 добавлений и 7 удалений

Просмотреть файл

@ -21,6 +21,7 @@ struct IPCServiceWorkerDescriptor
nsCString scope;
nsCString scriptURL;
ServiceWorkerState state;
bool handlesFetch;
};
} // namespace dom

Просмотреть файл

@ -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<IPCServiceWorkerDescriptor>(
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() &&

Просмотреть файл

@ -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;

Просмотреть файл

@ -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);

Просмотреть файл

@ -25,6 +25,8 @@ ServiceWorkerInterceptController::ShouldPrepareForIntercept(
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
RefPtr<ServiceWorkerManager> 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<ServiceWorkerDescriptor>& 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<ServiceWorkerRegistrationInfo> 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<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (!swm || !swm->IsAvailable(principal, aURI)) {
if (!swm || !swm->IsAvailable(principal, aURI, aChannel)) {
return NS_OK;
}

Просмотреть файл

@ -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<ServiceWorkerRegistrationInfo> 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<nsILoadInfo> loadInfo = aChannel->LoadInfo();
Maybe<ClientInfo> 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(

Просмотреть файл

@ -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