Bug 1229056 - Implement ClientQueryOptions.includeUncontrolled; r=jdm

This commit is contained in:
Ehsan Akhgari 2015-11-30 14:29:51 -05:00
parent 5ac100af7e
commit 6c4573cf9e
6 changed files with 87 additions and 30 deletions

View File

@ -181,11 +181,14 @@ class MatchAllRunnable final : public nsRunnable
RefPtr<PromiseWorkerProxy> mPromiseProxy; RefPtr<PromiseWorkerProxy> mPromiseProxy;
nsCString mScope; nsCString mScope;
bool mIncludeUncontrolled;
public: public:
MatchAllRunnable(PromiseWorkerProxy* aPromiseProxy, MatchAllRunnable(PromiseWorkerProxy* aPromiseProxy,
const nsCString& aScope) const nsCString& aScope,
bool aIncludeUncontrolled)
: mPromiseProxy(aPromiseProxy), : mPromiseProxy(aPromiseProxy),
mScope(aScope) mScope(aScope),
mIncludeUncontrolled(aIncludeUncontrolled)
{ {
MOZ_ASSERT(mPromiseProxy); MOZ_ASSERT(mPromiseProxy);
} }
@ -203,7 +206,8 @@ public:
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
nsTArray<ServiceWorkerClientInfo> result; nsTArray<ServiceWorkerClientInfo> result;
swm->GetAllClients(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(), mScope, result); swm->GetAllClients(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(), mScope,
mIncludeUncontrolled, result);
RefPtr<ResolvePromiseWorkerRunnable> r = RefPtr<ResolvePromiseWorkerRunnable> r =
new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(), new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(),
mPromiseProxy, result); mPromiseProxy, result);
@ -689,7 +693,7 @@ ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions,
nsString scope; nsString scope;
mWorkerScope->GetScope(scope); mWorkerScope->GetScope(scope);
if (aOptions.mIncludeUncontrolled || aOptions.mType != ClientType::Window) { if (aOptions.mType != ClientType::Window) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr; return nullptr;
} }
@ -708,7 +712,8 @@ ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions,
RefPtr<MatchAllRunnable> r = RefPtr<MatchAllRunnable> r =
new MatchAllRunnable(promiseProxy, new MatchAllRunnable(promiseProxy,
NS_ConvertUTF16toUTF8(scope)); NS_ConvertUTF16toUTF8(scope),
aOptions.mIncludeUncontrolled);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r))); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
return promise.forget(); return promise.forget();
} }

View File

@ -20,6 +20,7 @@
#include "nsINetworkInterceptController.h" #include "nsINetworkInterceptController.h"
#include "nsIMutableArray.h" #include "nsIMutableArray.h"
#include "nsIScriptError.h" #include "nsIScriptError.h"
#include "nsISimpleEnumerator.h"
#include "nsIUploadChannel2.h" #include "nsIUploadChannel2.h"
#include "nsPIDOMWindow.h" #include "nsPIDOMWindow.h"
#include "nsScriptLoader.h" #include "nsScriptLoader.h"
@ -3760,7 +3761,8 @@ ServiceWorkerManager::GetClient(nsIPrincipal* aPrincipal,
void void
ServiceWorkerManager::GetAllClients(nsIPrincipal* aPrincipal, ServiceWorkerManager::GetAllClients(nsIPrincipal* aPrincipal,
const nsCString& aScope, const nsCString& aScope,
nsTArray<ServiceWorkerClientInfo>& aControlledDocuments) bool aIncludeUncontrolled,
nsTArray<ServiceWorkerClientInfo>& aDocuments)
{ {
MOZ_ASSERT(aPrincipal); MOZ_ASSERT(aPrincipal);
@ -3772,21 +3774,65 @@ ServiceWorkerManager::GetAllClients(nsIPrincipal* aPrincipal,
return; return;
} }
for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) { nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
ServiceWorkerRegistrationInfo* thisRegistration = iter.UserData(); if (NS_WARN_IF(!obs)) {
MOZ_ASSERT(thisRegistration); return;
if (!registration->mScope.Equals(thisRegistration->mScope)) { }
continue;
nsCOMPtr<nsISimpleEnumerator> 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<nsIDocument> document = do_QueryInterface(iter.Key()); bool equals = false;
aPrincipal->Equals(aDoc->NodePrincipal(), &equals);
if (!document || !document->GetWindow()) { if (!equals) {
continue; return;
} }
ServiceWorkerClientInfo clientInfo(document); if (!Preferences::GetBool("dom.serviceWorkers.testing.enabled") &&
aControlledDocuments.AppendElement(clientInfo); !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<nsISupports> ptr;
rv = enumerator->GetNext(getter_AddRefs(ptr));
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
nsCOMPtr<nsIDocument> 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<nsIDocument> doc = do_QueryInterface(iter.Key());
ProcessDocument(aPrincipal, doc);
}
} }
} }

View File

@ -444,7 +444,8 @@ public:
void void
GetAllClients(nsIPrincipal* aPrincipal, GetAllClients(nsIPrincipal* aPrincipal,
const nsCString& aScope, const nsCString& aScope,
nsTArray<ServiceWorkerClientInfo>& aControlledDocuments); bool aIncludeUncontrolled,
nsTArray<ServiceWorkerClientInfo>& aDocuments);
void void
MaybeClaimClient(nsIDocument* aDocument, MaybeClaimClient(nsIDocument* aDocument,

View File

@ -1,6 +0,0 @@
[clients-matchall-include-uncontrolled.https.html]
type: testharness
expected: TIMEOUT
[Verify matchAll() respect includeUncontrolled]
expected: TIMEOUT

View File

@ -28,10 +28,10 @@ var expected_without_include_uncontrolled = [
var expected_with_include_uncontrolled = [ var expected_with_include_uncontrolled = [
/* visibilityState, focused, url, frameType */ /* visibilityState, focused, url, frameType */
['visible', true, location.href, 'top-level'],
['visible', false, new URL(scope + '#1', location).toString(), 'nested'], ['visible', false, new URL(scope + '#1', location).toString(), 'nested'],
['visible', true, new URL(scope + '#2', location).toString(), 'nested'], ['visible', true, new URL(scope + '#2', location).toString(), 'nested'],
['visible', false, new URL(base_url, location).toString(), 'nested'], ['visible', false, new URL(base_url, location).toString(), 'nested']
['visible', true, location.href, 'top-level']
]; ];
function test_matchall(frame, expected, query_options) { function test_matchall(frame, expected, query_options) {
@ -42,9 +42,13 @@ function test_matchall(frame, expected, query_options) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
var channel = new MessageChannel(); var channel = new MessageChannel();
channel.port1.onmessage = function(e) { channel.port1.onmessage = function(e) {
assert_equals(e.data.length, expected.length); // Ignore hidden clients which may be coming from background tabs.
for (var i = 0; i < e.data.length; i++) var data = e.data.filter(function(info) {
assert_array_equals(e.data[i], expected[i]); 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); resolve(frame);
}; };
frame.contentWindow.navigator.serviceWorker.controller.postMessage( frame.contentWindow.navigator.serviceWorker.controller.postMessage(

View File

@ -5,10 +5,17 @@ self.onmessage = function(e) {
self.clients.matchAll(options).then(function(clients) { self.clients.matchAll(options).then(function(clients) {
var message = []; var message = [];
clients.forEach(function(client) { 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, message.push([client.visibilityState,
client.focused, client.focused,
client.url, client.url,
client.frameType]); frame_type]);
}); });
// Sort by url // Sort by url
message.sort(function(a, b) { return a[2] > b[2] ? 1 : -1; }); message.sort(function(a, b) { return a[2] > b[2] ? 1 : -1; });