diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index e5729dd8f894..c713cca89262 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -14181,7 +14181,8 @@ nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel, ErrorResult error; nsCOMPtr runnable = - swm->PrepareFetchEvent(attrs, doc, aChannel, isReload, isSubresourceLoad, error); + swm->PrepareFetchEvent(attrs, doc, mInterceptedDocumentId, aChannel, + isReload, isSubresourceLoad, error); if (NS_WARN_IF(error.Failed())) { return error.StealNSResult(); } diff --git a/dom/webidl/FetchEvent.webidl b/dom/webidl/FetchEvent.webidl index 105ae12c02ef..751cf132719b 100644 --- a/dom/webidl/FetchEvent.webidl +++ b/dom/webidl/FetchEvent.webidl @@ -12,6 +12,7 @@ Exposed=(ServiceWorker)] interface FetchEvent : ExtendableEvent { [SameObject] readonly attribute Request? request; + readonly attribute DOMString? clientId; readonly attribute boolean isReload; [Throws] @@ -20,5 +21,6 @@ interface FetchEvent : ExtendableEvent { dictionary FetchEventInit : EventInit { Request request; + DOMString? clientId = null; boolean isReload = false; }; diff --git a/dom/workers/ServiceWorkerClient.h b/dom/workers/ServiceWorkerClient.h index e81eb37c541f..36a9cc1689aa 100644 --- a/dom/workers/ServiceWorkerClient.h +++ b/dom/workers/ServiceWorkerClient.h @@ -33,6 +33,11 @@ class ServiceWorkerClientInfo final public: explicit ServiceWorkerClientInfo(nsIDocument* aDoc); + const nsString& ClientId() const + { + return mClientId; + } + private: nsString mClientId; uint64_t mWindowId; diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index 83870051f787..5471effaedd4 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -102,6 +102,7 @@ FetchEvent::Constructor(const GlobalObject& aGlobal, e->SetTrusted(trusted); e->mRequest = aOptions.mRequest.WasPassed() ? &aOptions.mRequest.Value() : nullptr; + e->mClientId = aOptions.mClientId; e->mIsReload = aOptions.mIsReload; return e.forget(); } diff --git a/dom/workers/ServiceWorkerEvents.h b/dom/workers/ServiceWorkerEvents.h index 7159cb74213a..3d4f01366ad1 100644 --- a/dom/workers/ServiceWorkerEvents.h +++ b/dom/workers/ServiceWorkerEvents.h @@ -107,6 +107,7 @@ class FetchEvent final : public ExtendableEvent RefPtr mRequest; nsCString mScriptSpec; nsCString mPreventDefaultScriptSpec; + nsString mClientId; uint32_t mPreventDefaultLineNumber; uint32_t mPreventDefaultColumnNumber; bool mIsReload; @@ -149,6 +150,12 @@ public: return mRequest; } + void + GetClientId(nsAString& aClientId) const + { + aClientId = mClientId; + } + bool IsReload() const { diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 61e6fb38d6e3..8a4a5980a149 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -3271,18 +3271,18 @@ class ContinueDispatchFetchEventRunnable : public nsRunnable RefPtr mServiceWorkerPrivate; nsCOMPtr mChannel; nsCOMPtr mLoadGroup; - UniquePtr mClientInfo; + nsString mDocumentId; bool mIsReload; public: ContinueDispatchFetchEventRunnable(ServiceWorkerPrivate* aServiceWorkerPrivate, nsIInterceptedChannel* aChannel, nsILoadGroup* aLoadGroup, - UniquePtr&& aClientInfo, + const nsAString& aDocumentId, bool aIsReload) : mServiceWorkerPrivate(aServiceWorkerPrivate) , mChannel(aChannel) , mLoadGroup(aLoadGroup) - , mClientInfo(Move(aClientInfo)) + , mDocumentId(aDocumentId) , mIsReload(aIsReload) { MOZ_ASSERT(aServiceWorkerPrivate); @@ -3321,7 +3321,7 @@ public: } rv = mServiceWorkerPrivate->SendFetchEvent(mChannel, mLoadGroup, - Move(mClientInfo), mIsReload); + mDocumentId, mIsReload); if (NS_WARN_IF(NS_FAILED(rv))) { HandleError(); } @@ -3335,6 +3335,7 @@ public: already_AddRefed ServiceWorkerManager::PrepareFetchEvent(const PrincipalOriginAttributes& aOriginAttributes, nsIDocument* aDoc, + const nsAString& aDocumentIdForTopLevelNavigation, nsIInterceptedChannel* aChannel, bool aIsReload, bool aIsSubresourceLoad, @@ -3345,13 +3346,16 @@ ServiceWorkerManager::PrepareFetchEvent(const PrincipalOriginAttributes& aOrigin RefPtr serviceWorker; nsCOMPtr loadGroup; - UniquePtr clientInfo; + nsAutoString documentId; if (aIsSubresourceLoad) { MOZ_ASSERT(aDoc); serviceWorker = GetActiveWorkerInfoForDocument(aDoc); loadGroup = aDoc->GetDocumentLoadGroup(); - clientInfo.reset(new ServiceWorkerClientInfo(aDoc)); + nsresult rv = aDoc->GetOrCreateId(documentId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } } else { nsCOMPtr internalChannel; aRv = aChannel->GetChannel(getter_AddRefs(internalChannel)); @@ -3361,6 +3365,8 @@ ServiceWorkerManager::PrepareFetchEvent(const PrincipalOriginAttributes& aOrigin internalChannel->GetLoadGroup(getter_AddRefs(loadGroup)); + documentId = aDocumentIdForTopLevelNavigation; + nsCOMPtr uri; aRv = internalChannel->GetURI(getter_AddRefs(uri)); if (NS_WARN_IF(aRv.Failed())) { @@ -3389,7 +3395,7 @@ ServiceWorkerManager::PrepareFetchEvent(const PrincipalOriginAttributes& aOrigin nsCOMPtr continueRunnable = new ContinueDispatchFetchEventRunnable(serviceWorker->WorkerPrivate(), aChannel, loadGroup, - Move(clientInfo), aIsReload); + documentId, aIsReload); return continueRunnable.forget(); } diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h index 3a0d443a6d7e..23e2267a7f7b 100644 --- a/dom/workers/ServiceWorkerManager.h +++ b/dom/workers/ServiceWorkerManager.h @@ -360,6 +360,7 @@ public: already_AddRefed PrepareFetchEvent(const PrincipalOriginAttributes& aOriginAttributes, nsIDocument* aDoc, + const nsAString& aDocumentIdForTopLevelNavigation, nsIInterceptedChannel* aChannel, bool aIsReload, bool aIsSubresourceLoad, diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp index d4d9c395345a..980d35da9860 100644 --- a/dom/workers/ServiceWorkerPrivate.cpp +++ b/dom/workers/ServiceWorkerPrivate.cpp @@ -1016,6 +1016,7 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable nsTArray mHeaderValues; nsCString mSpec; nsCString mMethod; + nsString mClientId; bool mIsReload; DebugOnly mIsHttpChannel; RequestMode mRequestMode; @@ -1032,12 +1033,13 @@ public: // later on. const nsACString& aScriptSpec, nsMainThreadPtrHandle& aRegistration, - UniquePtr&& aClientInfo, + const nsAString& aDocumentId, bool aIsReload) : ExtendableFunctionalEventWorkerRunnable( aWorkerPrivate, aKeepAliveToken, aRegistration) , mInterceptedChannel(aChannel) , mScriptSpec(aScriptSpec) + , mClientId(aDocumentId) , mIsReload(aIsReload) , mIsHttpChannel(false) , mRequestMode(RequestMode::No_cors) @@ -1261,6 +1263,7 @@ private: init.mRequest.Value() = request; init.mBubbles = false; init.mCancelable = true; + init.mClientId = mClientId; init.mIsReload = mIsReload; RefPtr event = FetchEvent::Constructor(globalObj, NS_LITERAL_STRING("fetch"), init, result); @@ -1361,7 +1364,7 @@ NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVis nsresult ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, nsILoadGroup* aLoadGroup, - UniquePtr&& aClientInfo, + const nsAString& aDocumentId, bool aIsReload) { AssertIsOnMainThread(); @@ -1393,7 +1396,7 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, RefPtr r = new FetchEventRunnable(mWorkerPrivate, mKeepAliveToken, handle, mInfo->ScriptSpec(), regInfo, - Move(aClientInfo), aIsReload); + aDocumentId, aIsReload); rv = r->Init(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; diff --git a/dom/workers/ServiceWorkerPrivate.h b/dom/workers/ServiceWorkerPrivate.h index 86a40ad2cd24..43db837c8466 100644 --- a/dom/workers/ServiceWorkerPrivate.h +++ b/dom/workers/ServiceWorkerPrivate.h @@ -103,7 +103,7 @@ public: nsresult SendFetchEvent(nsIInterceptedChannel* aChannel, nsILoadGroup* aLoadGroup, - UniquePtr&& aClientInfo, + const nsAString& aDocumentId, bool aIsReload); void diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html index a4d9f68a6a4c..cd13df313dac 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html @@ -68,6 +68,41 @@ async_test(function(t) { .catch(unreached_rejection(t)); }, 'Service Worker responds to fetch event with the referrer URL'); +async_test(function(t) { + var scope = 'resources/simple.html?clientId'; + var frame; + var initial_client_id; + service_worker_unregister_and_register(t, worker, scope) + .then(function(reg) { + return wait_for_state(t, reg.installing, 'activated'); + }) + .then(function() { return with_iframe(scope); }) + .then(function(f) { + frame = f; + assert_equals( + frame.contentDocument.body.textContent.substr(0, 15), + 'Client ID Found', + 'Service Worker should respond to fetch with a client id'); + initial_client_id = frame.contentDocument.body.textContent.substr(17); + return frame.contentWindow.fetch('resources/other.html?clientId'); + }) + .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', + 'Service Worker should respond to fetch with an existing client id'); + assert_equals( + initial_client_id, + new_client_id, + 'Service Worker should observe the correct client ID for a controlled document'); + frame.remove(); + return service_worker_unregister_and_done(t, scope); + }) + .catch(unreached_rejection(t)); + }, 'Service Worker responds to fetch event with an existing client id'); + async_test(function(t) { var scope = 'resources/simple.html?ignore'; service_worker_unregister_and_register(t, worker, scope) diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-test-worker.js b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-test-worker.js index 2cdc0f926ea6..b56602e0fa2e 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-test-worker.js +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-test-worker.js @@ -11,6 +11,16 @@ function handleReferrer(event) { ['Referrer: ' + event.request.referrer]))); } +function handleClientId(event) { + var body; + if (event.clientId !== '') { + body = 'Client ID Found: ' + event.clientId; + } else { + body = 'Client ID Not Found'; + } + event.respondWith(new Response(body)); +} + function handleNullBody(event) { event.respondWith(new Response()); } @@ -66,6 +76,7 @@ self.addEventListener('fetch', function(event) { { pattern: '?string', fn: handleString }, { pattern: '?blob', fn: handleBlob }, { pattern: '?referrer', fn: handleReferrer }, + { pattern: '?clientId', fn: handleClientId }, { pattern: '?ignore', fn: function() {} }, { pattern: '?null', fn: handleNullBody }, { pattern: '?fetch', fn: handleFetch }, diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/interfaces-worker.sub.js b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/interfaces-worker.sub.js index a4cb4c107377..9c97319d43f2 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/interfaces-worker.sub.js +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/interfaces-worker.sub.js @@ -83,12 +83,18 @@ test(function() { assert_equals( new FetchEvent('FetchEvent').bubbles, false, 'Default FetchEvent.bubbles should be false'); + assert_equals( + new FetchEvent('FetchEvent').clientId, + null, 'Default FetchEvent.clientId should be null'); assert_equals( new FetchEvent('FetchEvent').isReload, false, 'Default FetchEvent.isReload should be false'); assert_equals( new FetchEvent('FetchEvent', {cancelable: false}).cancelable, false, 'FetchEvent.cancelable should be false'); + assert_equals( + new FetchEvent('FetchEvent', {clientId : 'test-client-id'}).clientId, 'test-client-id', + 'FetchEvent.clientId with option {clientId : "test-client-id"} should be "test-client-id"'); assert_equals( new FetchEvent('FetchEvent', {isReload : true}).isReload, true, 'FetchEvent.isReload with option {isReload : true} should be true');