From 71c4ea8b76733078dbcb375512a5dda8ea5bcb41 Mon Sep 17 00:00:00 2001 From: Perry Jiang Date: Thu, 15 Nov 2018 08:28:23 +0000 Subject: [PATCH] Bug 1264177 - Implement FetchEvent.resultingClientId r=edenchuang,mrbkap - Expose FetchEvent.resultingClientId on non-subresource, non-"report"-destination requests. - Delay Clients.get(FetchEvent.resultingClientId) resolution until the resulting client is execution ready. - Add WPTs to test for existence of resultingClientId and Clients.get promise resolution values. Differential Revision: https://phabricator.services.mozilla.com/D5333 --HG-- extra : moz-landing-system : lando --- dom/clients/manager/ClientManagerService.cpp | 33 +++- dom/clients/manager/ClientSourceParent.cpp | 13 ++ dom/clients/manager/ClientSourceParent.h | 5 + dom/serviceworkers/ServiceWorkerEvents.cpp | 1 + dom/serviceworkers/ServiceWorkerEvents.h | 7 + dom/serviceworkers/ServiceWorkerManager.cpp | 26 ++- dom/serviceworkers/ServiceWorkerPrivate.cpp | 40 ++++- dom/serviceworkers/ServiceWorkerPrivate.h | 7 +- dom/webidl/FetchEvent.webidl | 2 + .../service-worker/clients-get.https.html | 156 ++++++++++++++++++ .../service-worker/fetch-event.https.html | 23 ++- .../clients-get-resultingClientId-worker.js | 64 +++++++ .../resources/fetch-event-test-worker.js | 11 ++ 13 files changed, 373 insertions(+), 15 deletions(-) create mode 100644 testing/web-platform/tests/service-workers/service-worker/resources/clients-get-resultingClientId-worker.js diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp index 7efe0eddccae..628ac709693b 100644 --- a/dom/clients/manager/ClientManagerService.cpp +++ b/dom/clients/manager/ClientManagerService.cpp @@ -18,7 +18,9 @@ #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/PBackgroundSharedTypes.h" #include "mozilla/ClearOnShutdown.h" +#include "mozilla/MozPromise.h" #include "mozilla/SystemGroup.h" +#include "jsfriendapi.h" #include "nsIAsyncShutdown.h" #include "nsIXULRuntime.h" #include "nsProxyRelease.h" @@ -543,11 +545,34 @@ ClientManagerService::Claim(const ClientClaimArgs& aArgs) RefPtr ClientManagerService::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs) { - RefPtr ref; - ClientSourceParent* source = FindSource(aArgs.id(), aArgs.principalInfo()); - if (!source || !source->ExecutionReady()) { - ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + + if (!source) { + RefPtr ref = + ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + return ref.forget(); + } + + if (!source->ExecutionReady()) { + RefPtr self = this; + + // rejection ultimately converted to `undefined` in Clients::Get + RefPtr ref = + source->ExecutionReadyPromise() + ->Then(GetCurrentThreadSerialEventTarget(), __func__, + [self, aArgs] () -> RefPtr { + ClientSourceParent* source = self->FindSource(aArgs.id(), + aArgs.principalInfo()); + + if (!source) { + RefPtr ref = + ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + return ref.forget(); + } + + return source->StartOp(aArgs); + }); + return ref.forget(); } diff --git a/dom/clients/manager/ClientSourceParent.cpp b/dom/clients/manager/ClientSourceParent.cpp index 5818108e3e6d..02fd133fd6da 100644 --- a/dom/clients/manager/ClientSourceParent.cpp +++ b/dom/clients/manager/ClientSourceParent.cpp @@ -118,6 +118,8 @@ ClientSourceParent::RecvExecutionReady(const ClientSourceExecutionReadyArgs& aAr Unused << handle->SendExecutionReady(mClientInfo.ToIPC()); } + mExecutionReadyPromise.ResolveIfExists(true, __func__); + return IPC_OK(); }; @@ -228,6 +230,8 @@ ClientSourceParent::ClientSourceParent(const ClientSourceConstructorArgs& aArgs) ClientSourceParent::~ClientSourceParent() { MOZ_DIAGNOSTIC_ASSERT(mHandleList.IsEmpty()); + + mExecutionReadyPromise.RejectIfExists(NS_ERROR_FAILURE, __func__); } void @@ -268,6 +272,15 @@ ClientSourceParent::ExecutionReady() const return mExecutionReady; } +RefPtr +ClientSourceParent::ExecutionReadyPromise() +{ + // Only call if ClientSourceParent::ExecutionReady() is false; otherwise, + // the promise will never resolve + MOZ_ASSERT(!mExecutionReady); + return mExecutionReadyPromise.Ensure(__func__); +} + const Maybe& ClientSourceParent::GetController() const { diff --git a/dom/clients/manager/ClientSourceParent.h b/dom/clients/manager/ClientSourceParent.h index b1fc26d806e9..abd5d83d69ec 100644 --- a/dom/clients/manager/ClientSourceParent.h +++ b/dom/clients/manager/ClientSourceParent.h @@ -10,6 +10,7 @@ #include "ClientOpPromise.h" #include "mozilla/dom/PClientSourceParent.h" #include "mozilla/dom/ServiceWorkerDescriptor.h" +#include "mozilla/MozPromise.h" namespace mozilla { namespace dom { @@ -23,6 +24,7 @@ class ClientSourceParent final : public PClientSourceParent Maybe mController; RefPtr mService; nsTArray mHandleList; + MozPromiseHolder mExecutionReadyPromise; bool mExecutionReady; bool mFrozen; @@ -76,6 +78,9 @@ public: bool ExecutionReady() const; + RefPtr + ExecutionReadyPromise(); + const Maybe& GetController() const; diff --git a/dom/serviceworkers/ServiceWorkerEvents.cpp b/dom/serviceworkers/ServiceWorkerEvents.cpp index 6b5414e811e7..c39b903ab050 100644 --- a/dom/serviceworkers/ServiceWorkerEvents.cpp +++ b/dom/serviceworkers/ServiceWorkerEvents.cpp @@ -161,6 +161,7 @@ FetchEvent::Constructor(const GlobalObject& aGlobal, e->SetComposed(aOptions.mComposed); e->mRequest = aOptions.mRequest; e->mClientId = aOptions.mClientId; + e->mResultingClientId = aOptions.mResultingClientId; e->mIsReload = aOptions.mIsReload; return e.forget(); } diff --git a/dom/serviceworkers/ServiceWorkerEvents.h b/dom/serviceworkers/ServiceWorkerEvents.h index 1774a02839b9..59637a67c5c6 100644 --- a/dom/serviceworkers/ServiceWorkerEvents.h +++ b/dom/serviceworkers/ServiceWorkerEvents.h @@ -123,6 +123,7 @@ class FetchEvent final : public ExtendableEvent nsCString mScriptSpec; nsCString mPreventDefaultScriptSpec; nsString mClientId; + nsString mResultingClientId; uint32_t mPreventDefaultLineNumber; uint32_t mPreventDefaultColumnNumber; bool mIsReload; @@ -169,6 +170,12 @@ public: aClientId = mClientId; } + void + GetResultingClientId(nsAString& aResultingClientId) const + { + aResultingClientId = mResultingClientId; + } + bool IsReload() const { diff --git a/dom/serviceworkers/ServiceWorkerManager.cpp b/dom/serviceworkers/ServiceWorkerManager.cpp index 732fac332cf2..bab00c4defe3 100644 --- a/dom/serviceworkers/ServiceWorkerManager.cpp +++ b/dom/serviceworkers/ServiceWorkerManager.cpp @@ -2018,21 +2018,43 @@ public: } nsString clientId; + nsString resultingClientId; nsCOMPtr loadInfo = channel->GetLoadInfo(); if (loadInfo) { + char buf[NSID_LENGTH]; Maybe clientInfo = loadInfo->GetClientInfo(); if (clientInfo.isSome()) { - char buf[NSID_LENGTH]; clientInfo.ref().Id().ToProvidedString(buf); NS_ConvertASCIItoUTF16 uuid(buf); // Remove {} and the null terminator clientId.Assign(Substring(uuid, 1, NSID_LENGTH - 3)); } + + // Having an initial or reserved client are mutually exclusive events: + // either an initial client is used upon navigating an about:blank + // iframe, or a new, reserved environment/client is created (e.g. + // upon a top-level navigation). See step 4 of + // https://html.spec.whatwg.org/#process-a-navigate-fetch as well as + // https://github.com/w3c/ServiceWorker/issues/1228#issuecomment-345132444 + Maybe resulting = loadInfo->GetInitialClientInfo(); + + if (resulting.isNothing()) { + resulting = loadInfo->GetReservedClientInfo(); + } else { + MOZ_ASSERT(loadInfo->GetReservedClientInfo().isNothing()); + } + + if (resulting.isSome()) { + resulting.ref().Id().ToProvidedString(buf); + NS_ConvertASCIItoUTF16 uuid(buf); + + resultingClientId.Assign(Substring(uuid, 1, NSID_LENGTH - 3)); + } } rv = mServiceWorkerPrivate->SendFetchEvent(mChannel, mLoadGroup, clientId, - mIsReload); + resultingClientId, mIsReload); if (NS_WARN_IF(NS_FAILED(rv))) { HandleError(); } diff --git a/dom/serviceworkers/ServiceWorkerPrivate.cpp b/dom/serviceworkers/ServiceWorkerPrivate.cpp index 41cd9e6bf6fa..b9e738d8e70e 100644 --- a/dom/serviceworkers/ServiceWorkerPrivate.cpp +++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp @@ -1309,6 +1309,7 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable nsCString mFragment; nsCString mMethod; nsString mClientId; + nsString mResultingClientId; bool mIsReload; bool mMarkLaunchServiceWorkerEnd; RequestCache mCacheMode; @@ -1321,6 +1322,7 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable nsCString mReferrer; ReferrerPolicy mReferrerPolicy; nsString mIntegrity; + const bool mIsNonSubresourceRequest; public: FetchEventRunnable(WorkerPrivate* aWorkerPrivate, KeepAliveToken* aKeepAliveToken, @@ -1330,13 +1332,16 @@ public: const nsACString& aScriptSpec, nsMainThreadPtrHandle& aRegistration, const nsAString& aClientId, + const nsAString& aResultingClientId, bool aIsReload, - bool aMarkLaunchServiceWorkerEnd) + bool aMarkLaunchServiceWorkerEnd, + bool aIsNonSubresourceRequest) : ExtendableFunctionalEventWorkerRunnable( aWorkerPrivate, aKeepAliveToken, aRegistration) , mInterceptedChannel(aChannel) , mScriptSpec(aScriptSpec) , mClientId(aClientId) + , mResultingClientId(aResultingClientId) , mIsReload(aIsReload) , mMarkLaunchServiceWorkerEnd(aMarkLaunchServiceWorkerEnd) , mCacheMode(RequestCache::Default) @@ -1349,6 +1354,7 @@ public: , mUploadStreamContentLength(-1) , mReferrer(kFETCH_CLIENT_REFERRER_STR) , mReferrerPolicy(ReferrerPolicy::_empty) + , mIsNonSubresourceRequest(aIsNonSubresourceRequest) { MOZ_ASSERT(aWorkerPrivate); } @@ -1624,12 +1630,26 @@ private: init.mBubbles = false; init.mCancelable = true; // Only expose the FetchEvent.clientId on subresource requests for now. - // Once we implement .resultingClientId and .targetClientId we can then - // start exposing .clientId on non-subresource requests as well. See - // bug 1264177. + // Once we implement .targetClientId we can then start exposing .clientId + // on non-subresource requests as well. See bug 1487534. if (!mClientId.IsEmpty() && !internalReq->IsNavigationRequest()) { init.mClientId = mClientId; } + + /* + * https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm + * + * "If request is a non-subresource request and request’s + * destination is not "report", initialize e’s resultingClientId attribute + * to reservedClient’s [resultingClient's] id, and to the empty string + * otherwise." (Step 18.8) + */ + if (!mResultingClientId.IsEmpty() && + mIsNonSubresourceRequest && + internalReq->Destination() != RequestDestination::Report) { + init.mResultingClientId = mResultingClientId; + } + init.mIsReload = mIsReload; RefPtr event = FetchEvent::Constructor(globalObj, NS_LITERAL_STRING("fetch"), init, result); @@ -1673,7 +1693,9 @@ NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVis nsresult ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, nsILoadGroup* aLoadGroup, - const nsAString& aClientId, bool aIsReload) + const nsAString& aClientId, + const nsAString& aResultingClientId, + bool aIsReload) { MOZ_ASSERT(NS_IsMainThread()); @@ -1738,11 +1760,17 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, RefPtr token = CreateEventKeepAliveToken(); + nsCOMPtr channel; + rv = aChannel->GetChannel(getter_AddRefs(channel)); + NS_ENSURE_SUCCESS(rv, rv); + bool isNonSubresourceRequest = nsContentUtils::IsNonSubresourceRequest(channel); RefPtr r = new FetchEventRunnable(mWorkerPrivate, token, handle, mInfo->ScriptSpec(), regInfo, - aClientId, aIsReload, newWorkerCreated); + aClientId, aResultingClientId, + aIsReload, newWorkerCreated, + isNonSubresourceRequest); rv = r->Init(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; diff --git a/dom/serviceworkers/ServiceWorkerPrivate.h b/dom/serviceworkers/ServiceWorkerPrivate.h index fbb3792a0d00..8fb098f10ced 100644 --- a/dom/serviceworkers/ServiceWorkerPrivate.h +++ b/dom/serviceworkers/ServiceWorkerPrivate.h @@ -125,8 +125,11 @@ public: const nsAString& aScope); nsresult - SendFetchEvent(nsIInterceptedChannel* aChannel, nsILoadGroup* aLoadGroup, - const nsAString& aClientId, bool aIsReload); + SendFetchEvent(nsIInterceptedChannel* aChannel, + nsILoadGroup* aLoadGroup, + const nsAString& aClientId, + const nsAString& aResultingClientId, + bool aIsReload); bool MaybeStoreISupports(nsISupports* aSupports); diff --git a/dom/webidl/FetchEvent.webidl b/dom/webidl/FetchEvent.webidl index 148433f356b9..97005402f9d7 100644 --- a/dom/webidl/FetchEvent.webidl +++ b/dom/webidl/FetchEvent.webidl @@ -13,6 +13,7 @@ interface FetchEvent : ExtendableEvent { [SameObject] readonly attribute Request request; readonly attribute DOMString clientId; + readonly attribute DOMString resultingClientId; readonly attribute boolean isReload; [Throws] @@ -22,5 +23,6 @@ interface FetchEvent : ExtendableEvent { dictionary FetchEventInit : EventInit { required Request request; DOMString clientId = ""; + DOMString resultingClientId = ""; boolean isReload = false; }; diff --git a/testing/web-platform/tests/service-workers/service-worker/clients-get.https.html b/testing/web-platform/tests/service-workers/service-worker/clients-get.https.html index 68c6d7f7ac02..b05274039082 100644 --- a/testing/web-platform/tests/service-workers/service-worker/clients-get.https.html +++ b/testing/web-platform/tests/service-workers/service-worker/clients-get.https.html @@ -49,6 +49,162 @@ promise_test(function(t) { }); }, 'Test Clients.get()'); +promise_test((t) => { + let frame = null; + const scope = 'resources/simple.html'; + const outerSwContainer = navigator.serviceWorker; + let innerSwReg = null; + let innerSw = null; + + return service_worker_unregister_and_register( + t, 'resources/clients-get-resultingClientId-worker.js', scope) + .then((registration) => { + innerSwReg = registration; + add_completion_callback(function() { registration.unregister(); }); + return wait_for_state(t, registration.installing, 'activated'); + }) + .then(() => { + // load frame and get resulting client id + let channel = new MessageChannel(); + innerSw = innerSwReg.active; + + let p = new Promise(resolve => { + function getResultingClientId(e) { + if (e.data.msg == 'getResultingClientId') { + const { resultingClientId } = e.data; + + channel.port1.removeEventListener('message', getResultingClientId); + + resolve({ resultingClientId, port: channel.port1 }); + } + } + + channel.port1.onmessage = getResultingClientId; + }); + + + return with_iframe(scope).then((iframe) => { + innerSw.postMessage( + { port: channel.port2, msg: 'getResultingClientId' }, + [channel.port2], + ); + + frame = iframe; + frame.focus(); + add_completion_callback(() => iframe.remove()); + + return p; + }); + }) + .then(({ resultingClientId, port }) => { + // query service worker for clients.get(resultingClientId) + let channel = new MessageChannel(); + + let p = new Promise(resolve => { + function getIsResultingClientUndefined(e) { + if (e.data.msg == 'getIsResultingClientUndefined') { + let { isResultingClientUndefined } = e.data; + + port.removeEventListener('message', getIsResultingClientUndefined); + + resolve(isResultingClientUndefined); + } + } + + port.onmessage = getIsResultingClientUndefined; + }); + + innerSw.postMessage( + { port: channel.port2, msg: 'getIsResultingClientUndefined', resultingClientId }, + [channel.port2], + ); + + return p; + }) + .then((isResultingClientUndefined) => { + assert_false(isResultingClientUndefined, 'Clients.get(FetchEvent.resultingClientId) resolved with a Client'); + }); +}, 'Test successful Clients.get(FetchEvent.resultingClientId)'); + +promise_test((t) => { + const scope = 'resources/simple.html?fail'; + const outerSwContainer = navigator.serviceWorker; + let innerSwReg = null; + let innerSw = null; + + return service_worker_unregister_and_register( + t, 'resources/clients-get-resultingClientId-worker.js', scope) + .then((registration) => { + innerSwReg = registration; + add_completion_callback(function() { registration.unregister(); }); + return wait_for_state(t, registration.installing, 'activated'); + }) + .then(() => { + // load frame, destroying it while loading, and get resulting client id + innerSw = innerSwReg.active; + + let iframe = document.createElement('iframe'); + iframe.className = 'test-iframe'; + iframe.src = scope; + + function destroyIframe(e) { + if (e.data.msg == 'destroyResultingClient') { + iframe.remove(); + iframe = null; + + innerSw.postMessage({ msg: 'resultingClientDestroyed' }); + } + } + + outerSwContainer.addEventListener('message', destroyIframe); + + let p = new Promise(resolve => { + function resultingClientDestroyedAck(e) { + if (e.data.msg == 'resultingClientDestroyedAck') { + let { resultingDestroyedClientId } = e.data; + + outerSwContainer.removeEventListener('message', resultingClientDestroyedAck); + resolve(resultingDestroyedClientId); + } + } + + outerSwContainer.addEventListener('message', resultingClientDestroyedAck); + }); + + document.body.appendChild(iframe); + + return p; + }) + .then((resultingDestroyedClientId) => { + // query service worker for clients.get(resultingDestroyedClientId) + let channel = new MessageChannel(); + + let p = new Promise((resolve, reject) => { + function getIsResultingClientUndefined(e) { + if (e.data.msg == 'getIsResultingClientUndefined') { + let { isResultingClientUndefined } = e.data; + + channel.port1.removeEventListener('message', getIsResultingClientUndefined); + + resolve(isResultingClientUndefined); + } + } + + channel.port1.onmessage = getIsResultingClientUndefined; + }); + + innerSw.postMessage( + { port: channel.port2, msg: 'getIsResultingClientUndefined', resultingClientId: resultingDestroyedClientId }, + [channel.port2], + ); + + return p; + }) + .then((isResultingClientUndefined) => { + assert_true(isResultingClientUndefined, 'Clients.get(FetchEvent.resultingClientId) resolved with `undefined`'); + }); +}, 'Test unsuccessful Clients.get(FetchEvent.resultingClientId)'); + function wait_for_clientId() { return new Promise(function(resolve, reject) { function get_client_id(e) { diff --git a/testing/web-platform/tests/service-workers/service-worker/fetch-event.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-event.https.html index 78e099b8b553..169a0b17d623 100644 --- a/testing/web-platform/tests/service-workers/service-worker/fetch-event.https.html +++ b/testing/web-platform/tests/service-workers/service-worker/fetch-event.https.html @@ -116,7 +116,6 @@ promise_test(t => { }) .then(function(response) { return response.text(); }) .then(function(response_text) { - var new_client_id = response_text.substr(17); assert_equals( response_text.substr(0, 15), 'Client ID Found', @@ -124,6 +123,28 @@ promise_test(t => { }); }, 'Service Worker responds to fetch event with an existing client id'); +promise_test(t => { + const page_url = 'resources/simple.html?resultingClientId'; + const expected_found = 'Resulting Client ID Found'; + const expected_not_found = 'Resulting Client ID Not Found'; + return with_iframe(page_url) + .then(function(frame) { + t.add_cleanup(() => { frame.remove(); }); + assert_equals( + frame.contentDocument.body.textContent.substr(0, expected_found.length), + expected_found, + 'Service Worker should respond with an existing resulting client id for non-subresource requests'); + return frame.contentWindow.fetch('resources/other.html?resultingClientId'); + }) + .then(function(response) { return response.text(); }) + .then(function(response_text) { + assert_equals( + response_text.substr(0), + expected_not_found, + 'Service Worker should respond with an empty resulting client id for subresource requests'); + }); + }, 'Service Worker responds to fetch event with the correct resulting client id'); + promise_test(t => { const page_url = 'resources/simple.html?ignore'; return with_iframe(page_url) diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-resultingClientId-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-resultingClientId-worker.js new file mode 100644 index 000000000000..153a8e3f5b94 --- /dev/null +++ b/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-resultingClientId-worker.js @@ -0,0 +1,64 @@ +let savedPort = null; +let savedResultingClientId = null; + +async function destroyResultingClient(e) { + const outer = await self.clients.matchAll({ type: 'window', includeUncontrolled: true }) + .then((clientList) => { + for (let c of clientList) { + if (c.url.endsWith('clients-get.https.html')) { + c.focus(); + return c; + } + } + }); + + const p = new Promise(resolve => { + function resultingClientDestroyed(evt) { + if (evt.data.msg == 'resultingClientDestroyed') { + self.removeEventListener('message', resultingClientDestroyed); + resolve(outer); + } + } + + self.addEventListener('message', resultingClientDestroyed); + }); + + outer.postMessage({ msg: 'destroyResultingClient' }); + + return await p; +} + +self.addEventListener('fetch', async (e) => { + let { resultingClientId } = e; + savedResultingClientId = resultingClientId; + + if (e.request.url.endsWith('simple.html?fail')) { + e.waitUntil(new Promise(async (resolve) => { + let outer = await destroyResultingClient(e); + + outer.postMessage({ msg: 'resultingClientDestroyedAck', + resultingDestroyedClientId: savedResultingClientId }); + resolve(); + })); + } else { + e.respondWith(fetch(e.request)); + } +}); + +self.addEventListener('message', (e) => { + let { msg, port, resultingClientId } = e.data; + savedPort = savedPort || port; + + if (msg == 'getIsResultingClientUndefined') { + self.clients.get(resultingClientId).then((client) => { + let isUndefined = typeof client == 'undefined'; + savedPort.postMessage({ msg: 'getIsResultingClientUndefined', + isResultingClientUndefined: isUndefined }); + }); + } + + if (msg == 'getResultingClientId') { + savedPort.postMessage({ msg: 'getResultingClientId', + resultingClientId: savedResultingClientId }); + } +}); diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js index d503a6609d69..0a52a8201ea9 100644 --- a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js +++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js @@ -37,6 +37,16 @@ function handleClientId(event) { event.respondWith(new Response(body)); } +function handleResultingClientId(event) { + var body; + if (event.resultingClientId !== "") { + body = 'Resulting Client ID Found: ' + event.resultingClientId; + } else { + body = 'Resulting Client ID Not Found'; + } + event.respondWith(new Response(body)); +} + function handleNullBody(event) { event.respondWith(new Response()); } @@ -155,6 +165,7 @@ self.addEventListener('fetch', function(event) { { pattern: '?referrerPolicy', fn: handleReferrerPolicy }, { pattern: '?referrer', fn: handleReferrer }, { pattern: '?clientId', fn: handleClientId }, + { pattern: '?resultingClientId', fn: handleResultingClientId }, { pattern: '?ignore', fn: function() {} }, { pattern: '?null', fn: handleNullBody }, { pattern: '?fetch', fn: handleFetch },