Skip to content

Commit 6dbe2c7

Browse files
jakearchibaldMs2ger
authored andcommitted
1 parent 87bf05e commit 6dbe2c7

File tree

5 files changed

+259
-0
lines changed

5 files changed

+259
-0
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<!DOCTYPE html>
2+
<script src="/resources/testharness.js"></script>
3+
<script src="/resources/testharnessreport.js"></script>
4+
<script src="resources/get-host-info.sub.js"></script>
5+
<script src="resources/test-helpers.sub.js"></script>
6+
<body>
7+
<script>
8+
const worker = 'resources/fetch-event-within-sw-worker.js';
9+
10+
function wait(ms) {
11+
return new Promise(r => setTimeout(r, ms));
12+
}
13+
14+
function reset() {
15+
for (const iframe of [...document.querySelectorAll('.test-iframe')]) {
16+
iframe.remove();
17+
}
18+
return navigator.serviceWorker.getRegistrations().then(registrations => {
19+
return Promise.all(registrations.map(r => r.unregister()));
20+
}).then(() => caches.keys()).then(cacheKeys => {
21+
return Promise.all(cacheKeys.map(c => caches.delete(c)));
22+
});
23+
}
24+
25+
add_completion_callback(reset);
26+
27+
function regReady(reg) {
28+
return new Promise((resolve, reject) => {
29+
if (reg.active) {
30+
resolve();
31+
return;
32+
}
33+
const nextWorker = reg.waiting || reg.installing;
34+
35+
nextWorker.addEventListener('statechange', () => {
36+
if (nextWorker.state == 'redundant') {
37+
reject(Error(`Service worker failed to install`));
38+
return;
39+
}
40+
if (nextWorker.state == 'activated') {
41+
resolve();
42+
}
43+
});
44+
});
45+
}
46+
47+
function getCookies() {
48+
return new Map(
49+
document.cookie
50+
.split(/;/g)
51+
.map(c => c.trim().split('=').map(s => s.trim()))
52+
);
53+
}
54+
55+
function registerSwAndOpenFrame() {
56+
return reset().then(() => navigator.serviceWorker.register(worker, {scope: 'resources/'}))
57+
.then(reg => regReady(reg))
58+
.then(() => with_iframe('resources/simple.html'));
59+
}
60+
61+
function raceBroadcastAndCookie(channel, cookie) {
62+
const initialCookie = getCookies().get(cookie);
63+
let done = false;
64+
65+
return Promise.race([
66+
new Promise(resolve => {
67+
const bc = new BroadcastChannel(channel);
68+
bc.onmessage = () => {
69+
bc.close();
70+
resolve('broadcast');
71+
};
72+
}),
73+
(function checkCookie() {
74+
// Stop polling if the broadcast channel won
75+
if (done == true) return;
76+
if (getCookies().get(cookie) != initialCookie) return 'cookie';
77+
78+
return wait(200).then(checkCookie);
79+
}())
80+
]).then(val => {
81+
done = true;
82+
return val;
83+
});
84+
}
85+
86+
promise_test(() => {
87+
return Notification.requestPermission().then(permission => {
88+
if (permission != "granted") {
89+
throw Error('You must allow notifications for this origin before running this test.');
90+
}
91+
return registerSwAndOpenFrame();
92+
}).then(iframe => {
93+
return Promise.resolve().then(() => {
94+
// In this test, the service worker will ping the 'icon-request' channel
95+
// if it intercepts a request for 'notification_icon.py'. If the request
96+
// reaches the server it sets the 'notification' cookie to the value given
97+
// in the URL. "raceBroadcastAndCookie" monitors both and returns which
98+
// happens first.
99+
const race = raceBroadcastAndCookie('icon-request', 'notification');
100+
const notification = new iframe.contentWindow.Notification('test', {
101+
icon: `notification_icon.py?set-cookie-notification=${Math.random()}`
102+
});
103+
notification.close();
104+
105+
return race.then(winner => {
106+
assert_equals(winner, 'broadcast', 'The service worker intercepted the from-window notification icon request');
107+
});
108+
}).then(() => {
109+
// Similar race to above, but this time the service worker requests the
110+
// notification.
111+
const race = raceBroadcastAndCookie('icon-request', 'notification');
112+
iframe.contentWindow.fetch(`show-notification?set-cookie-notification=${Math.random()}`);
113+
114+
return race.then(winner => {
115+
assert_equals(winner, 'broadcast', 'The service worker intercepted the from-service-worker notification icon request');
116+
});
117+
})
118+
});
119+
}, `Notification requests intercepted both from window and SW`);
120+
121+
</script>
122+
</body>
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<!DOCTYPE html>
2+
<script src="/resources/testharness.js"></script>
3+
<script src="/resources/testharnessreport.js"></script>
4+
<script src="resources/get-host-info.sub.js"></script>
5+
<script src="resources/test-helpers.sub.js"></script>
6+
<body>
7+
<script>
8+
const worker = 'resources/fetch-event-within-sw-worker.js';
9+
10+
function reset() {
11+
for (const iframe of [...document.querySelectorAll('.test-iframe')]) {
12+
iframe.remove();
13+
}
14+
return navigator.serviceWorker.getRegistrations().then(registrations => {
15+
return Promise.all(registrations.map(r => r.unregister()));
16+
}).then(() => caches.keys()).then(cacheKeys => {
17+
return Promise.all(cacheKeys.map(c => caches.delete(c)));
18+
});
19+
}
20+
21+
add_completion_callback(reset);
22+
23+
function regReady(reg) {
24+
return new Promise((resolve, reject) => {
25+
if (reg.active) {
26+
resolve();
27+
return;
28+
}
29+
const nextWorker = reg.waiting || reg.installing;
30+
31+
nextWorker.addEventListener('statechange', () => {
32+
if (nextWorker.state == 'redundant') {
33+
reject(Error(`Service worker failed to install`));
34+
return;
35+
}
36+
if (nextWorker.state == 'activated') {
37+
resolve();
38+
}
39+
});
40+
});
41+
}
42+
43+
function registerSwAndOpenFrame() {
44+
return reset().then(() => navigator.serviceWorker.register(worker, { scope: 'resources/' }))
45+
.then(reg => regReady(reg))
46+
.then(() => with_iframe('resources/simple.html'));
47+
}
48+
49+
promise_test(() => {
50+
return registerSwAndOpenFrame().then(iframe => {
51+
return Promise.all([
52+
iframe.contentWindow.fetch('dummy.txt').then(r => r.text()),
53+
iframe.contentWindow.caches.open('test')
54+
.then(cache =>
55+
cache.add('dummy.txt').then(() => cache.match('dummy.txt'))
56+
).then(response => {
57+
if (!response) return 'cache match failed';
58+
return response.text();
59+
})
60+
])
61+
}).then(([fetchText, cacheText]) => {
62+
assert_equals(fetchText, 'intercepted', 'fetch intercepted');
63+
assert_equals(cacheText, 'intercepted', 'cache.add intercepted');
64+
});
65+
}, 'Service worker intercepts requests from window');
66+
67+
promise_test(() => {
68+
return registerSwAndOpenFrame().then(iframe => {
69+
return Promise.all([
70+
iframe.contentWindow.fetch('dummy.txt-inner-fetch').then(r => r.text()),
71+
iframe.contentWindow.fetch('dummy.txt-inner-cache').then(r => r.text())
72+
])
73+
}).then(([fetchText, cacheText]) => {
74+
assert_equals(fetchText, 'Hello world\n', 'fetch within SW not intercepted');
75+
assert_equals(cacheText, 'Hello world\n', 'cache.add within SW not intercepted');
76+
});
77+
}, `Service worker does not intercept fetch/cache requests within service worker`);
78+
</script>
79+
</body>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
skipWaiting();
2+
3+
addEventListener('fetch', event => {
4+
const url = new URL(event.request.url);
5+
6+
if (url.origin != location.origin) return;
7+
8+
if (url.pathname.endsWith('/dummy.txt')) {
9+
event.respondWith(new Response('intercepted'));
10+
return;
11+
}
12+
13+
if (url.pathname.endsWith('/dummy.txt-inner-fetch')) {
14+
event.respondWith(fetch('dummy.txt'));
15+
return;
16+
}
17+
18+
if (url.pathname.endsWith('/dummy.txt-inner-cache')) {
19+
event.respondWith(
20+
caches.open('test-inner-cache').then(cache =>
21+
cache.add('dummy.txt').then(() => cache.match('dummy.txt'))
22+
)
23+
);
24+
return;
25+
}
26+
27+
if (url.pathname.endsWith('/show-notification')) {
28+
// Copy the currect search string onto the icon url
29+
const iconURL = new URL('notification_icon.py', location);
30+
iconURL.search = url.search;
31+
32+
event.respondWith(
33+
registration.showNotification('test', {
34+
icon: iconURL
35+
}).then(() => registration.getNotifications()).then(notifications => {
36+
for (const n of notifications) n.close();
37+
return new Response('done');
38+
})
39+
);
40+
return;
41+
}
42+
43+
if (url.pathname.endsWith('/notification_icon.py')) {
44+
new BroadcastChannel('icon-request').postMessage('yay');
45+
event.respondWith(new Response('done'));
46+
return;
47+
}
48+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import urlparse
2+
3+
def main(req, res):
4+
qs_cookie_val = urlparse.parse_qs(req.url_parts.query).get('set-cookie-notification')
5+
6+
if qs_cookie_val:
7+
res.set_cookie('notification', qs_cookie_val[0])
8+
9+
return 'not really an icon'

service-workers/service-worker/resources/test-helpers.sub.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ function unreached_rejection(test, prefix) {
5252
function with_iframe(url) {
5353
return new Promise(function(resolve) {
5454
var frame = document.createElement('iframe');
55+
frame.className = 'test-iframe';
5556
frame.src = url;
5657
frame.onload = function() { resolve(frame); };
5758
document.body.appendChild(frame);

0 commit comments

Comments
 (0)