Bug 1460490 [wpt PR 10946] - Async Cookies: Change events in service workers., a=testonly

Automatic update from web-platform-testsAsync Cookies: Change events in service workers.

While installing, a service worker may call
cookieStore.subscribeToChanges() to express interest in observing the
changes to the list of cookies sent in a request to a specific URL.
After the service worker becomes active, it is notified of changes to
the cookies it is interested in via  "cookiechange" events, which are
dispatched to the service worker's global scope.

This CL lays the last piece in the foundation of the Async Cookies API,
which is currently gated by the Experimental Web Platform Features flag.
This CL focuses on the core functionality and plumbing. Follow-up CLs
will add more tests, fix the handling of edge cases, and improve
performance.

Bug: 729800
Change-Id: I52f2e550d7901d746ed1a973426d3181ea091147
Reviewed-on: https://chromium-review.googlesource.com/979334
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Reviewed-by: Matt Falkenhagen <falken@chromium.org>
Reviewed-by: Marijn Kruisselbrink <mek@chromium.org>
Reviewed-by: Joshua Bell <jsbell@chromium.org>
Reviewed-by: Mark Pearson <mpearson@chromium.org>
Commit-Queue: Jeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#558682}

--

wpt-commits: c16fdfb6fe53019649d14179bce3e50acdefa3b4
wpt-pr: 10946
This commit is contained in:
Victor Costan 2018-05-17 14:10:01 +00:00 коммит произвёл James Graham
Родитель 19633055bf
Коммит 1fea07d2d4
8 изменённых файлов: 291 добавлений и 6 удалений

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

@ -216294,6 +216294,16 @@
{}
]
],
"cookie-store/serviceworker_cookieStore_subscriptions.js": [
[
{}
]
],
"cookie-store/serviceworker_cookieStore_subscriptions_basic.js": [
[
{}
]
],
"cookies/OWNERS": [
[
{}
@ -315103,6 +315113,18 @@
{}
]
],
"cookie-store/serviceworker_cookieStore_subscriptions.tentative.https.html": [
[
"/cookie-store/serviceworker_cookieStore_subscriptions.tentative.https.html",
{}
]
],
"cookie-store/serviceworker_cookieStore_subscriptions_basic.tentative.https.html": [
[
"/cookie-store/serviceworker_cookieStore_subscriptions_basic.tentative.https.html",
{}
]
],
"cookies/http-state/attribute-tests.html": [
[
"/cookies/http-state/attribute-tests.html",
@ -429907,11 +429929,11 @@
"testharness"
],
"cookie-store/idlharness.tentative.html": [
"2a588a0b80a3cee80af52b9109770fe8ec17becd",
"42be9e73d8e6ad3760a4c9871f6f633962f2ab85",
"testharness"
],
"cookie-store/idlharness_serviceworker.js": [
"aceb00f593b0447853fa70c8c971902cf02a164b",
"516a0596c03b9db2446ca77d70d3eb95f060e895",
"support"
],
"cookie-store/idlharness_serviceworker.tentative.https.html": [
@ -429994,6 +430016,22 @@
"0700dc72bc1e00832546d4a6826a5474600af06c",
"testharness"
],
"cookie-store/serviceworker_cookieStore_subscriptions.js": [
"932a140052fdd95c256bb8a7dfa24522774b569e",
"support"
],
"cookie-store/serviceworker_cookieStore_subscriptions.tentative.https.html": [
"4389e108fcbad909506ce6ea0d30e32f5973bfce",
"testharness"
],
"cookie-store/serviceworker_cookieStore_subscriptions_basic.js": [
"90300c01688cfecd10140078bf36d3801566c2ea",
"support"
],
"cookie-store/serviceworker_cookieStore_subscriptions_basic.tentative.https.html": [
"3836f1d36746ae11b76ee420ba34d902d48bc0af",
"testharness"
],
"cookies/OWNERS": [
"15417c1a9e90762ae826b0258fe3619cc6a78b0e",
"support"
@ -582539,7 +582577,7 @@
"support"
],
"interfaces/cookie-store.idl": [
"0d74ef8e7681fddddfb786b75075a1dd0ddb9147",
"bb4c385873deafd746f186058b111193c8aebf01",
"support"
],
"interfaces/css-font-loading.idl": [

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

@ -42,6 +42,11 @@ promise_test(async t => {
'EventModifierInit',
] });
idl_array.add_untested_idls(
`dictionary ExtendableEventInit {};`);
idl_array.add_untested_idls(
`[Global=ExtendableEvent, Exposed=ServiceWorker]
interface ExtendableEvent : Event {};`);
idl_array.add_untested_idls(
`[Global=ServiceWorker, Exposed=ServiceWorker]
interface ServiceWorkerGlobalScope {};`);

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

@ -16,8 +16,12 @@ promise_test(async t => {
idl_array.add_untested_idls(
`[Global=Event, Exposed=ServiceWorker]
interface Event {};`);
idl_array.add_untested_idls(
`[Global=ExtendableEvent, Exposed=ServiceWorker]
interface ExtendableEvent : Event {};`);
idl_array.add_untested_idls('dictionary EventHandler {};');
idl_array.add_untested_idls('dictionary EventInit {};');
idl_array.add_untested_idls('dictionary ExtendableEventInit {};');
idl_array.add_untested_idls(
`[Global=EventTarget, Exposed=ServiceWorker]
interface EventTarget {};`);
@ -32,7 +36,8 @@ promise_test(async t => {
idl_array.add_objects({
CookieStore: [self.cookieStore],
CookieChangeEvent: [new CookieChangeEvent('change')],
ExtendableCookieChangeEvent: [
new ExtendableCookieChangeEvent('cookiechange')],
});
idl_array.test();
}, 'Interface test');

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

@ -0,0 +1,113 @@
self.GLOBAL = {
isWindow: function() { return false; },
isWorker: function() { return true; },
};
importScripts("/resources/testharness.js");
self.addEventListener('install', (event) => {
event.waitUntil((async () => {
// The subscribeToChanges calls are not done in parallel on purpose. Having
// multiple in-flight requests introduces failure modes aside from the
// cookie change logic that this test aims to cover.
await cookieStore.subscribeToChanges([
{ name: 'cookie-name1', matchType: 'equals', url: '/scope/path1' }]);
await cookieStore.subscribeToChanges([
{ }, // Test the default values for subscription properties.
{ name: 'cookie-prefix', matchType: 'startsWith' },
]);
})());
});
// Workaround because add_cleanup doesn't support async functions yet.
// See https://github.com/w3c/web-platform-tests/issues/6075
async function async_cleanup(cleanup_function) {
try {
await cleanup_function();
} catch (e) {
// Errors in cleanup functions shouldn't result in test failures.
}
}
// Resolves when the service worker receives the 'activate' event.
const kServiceWorkerActivatedPromise = new Promise(resolve => {
self.addEventListener('activate', event => { resolve(); });
});
// sort() comparator that uses the < operator.
//
// This is intended to be used for sorting strings. Using < is preferred to
// localeCompare() because the latter has some implementation-dependent
// behavior.
function CompareStrings(a, b) {
return a < b ? -1 : (b < a ? 1 : 0);
}
promise_test(async testCase => {
await kServiceWorkerActivatedPromise;
const subscriptions = await cookieStore.getChangeSubscriptions();
assert_equals(subscriptions.length, 3);
subscriptions.sort((a, b) => CompareStrings(`${a.name}`, `${b.name}`));
assert_equals(subscriptions[0].name, 'cookie-name1');
assert_equals('equals', subscriptions[0].matchType);
assert_equals(subscriptions[1].name, 'cookie-prefix');
assert_equals('startsWith', subscriptions[1].matchType);
assert_false('name' in subscriptions[2]);
assert_equals('startsWith', subscriptions[2].matchType);
}, 'getChangeSubscriptions returns subscriptions passed to subscribeToChanges');
promise_test(async testCase => {
promise_rejects(
testCase, new TypeError(),
cookieStore.subscribeToChanges([{ name: 'cookie-name2' }]));
}, 'subscribeToChanges rejects when called outside the install handler');
// Accumulates cookiechange events dispatched to the service worker.
let g_cookie_changes = [];
// Resolved when a cookiechange event is received. Rearmed by
// ResetCookieChangeReceivedPromise().
let g_cookie_change_received_promise = null;
let g_cookie_change_received_promise_resolver = null;
self.addEventListener('cookiechange', (event) => {
g_cookie_changes.push(event);
if (g_cookie_change_received_promise_resolver)
g_cookie_change_received_promise_resolver();
});
function RearmCookieChangeReceivedPromise() {
g_cookie_change_received_promise = new Promise((resolve) => {
g_cookie_change_received_promise_resolver = resolve;
});
}
RearmCookieChangeReceivedPromise();
promise_test(async testCase => {
await kServiceWorkerActivatedPromise;
await cookieStore.set('cookie-name', 'cookie-value');
await g_cookie_change_received_promise;
assert_equals(g_cookie_changes.length, 1);
const event = g_cookie_changes[0]
assert_equals(event.type, 'cookiechange');
assert_equals(event.changed.length, 1);
assert_equals(event.changed[0].name, 'cookie-name');
assert_equals(event.changed[0].value, 'cookie-value');
assert_equals(event.deleted.length, 0);
assert_true(event instanceof ExtendableCookieChangeEvent);
assert_true(event instanceof ExtendableEvent);
await async_cleanup(() => {
cookieStore.delete('cookie-name');
g_cookie_changes = [];
RearmCookieChangeReceivedPromise();
});
}, 'cookiechange dispatched with cookie change that matches subscription');
done();

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

@ -0,0 +1,22 @@
<!doctype html>
<meta charset="utf-8">
<title>Async Cookies: cookie change events in ServiceWorker</title>
<link rel="help" href="https://github.com/WICG/cookie-store">
<link rel="author" href="pwnall@chromium.org" title="Victor Costan">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
'use strict';
(async () => {
const scope = 'scope';
let registration = await navigator.serviceWorker.getRegistration(scope);
if (registration)
await registration.unregister();
registration = await navigator.serviceWorker.register(
'serviceworker_cookieStore_subscriptions.js', {scope});
fetch_tests_from_worker(registration.installing);
})();
</script>

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

@ -0,0 +1,63 @@
self.GLOBAL = {
isWindow: function() { return false; },
isWorker: function() { return true; },
};
importScripts("/resources/testharness.js");
self.addEventListener('install', (event) => {
event.waitUntil((async () => {
cookieStore.subscribeToChanges([
{ name: 'cookie-name', matchType: 'equals', url: '/scope/path' }]);
})());
});
// Workaround because add_cleanup doesn't support async functions yet.
// See https://github.com/w3c/web-platform-tests/issues/6075
async function async_cleanup(cleanup_function) {
try {
await cleanup_function();
} catch (e) {
// Errors in cleanup functions shouldn't result in test failures.
}
}
// Resolves when the service worker receives the 'activate' event.
const kServiceWorkerActivatedPromise = new Promise(resolve => {
self.addEventListener('activate', event => { resolve(); });
});
promise_test(async testCase => {
await kServiceWorkerActivatedPromise;
const subscriptions = await cookieStore.getChangeSubscriptions();
assert_equals(subscriptions.length, 1);
assert_equals(subscriptions[0].name, 'cookie-name');
assert_equals('equals', subscriptions[0].matchType);
}, 'getChangeSubscriptions returns a subscription passed to subscribeToChanges');
promise_test(async testCase => {
await kServiceWorkerActivatedPromise;
cookie_change_received_promise = new Promise((resolve) => {
self.addEventListener('cookiechange', (event) => {
resolve(event);
});
});
await cookieStore.set('cookie-name', 'cookie-value');
const event = await cookie_change_received_promise;
assert_equals(event.type, 'cookiechange');
assert_equals(event.changed.length, 1);
assert_equals(event.changed[0].name, 'cookie-name');
assert_equals(event.changed[0].value, 'cookie-value');
assert_equals(event.deleted.length, 0);
assert_true(event instanceof ExtendableCookieChangeEvent);
assert_true(event instanceof ExtendableEvent);
await async_cleanup(() => { cookieStore.delete('cookie-name'); });
}, 'cookiechange dispatched with cookie change that matches subscription');
done();

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

@ -0,0 +1,22 @@
<!doctype html>
<meta charset="utf-8">
<title>Async Cookies: cookie change events in ServiceWorker</title>
<link rel="help" href="https://github.com/WICG/cookie-store">
<link rel="author" href="pwnall@chromium.org" title="Victor Costan">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
'use strict';
(async () => {
const scope = 'scope';
let registration = await navigator.serviceWorker.getRegistration(scope);
if (registration)
await registration.unregister();
registration = await navigator.serviceWorker.register(
'serviceworker_cookieStore_subscriptions_basic.js', {scope});
fetch_tests_from_worker(registration.installing);
})();
</script>

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

@ -13,13 +13,26 @@ dictionary CookieChangeEventInit : EventInit {
};
[
Exposed=(ServiceWorker,Window),
Exposed=Window,
Constructor(DOMString type, optional CookieChangeEventInit eventInitDict)
] interface CookieChangeEvent : Event {
readonly attribute CookieList changed;
readonly attribute CookieList deleted;
};
dictionary ExtendableCookieChangeEventInit : ExtendableEventInit {
CookieList changed;
CookieList deleted;
};
[
Exposed=ServiceWorker,
Constructor(DOMString type, optional ExtendableCookieChangeEventInit eventInitDict)
] interface ExtendableCookieChangeEvent : ExtendableEvent {
readonly attribute CookieList changed;
readonly attribute CookieList deleted;
};
enum CookieMatchType {
"equals",
"startsWith"
@ -59,7 +72,11 @@ dictionary CookieStoreSetOptions {
Promise<void> delete(USVString name, optional CookieStoreSetOptions options);
Promise<void> delete(CookieStoreSetOptions options);
attribute EventHandler onchange;
[Exposed=ServiceWorker] Promise<void> subscribeToChanges(sequence<CookieStoreGetOptions> subscriptions);
[Exposed=ServiceWorker] Promise<sequence<CookieStoreGetOptions>> getChangeSubscriptions();
[Exposed=Window] attribute EventHandler onchange;
};
partial interface Window {