1+ <!DOCTYPE html>
2+ < title > Service Worker: Client.url is Window creation URL tests</ title >
3+ < script src ="/resources/testharness.js "> </ script >
4+ < script src ="/resources/testharnessreport.js "> </ script >
5+ < script src ="resources/test-helpers.sub.js "> </ script >
6+ < script >
7+ // These tests all validate that the Service Worker's Client.url property is
8+ // correct for different types of Window client navigations.
9+ // All test cases involve an iframe navigating in some manner and then
10+ // the service worker validating that the Client.url property is correct.
11+
12+ // Waits for and returns the next postMessage from `frame` to this window.
13+ function wait_for_message_from_frame ( frame ) {
14+ return new Promise ( resolve => {
15+ const message_handler = e => {
16+ if ( e . source === frame . contentWindow ) {
17+ frame . removeEventListener ( 'message' , message_handler ) ;
18+ resolve ( e . data ) ;
19+ }
20+ } ;
21+ window . addEventListener ( 'message' , message_handler ) ;
22+ } ) ;
23+ }
24+
25+ // Returns a promise to get a client URL under the ServiceWorker's
26+ // (client-url-creation-url-sw.js) control. `target` should be an active
27+ // ServiceWorker to post message. We expect it to send back the client URL
28+ // via the given channel.
29+ function post_message_and_wait_for_response (
30+ target , data ) {
31+ return new Promise ( resolve => {
32+ const channel = new MessageChannel ( ) ;
33+ channel . port1 . onmessage = e => {
34+ resolve ( e . data ) ;
35+ } ;
36+ target . postMessage (
37+ { message : data , port : channel . port2 } ,
38+ [ channel . port2 ] ) ;
39+ } ) ;
40+ }
41+
42+ const SCOPE = 'resources/client-url-creation-url' ;
43+ const IFRAME_URL = SCOPE + '-iframe.html?step=1' ;
44+
45+ // The test helper function implements a test case by
46+ // using an iframe to navigate in various ways described by `navigation_kinds`
47+ // and then validates the ServiceWorker Client.url property of the iframe
48+ // against the provided `expected_client_url`.
49+ // `navigation_kinds` is an array of strings each of which is one of the
50+ // navigation actions supported by client-url-creation-url-iframe.html.
51+ async function test_client_url_helper (
52+ t , navigation_kinds , expected_client_url ) {
53+ const registration = await service_worker_unregister_and_register (
54+ t , 'resources/client-url-creation-url-sw.js' , SCOPE ) ;
55+ t . add_cleanup ( _ => registration . unregister ( ) ) ;
56+ await wait_for_state ( t , registration . installing , 'activated' ) ;
57+
58+ const frame = await with_iframe ( IFRAME_URL ) ;
59+ t . add_cleanup ( _ => frame . remove ( ) ) ;
60+ const message = await wait_for_message_from_frame ( frame ) ;
61+ assert_equals ( message , 'done' , 'iframe loaded successfully' ) ;
62+
63+ for ( let navigation_kind_idx = 0 ;
64+ navigation_kind_idx < navigation_kinds . length ;
65+ ++ navigation_kind_idx ) {
66+ const navigation_kind = navigation_kinds [ navigation_kind_idx ] ;
67+
68+ frame . contentWindow . postMessage ( navigation_kind ) ;
69+ const result = await wait_for_message_from_frame ( frame ) ;
70+ assert_equals ( result , 'done' , 'iframe navigation successfully completed' ) ;
71+ }
72+
73+ const actual_client_url = await post_message_and_wait_for_response (
74+ registration . active , "get_client_url" ) ;
75+
76+ assert_equals (
77+ actual_client_url , expected_client_url , 'Client.url should match' ) ;
78+ }
79+
80+ const EXPECTED_URL_STEP_1 = new URL (
81+ './resources/client-url-creation-url-iframe.html?step=1' , location ) . href ;
82+ const EXPECTED_URL_STEP_2 = new URL (
83+ './resources/client-url-creation-url-iframe.html?step=2' , location ) . href ;
84+
85+ // Tests that perform navigations that don't create a new document
86+ promise_test ( async t => {
87+ await test_client_url_helper ( t , [ ] , EXPECTED_URL_STEP_1 ) ;
88+ } , 'No navigation creation URL is same as window URL' ) ;
89+
90+ promise_test ( async t => {
91+ await test_client_url_helper ( t , [ 'fragment' ] , EXPECTED_URL_STEP_1 ) ;
92+ } , 'Fragment only navigation doesn\'t change creation URL' ) ;
93+
94+ promise_test ( async t => {
95+ await test_client_url_helper ( t , [ 'pushstate' ] , EXPECTED_URL_STEP_1 ) ;
96+ } , 'Pushstate doesn\'t change creation URL' ) ;
97+
98+ promise_test ( async t => {
99+ await test_client_url_helper ( t , [ 'replacestate' ] , EXPECTED_URL_STEP_1 ) ;
100+ } , 'Replacestate doesn\'t change creation URL' ) ;
101+
102+ promise_test ( async t => {
103+ await test_client_url_helper (
104+ t , [ 'pushstate' , 'pushstate' , 'back-within-same-document' ] , EXPECTED_URL_STEP_1 ) ;
105+ } , 'Going back over pushstate to other pushstate via back' ) ;
106+
107+ // Tests that perform navigations that create new document(s)
108+ promise_test ( async t => {
109+ await test_client_url_helper ( t , [ 'query' ] , EXPECTED_URL_STEP_2 ) ;
110+ } , 'Query navigation changes creation URL' ) ;
111+
112+ promise_test ( async t => {
113+ await test_client_url_helper ( t , [ 'reload' ] , EXPECTED_URL_STEP_1 ) ;
114+ } , 'Reloading doesn\'t change creation URL' ) ;
115+
116+ promise_test ( async t => {
117+ await test_client_url_helper ( t , [ 'pushstate' , 'reload' ] , EXPECTED_URL_STEP_2 ) ;
118+ } , 'Reloading pushstate URL changes creation URL' ) ;
119+
120+ promise_test ( async t => {
121+ await test_client_url_helper (
122+ t , [ 'query' , 'pushstate' , 'back-within-same-document' ] , EXPECTED_URL_STEP_2 ) ;
123+ } , 'Going back over pushstate to creation URL' ) ;
124+
125+ promise_test ( async t => {
126+ await test_client_url_helper (
127+ t , [ 'query' , 'query' , 'back-cross-document' ] , EXPECTED_URL_STEP_2 ) ;
128+ } , 'Going back to new document changes creation URL' ) ;
129+ </ script >
0 commit comments