From 6c4573cf9e8ce1cf8c71eedad574437d1e57e2e9 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 30 Nov 2015 14:29:51 -0500 Subject: [PATCH] Bug 1229056 - Implement ClientQueryOptions.includeUncontrolled; r=jdm --- dom/workers/ServiceWorkerClients.cpp | 15 ++-- dom/workers/ServiceWorkerManager.cpp | 70 +++++++++++++++---- dom/workers/ServiceWorkerManager.h | 3 +- ...tchall-include-uncontrolled.https.html.ini | 6 -- ...s-matchall-include-uncontrolled.https.html | 14 ++-- .../resources/clients-matchall-worker.js | 9 ++- 6 files changed, 87 insertions(+), 30 deletions(-) delete mode 100644 testing/web-platform/mozilla/meta/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html.ini diff --git a/dom/workers/ServiceWorkerClients.cpp b/dom/workers/ServiceWorkerClients.cpp index 67358084e30c..b3f23217932a 100644 --- a/dom/workers/ServiceWorkerClients.cpp +++ b/dom/workers/ServiceWorkerClients.cpp @@ -181,11 +181,14 @@ class MatchAllRunnable final : public nsRunnable RefPtr mPromiseProxy; nsCString mScope; + bool mIncludeUncontrolled; public: MatchAllRunnable(PromiseWorkerProxy* aPromiseProxy, - const nsCString& aScope) + const nsCString& aScope, + bool aIncludeUncontrolled) : mPromiseProxy(aPromiseProxy), - mScope(aScope) + mScope(aScope), + mIncludeUncontrolled(aIncludeUncontrolled) { MOZ_ASSERT(mPromiseProxy); } @@ -203,7 +206,8 @@ public: RefPtr swm = ServiceWorkerManager::GetInstance(); nsTArray result; - swm->GetAllClients(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(), mScope, result); + swm->GetAllClients(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(), mScope, + mIncludeUncontrolled, result); RefPtr r = new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(), mPromiseProxy, result); @@ -689,7 +693,7 @@ ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions, nsString scope; mWorkerScope->GetScope(scope); - if (aOptions.mIncludeUncontrolled || aOptions.mType != ClientType::Window) { + if (aOptions.mType != ClientType::Window) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return nullptr; } @@ -708,7 +712,8 @@ ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions, RefPtr r = new MatchAllRunnable(promiseProxy, - NS_ConvertUTF16toUTF8(scope)); + NS_ConvertUTF16toUTF8(scope), + aOptions.mIncludeUncontrolled); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r))); return promise.forget(); } diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 7faca3a081aa..c944eb4cd889 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -20,6 +20,7 @@ #include "nsINetworkInterceptController.h" #include "nsIMutableArray.h" #include "nsIScriptError.h" +#include "nsISimpleEnumerator.h" #include "nsIUploadChannel2.h" #include "nsPIDOMWindow.h" #include "nsScriptLoader.h" @@ -3760,7 +3761,8 @@ ServiceWorkerManager::GetClient(nsIPrincipal* aPrincipal, void ServiceWorkerManager::GetAllClients(nsIPrincipal* aPrincipal, const nsCString& aScope, - nsTArray& aControlledDocuments) + bool aIncludeUncontrolled, + nsTArray& aDocuments) { MOZ_ASSERT(aPrincipal); @@ -3772,21 +3774,65 @@ ServiceWorkerManager::GetAllClients(nsIPrincipal* aPrincipal, return; } - for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) { - ServiceWorkerRegistrationInfo* thisRegistration = iter.UserData(); - MOZ_ASSERT(thisRegistration); - if (!registration->mScope.Equals(thisRegistration->mScope)) { - continue; + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (NS_WARN_IF(!obs)) { + return; + } + + nsCOMPtr enumerator; + nsresult rv = obs->EnumerateObservers("service-worker-get-client", + getter_AddRefs(enumerator)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + auto ProcessDocument = [&aDocuments](nsIPrincipal* aPrincipal, nsIDocument* aDoc) { + if (!aDoc || !aDoc->GetWindow()) { + return; } - nsCOMPtr document = do_QueryInterface(iter.Key()); - - if (!document || !document->GetWindow()) { - continue; + bool equals = false; + aPrincipal->Equals(aDoc->NodePrincipal(), &equals); + if (!equals) { + return; } - ServiceWorkerClientInfo clientInfo(document); - aControlledDocuments.AppendElement(clientInfo); + if (!Preferences::GetBool("dom.serviceWorkers.testing.enabled") && + !IsFromAuthenticatedOrigin(aDoc)) { + return; + } + + ServiceWorkerClientInfo clientInfo(aDoc); + aDocuments.AppendElement(aDoc); + }; + + // Since it's not simple to check whether a document is in + // mControlledDocuments, we take different code paths depending on whether we + // need to look at all documents. The common parts of the two loops are + // factored out into the ProcessDocument lambda. + if (aIncludeUncontrolled) { + bool loop = true; + while (NS_SUCCEEDED(enumerator->HasMoreElements(&loop)) && loop) { + nsCOMPtr ptr; + rv = enumerator->GetNext(getter_AddRefs(ptr)); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + + nsCOMPtr doc = do_QueryInterface(ptr); + ProcessDocument(aPrincipal, doc); + } + } else { + for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) { + ServiceWorkerRegistrationInfo* thisRegistration = iter.UserData(); + MOZ_ASSERT(thisRegistration); + if (!registration->mScope.Equals(thisRegistration->mScope)) { + continue; + } + + nsCOMPtr doc = do_QueryInterface(iter.Key()); + ProcessDocument(aPrincipal, doc); + } } } diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h index 6180e3e98149..b652480d937f 100644 --- a/dom/workers/ServiceWorkerManager.h +++ b/dom/workers/ServiceWorkerManager.h @@ -444,7 +444,8 @@ public: void GetAllClients(nsIPrincipal* aPrincipal, const nsCString& aScope, - nsTArray& aControlledDocuments); + bool aIncludeUncontrolled, + nsTArray& aDocuments); void MaybeClaimClient(nsIDocument* aDocument, diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html.ini deleted file mode 100644 index 6a0a9a997eb6..000000000000 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[clients-matchall-include-uncontrolled.https.html] - type: testharness - expected: TIMEOUT - [Verify matchAll() respect includeUncontrolled] - expected: TIMEOUT - diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html index 4aadb3dd851a..66301fe94047 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html @@ -28,10 +28,10 @@ var expected_without_include_uncontrolled = [ var expected_with_include_uncontrolled = [ /* visibilityState, focused, url, frameType */ + ['visible', true, location.href, 'top-level'], ['visible', false, new URL(scope + '#1', location).toString(), 'nested'], ['visible', true, new URL(scope + '#2', location).toString(), 'nested'], - ['visible', false, new URL(base_url, location).toString(), 'nested'], - ['visible', true, location.href, 'top-level'] + ['visible', false, new URL(base_url, location).toString(), 'nested'] ]; function test_matchall(frame, expected, query_options) { @@ -42,9 +42,13 @@ function test_matchall(frame, expected, query_options) { return new Promise(function(resolve, reject) { var channel = new MessageChannel(); channel.port1.onmessage = function(e) { - assert_equals(e.data.length, expected.length); - for (var i = 0; i < e.data.length; i++) - assert_array_equals(e.data[i], expected[i]); + // Ignore hidden clients which may be coming from background tabs. + var data = e.data.filter(function(info) { + return info[0] == 'visible'; + }); + assert_equals(data.length, expected.length); + for (var i = 0; i < data.length; i++) + assert_array_equals(data[i], expected[i]); resolve(frame); }; frame.contentWindow.navigator.serviceWorker.controller.postMessage( diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-matchall-worker.js b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-matchall-worker.js index be69c4b23e3e..f0ae90d81422 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-matchall-worker.js +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-matchall-worker.js @@ -5,10 +5,17 @@ self.onmessage = function(e) { self.clients.matchAll(options).then(function(clients) { var message = []; clients.forEach(function(client) { + var frame_type = client.frameType; + if (client.url.indexOf('clients-matchall-include-uncontrolled.https.html') > -1 && + client.frameType == 'auxiliary') { + // The test tab might be opened using window.open() by the test framework. + // In that case, just pretend it's top-level! + frame_type = 'top-level'; + } message.push([client.visibilityState, client.focused, client.url, - client.frameType]); + frame_type]); }); // Sort by url message.sort(function(a, b) { return a[2] > b[2] ? 1 : -1; });