Commit 0cca86c6 authored by Rayan Kanso's avatar Rayan Kanso Committed by Commit Bot

[Background Fetch] Reject BackgroundFetchUpdateEvent::UpdateUI if called again

Also add WPT that check
- calling updateUI works
- calling updateUI more than once throws an exception

Change-Id: I4b8e1847a09fd84a087d4f4af996ccf19c9f212b
Reviewed-on: https://chromium-review.googlesource.com/1159073
Commit-Queue: Rayan Kanso <rayankans@chromium.org>
Reviewed-by: default avatarPeter Beverloo <peter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#582140}
parent b883f432
...@@ -14,14 +14,14 @@ async function getMessageFromServiceWorker() { ...@@ -14,14 +14,14 @@ async function getMessageFromServiceWorker() {
}); });
} }
// Registers the instrumentation Service Worker located at "resources/sw.js" // Registers the |name| instrumentation Service Worker located at "service_workers/"
// with a scope unique to the test page that's running, and waits for it to be // with a scope unique to the test page that's running, and waits for it to be
// activated. The Service Worker will be unregistered automatically. // activated. The Service Worker will be unregistered automatically.
// //
// Depends on /service-workers/service-worker/resources/test-helpers.sub.js // Depends on /service-workers/service-worker/resources/test-helpers.sub.js
async function registerAndActivateServiceWorker(test) { async function registerAndActivateServiceWorker(test, name) {
const script = 'resources/sw.js'; const script = `service_workers/${name}`;
const scope = 'resources/scope' + location.pathname; const scope = 'service_workers/scope' + location.pathname;
let serviceWorkerRegistration = let serviceWorkerRegistration =
await service_worker_unregister_and_register(test, script, scope); await service_worker_unregister_and_register(test, script, scope);
...@@ -35,10 +35,13 @@ async function registerAndActivateServiceWorker(test) { ...@@ -35,10 +35,13 @@ async function registerAndActivateServiceWorker(test) {
// Creates a Promise test for |func| given the |description|. The |func| will be // Creates a Promise test for |func| given the |description|. The |func| will be
// executed with the `backgroundFetch` object of an activated Service Worker // executed with the `backgroundFetch` object of an activated Service Worker
// Registration. // Registration.
function backgroundFetchTest(func, description) { // |workerName| is the name of the service worker file in the service_workers
// directory to register.
function backgroundFetchTest(func, description, workerName = 'sw.js') {
promise_test(async t => { promise_test(async t => {
const serviceWorkerRegistration = await registerAndActivateServiceWorker(t); const serviceWorkerRegistration =
serviceWorkerRegistration.active.postMessage(null /* unused */); await registerAndActivateServiceWorker(t, workerName);
serviceWorkerRegistration.active.postMessage(null);
assert_equals(await getMessageFromServiceWorker(), 'ready'); assert_equals(await getMessageFromServiceWorker(), 'ready');
......
// The source to post setup and completion results to.
let source = null;
function sendMessageToDocument(msg) {
source.postMessage(msg);
}
// Notify the document that the SW is registered and ready.
self.addEventListener('message', event => {
source = event.source;
sendMessageToDocument('ready');
});
importScripts('sw-helpers.js');
async function updateUI(event) {
let updateParams = [];
switch (event.id) {
case 'update-once':
updateParams = [{title: 'Title1'}];
break;
case 'update-twice':
updateParams = [{title: 'Title1'}, {title: 'Title2'}];
break;
}
return Promise.all(updateParams.map(param => event.updateUI(param)))
.then(() => 'update success')
.catch(e => e.message);
}
self.addEventListener('backgroundfetched', event => {
event.waitUntil(updateUI(event)
.then(update => sendMessageToDocument({ type: event.type, update })))
});
let source = null; importScripts('sw-helpers.js');
async function getFetchResult(settledFetch) { async function getFetchResult(settledFetch) {
if (!settledFetch.response) if (!settledFetch.response)
...@@ -11,14 +11,9 @@ async function getFetchResult(settledFetch) { ...@@ -11,14 +11,9 @@ async function getFetchResult(settledFetch) {
}; };
} }
self.addEventListener('message', event => {
source = event.source;
source.postMessage('ready');
});
self.addEventListener('backgroundfetched', event => { self.addEventListener('backgroundfetched', event => {
event.waitUntil( event.waitUntil(
event.fetches.values() event.fetches.values()
.then(fetches => Promise.all(fetches.map(fetch => getFetchResult(fetch)))) .then(fetches => Promise.all(fetches.map(fetch => getFetchResult(fetch))))
.then(results => source.postMessage({ type: event.type, results }))); .then(results => sendMessageToDocument({ type: event.type, results })));
}); });
// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
// META: script=resources/utils.js
'use strict';
// Covers functionality provided by BackgroundFetchUpdateEvent.updateUI().
//
// https://wicg.github.io/background-fetch/#backgroundfetchupdateuievent
const swName = 'sw-update-ui.js';
backgroundFetchTest(async (test, backgroundFetch) => {
const registrationId = 'update-once';
const registration =
await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt');
assert_equals(registration.id, registrationId);
const message = await getMessageFromServiceWorker();
assert_equals(message.update, 'update success');
}, 'Background Fetch updateUI resolves', swName);
backgroundFetchTest(async (test, backgroundFetch) => {
const registrationId = 'update-twice';
const registration =
await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt');
assert_equals(registration.id, registrationId);
const message = await getMessageFromServiceWorker();
assert_equals(message.update, 'updateUI may only be called once.');
}, 'Background Fetch updateUI called twice fails', swName);
...@@ -46,6 +46,16 @@ void BackgroundFetchUpdateEvent::Trace(blink::Visitor* visitor) { ...@@ -46,6 +46,16 @@ void BackgroundFetchUpdateEvent::Trace(blink::Visitor* visitor) {
ScriptPromise BackgroundFetchUpdateEvent::updateUI( ScriptPromise BackgroundFetchUpdateEvent::updateUI(
ScriptState* script_state, ScriptState* script_state,
const BackgroundFetchUIOptions& ui_options) { const BackgroundFetchUIOptions& ui_options) {
if (update_ui_called_) {
// Return a rejected promise as this method should only be called once.
return ScriptPromise::Reject(
script_state,
V8ThrowException::CreateTypeError(script_state->GetIsolate(),
"updateUI may only be called once."));
}
update_ui_called_ = true;
if (!registration_) { if (!registration_) {
// Return a Promise that will never settle when a developer calls this // Return a Promise that will never settle when a developer calls this
// method on a BackgroundFetchedEvent instance they created themselves. // method on a BackgroundFetchedEvent instance they created themselves.
......
...@@ -64,6 +64,8 @@ class MODULES_EXPORT BackgroundFetchUpdateEvent final ...@@ -64,6 +64,8 @@ class MODULES_EXPORT BackgroundFetchUpdateEvent final
void DidUpdateUI(ScriptPromiseResolver* resolver, void DidUpdateUI(ScriptPromiseResolver* resolver,
mojom::blink::BackgroundFetchError error); mojom::blink::BackgroundFetchError error);
bool update_ui_called_ = false;
Member<ServiceWorkerRegistration> registration_; Member<ServiceWorkerRegistration> registration_;
Member<BackgroundFetchIconLoader> loader_; Member<BackgroundFetchIconLoader> loader_;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment