| <!DOCTYPE html> |
| <meta charset="utf-8"/> |
| <title>Service Worker: Partitioned Service Workers</title> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="resources/test-helpers.sub.js"></script> |
| <script src="/common/get-host-info.sub.js"></script> |
| <script src="resources/partitioned-utils.js"></script> |
| |
| <body> |
| <!-- Debugging text for both test cases --> |
| The 3p iframe's postMessage: |
| <p id="iframe_response">No message received</p> |
| |
| The nested iframe's postMessage: |
| <p id="nested_iframe_response">No message received</p> |
| |
| <script> |
| promise_test(async t => { |
| const script = './resources/partitioned-storage-sw.js' |
| const scope = './resources/partitioned-' |
| |
| // Add service worker to this 1P context. wait_for_state() and |
| // service_worker_unregister_and_register() are helper functions |
| // for creating test ServiceWorkers defined in: |
| // service-workers/service-worker/resources/test-helpers.sub.js |
| const reg = await service_worker_unregister_and_register(t, script, scope); |
| t.add_cleanup(() => reg.unregister()); |
| await wait_for_state(t, reg.installing, 'activated'); |
| |
| // Registers the message listener with messageEventHandler(), defined in: |
| // service-workers/service-worker/resources/partitioned-utils.js |
| self.addEventListener('message', messageEventHandler); |
| |
| // Open an iframe that will create a promise within the SW. |
| // Defined in service-workers/service-worker/resources/partitioned-storage-sw.js: |
| // `waitUntilResolved.fakehtml`: URL scope that creates the promise. |
| // `?From1pFrame`: query param that tracks which request the service worker is |
| // handling. |
| const wait_frame_url = new URL( |
| './resources/partitioned-waitUntilResolved.fakehtml?From1pFrame', |
| self.location); |
| |
| // Loads a child iframe with wait_frame_url as the content and returns |
| // a promise for the data messaged from the loaded iframe. |
| // loadAndReturnSwData() defined in: |
| // service-workers/service-worker/resources/partitioned-utils.js: |
| const wait_frame_1p_data = await loadAndReturnSwData(t, wait_frame_url, |
| 'iframe'); |
| assert_equals(wait_frame_1p_data.source, 'From1pFrame', |
| 'The data for the 1p frame came from the wrong source'); |
| |
| // Now create a 3p iframe that will try to resolve the SW in a 3p context. |
| const third_party_iframe_url = new URL( |
| './resources/partitioned-service-worker-third-party-iframe.html', |
| get_host_info().HTTPS_ORIGIN + self.location.pathname); |
| |
| // loadAndReturnSwData() creates a HTTPS_NOTSAMESITE_ORIGIN or 3p `window` |
| // element which embeds an iframe with the ServiceWorker and returns |
| // a promise of the data messaged from that frame. |
| const frame_3p_data = await loadAndReturnSwData(t, third_party_iframe_url, 'window'); |
| assert_equals(frame_3p_data.source, 'From3pFrame', |
| 'The data for the 3p frame came from the wrong source'); |
| |
| // Print some debug info to the main frame. |
| document.getElementById("iframe_response").innerHTML = |
| "3p iframe's has_pending: " + frame_3p_data.has_pending + " source: " + |
| frame_3p_data.source + ". "; |
| |
| // Now do the same for the 1p iframe. |
| // Defined in service-workers/service-worker/resources/partitioned-storage-sw.js: |
| // `resolve.fakehtml`: URL scope that resolves the promise. |
| const resolve_frame_url = new URL( |
| './resources/partitioned-resolve.fakehtml?From1pFrame', self.location); |
| |
| const frame_1p_data = await loadAndReturnSwData(t, resolve_frame_url, |
| 'iframe'); |
| assert_equals(frame_1p_data.source, 'From1pFrame', |
| 'The data for the 1p frame came from the wrong source'); |
| // Both the 1p frames should have been serviced by the same service worker ID. |
| // If this isn't the case then that means the SW could have been deactivated |
| // which invalidates the test. |
| assert_equals(frame_1p_data.ID, wait_frame_1p_data.ID, |
| 'The 1p frames were serviced by different service workers.'); |
| |
| document.getElementById("iframe_response").innerHTML += |
| "1p iframe's has_pending: " + frame_1p_data.has_pending + " source: " + |
| frame_1p_data.source; |
| |
| // If partitioning is working correctly then only the 1p iframe should see |
| // (and resolve) its SW's promise. Additionally the two frames should see |
| // different IDs. |
| assert_true(frame_1p_data.has_pending, |
| 'The 1p iframe saw a pending promise in the service worker.'); |
| assert_false(frame_3p_data.has_pending, |
| 'The 3p iframe saw a pending promise in the service worker.'); |
| assert_not_equals(frame_1p_data.ID, frame_3p_data.ID, |
| 'The frames were serviced by the same service worker thread.'); |
| }, 'Services workers under different top-level sites are partitioned.'); |
| |
| // Optional Test: Checking for partitioned ServiceWorkers in an A->B->A |
| // (nested-iframes with cross-site ancestor) scenario. |
| promise_test(async t => { |
| const script = './resources/partitioned-storage-sw.js' |
| const scope = './resources/partitioned-' |
| |
| // Add service worker to this 1P context. wait_for_state() and |
| // service_worker_unregister_and_register() are helper functions |
| // for creating test ServiceWorkers defined in: |
| // service-workers/service-worker/resources/test-helpers.sub.js |
| const reg = await service_worker_unregister_and_register(t, script, scope); |
| t.add_cleanup(() => reg.unregister()); |
| await wait_for_state(t, reg.installing, 'activated'); |
| |
| // Registers the message listener with messageEventHandler(), defined in: |
| // service-workers/service-worker/resources/partitioned-utils.js |
| self.addEventListener('message', messageEventHandler); |
| |
| // Open an iframe that will create a promise within the SW. |
| // Defined in service-workers/service-worker/resources/partitioned-storage-sw.js: |
| // `waitUntilResolved.fakehtml`: URL scope that creates the promise. |
| // `?From1pFrame`: query param that tracks which request the service worker is |
| // handling. |
| const wait_frame_url = new URL( |
| './resources/partitioned-waitUntilResolved.fakehtml?From1pFrame', |
| self.location); |
| |
| // Load a child iframe with wait_frame_url as the content. |
| // loadAndReturnSwData() defined in: |
| // service-workers/service-worker/resources/partitioned-utils.js: |
| const wait_frame_1p_data = await loadAndReturnSwData(t, wait_frame_url, |
| 'iframe'); |
| assert_equals(wait_frame_1p_data.source, 'From1pFrame', |
| 'The data for the 1p frame came from the wrong source'); |
| |
| // Now create a set of nested iframes in the configuration A1->B->A2 |
| // where B is cross-site and A2 is same-site to this top-level |
| // site (A1). The innermost iframe of the nested iframes (A2) will |
| // create an additional iframe to finally resolve the ServiceWorker. |
| const nested_iframe_url = new URL( |
| './resources/partitioned-service-worker-nested-iframe-parent.html', |
| get_host_info().HTTPS_NOTSAMESITE_ORIGIN + self.location.pathname); |
| |
| // Create the nested iframes (which will in turn create the iframe |
| // with the ServiceWorker) and await on receiving its data. |
| const nested_iframe_data = await loadAndReturnSwData(t, nested_iframe_url, 'iframe'); |
| assert_equals(nested_iframe_data.source, 'FromNestedFrame', |
| 'The data for the nested iframe frame came from the wrong source'); |
| |
| // Print some debug info to the main frame. |
| document.getElementById("nested_iframe_response").innerHTML = |
| "Nested iframe's has_pending: " + nested_iframe_data.has_pending + " source: " + |
| nested_iframe_data.source + ". "; |
| |
| // Now do the same for the 1p iframe. |
| // Defined in service-workers/service-worker/resources/partitioned-storage-sw.js: |
| // `resolve.fakehtml`: URL scope that resolves the promise. |
| const resolve_frame_url = new URL( |
| './resources/partitioned-resolve.fakehtml?From1pFrame', self.location); |
| |
| const frame_1p_data = await loadAndReturnSwData(t, resolve_frame_url, |
| 'iframe'); |
| assert_equals(frame_1p_data.source, 'From1pFrame', |
| 'The data for the 1p frame came from the wrong source'); |
| // Both the 1p frames should have been serviced by the same service worker ID. |
| // If this isn't the case then that means the SW could have been deactivated |
| // which invalidates the test. |
| assert_equals(frame_1p_data.ID, wait_frame_1p_data.ID, |
| 'The 1p frames were serviced by different service workers.'); |
| |
| document.getElementById("nested_iframe_response").innerHTML += |
| "1p iframe's has_pending: " + frame_1p_data.has_pending + " source: " + |
| frame_1p_data.source; |
| |
| // If partitioning is working correctly then only the 1p iframe should see |
| // (and resolve) its SW's promise. Additionally, the innermost iframe of |
| // the nested iframes (A2 in the configuration A1->B->A2) should have a |
| // different service worker ID than the 1p (A1) frame. |
| assert_true(frame_1p_data.has_pending, |
| 'The 1p iframe saw a pending promise in the service worker.'); |
| assert_false(nested_iframe_data.has_pending, |
| 'The 3p iframe saw a pending promise in the service worker.'); |
| assert_not_equals(frame_1p_data.ID, nested_iframe_data.ID, |
| 'The frames were serviced by the same service worker thread.'); |
| }, 'Services workers with cross-site ancestors are partitioned.'); |
| |
| </script> |
| </body> |