| <!doctype html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <title>Connection partitioning by site</title> |
| <meta name="help" href="https://fetch.spec.whatwg.org/#network-partition-keys"> |
| <meta name="timeout" content="long"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/common/utils.js"></script> |
| <script src="/common/get-host-info.sub.js"></script> |
| </head> |
| <body> |
| <!-- Used to open about:blank tabs from opaque origins --> |
| <iframe id="iframe0" sandbox="allow-popups allow-scripts allow-popups-to-escape-sandbox"></iframe> |
| <iframe id="iframe1" sandbox="allow-popups allow-scripts allow-popups-to-escape-sandbox"></iframe> |
| <script> |
| const host = get_host_info(); |
| |
| // These two origins must correspond to different sites for this test to pass. |
| const POPUP_ORIGINS = [ |
| host.ORIGIN, |
| host.HTTP_NOTSAMESITE_ORIGIN |
| ]; |
| |
| // This origin should ideally correspond to a different site from the two above, but the |
| // tests will still pass if it matches the site of one of the other two origins. |
| const OTHER_ORIGIN = host.REMOTE_ORIGIN; |
| |
| // Except for the csp_sandbox and about:blanks, each test opens up two windows, one at |
| // POPUP_ORIGINS[0], one at POPUP_ORIGINS[1], and has them request subresources from |
| // subresource_origin. All requests (HTML, JS, and fetch requests) for each window go |
| // through network-partition-key.py and have a partition_id parameter, which is used |
| // to check if any request for one window uses the same socket as a request for the |
| // other window. |
| // |
| // Whenever requests from the two different popup windows use the same connection, the |
| // fetch requests all start returning 400 errors, but other requests will continue to |
| // succeed, to make for clearer errors. |
| // |
| // include_credentials indicates whether the fetch requests use credentials or not, |
| // which is interesting as uncredentialed sockets have separate connection pools. |
| const tests = [ |
| { |
| name: 'With credentials', |
| subresource_origin: POPUP_ORIGINS[0], |
| include_credentials: true, |
| popup_params: [ |
| {type: 'main_frame'}, |
| {type: 'main_frame'} |
| ] |
| }, |
| { |
| name: 'Without credentials', |
| subresource_origin: POPUP_ORIGINS[0], |
| include_credentials: false, |
| popup_params: [ |
| {type: 'main_frame'}, |
| {type: 'main_frame'} |
| ] |
| }, |
| { |
| name: 'Cross-site resources with credentials', |
| subresource_origin: OTHER_ORIGIN, |
| include_credentials: true, |
| popup_params: [ |
| {type: 'main_frame'}, |
| {type: 'main_frame'} |
| ] |
| }, |
| { |
| name: 'Cross-site resources without credentials', |
| subresource_origin: OTHER_ORIGIN, |
| include_credentials: false, |
| popup_params: [ |
| {type: 'main_frame'}, |
| {type: 'main_frame'} |
| ] |
| }, |
| { |
| name: 'Iframes', |
| subresource_origin: OTHER_ORIGIN, |
| include_credentials: true, |
| popup_params: [ |
| { |
| type: 'iframe', |
| iframe_origin: OTHER_ORIGIN |
| }, |
| { |
| type: 'iframe', |
| iframe_origin: OTHER_ORIGIN |
| } |
| ] |
| }, |
| { |
| name: 'Workers', |
| subresource_origin: POPUP_ORIGINS[0], |
| include_credentials: true, |
| popup_params: [ |
| {type: 'worker'}, |
| {type: 'worker'} |
| ] |
| }, |
| { |
| name: 'Workers with cross-site resources', |
| subresource_origin: OTHER_ORIGIN, |
| include_credentials: true, |
| popup_params: [ |
| {type: 'worker'}, |
| {type: 'worker'} |
| ] |
| }, |
| { |
| name: 'CSP sandbox', |
| subresource_origin: POPUP_ORIGINS[0], |
| include_credentials: true, |
| popup_params: [ |
| {type: 'csp_sandbox'}, |
| {type: 'csp_sandbox'} |
| ] |
| }, |
| { |
| name: 'about:blank from opaque origin iframe', |
| subresource_origin: OTHER_ORIGIN, |
| include_credentials: true, |
| popup_params: [ |
| {type: 'opaque_about_blank'}, |
| {type: 'opaque_about_blank'} |
| ] |
| }, |
| ]; |
| |
| const BASE_PATH = window.location.pathname.replace(/\/[^\/]*$/, '/'); |
| |
| function create_script_url(origin, uuid, partition_id, dispatch) { |
| return `${origin}${BASE_PATH}resources/network-partition-key.py?uuid=${uuid}&partition_id=${partition_id}&dispatch=${dispatch}` |
| } |
| |
| function run_test(test) { |
| var uuid = token(); |
| |
| // Used to track the opened popup windows, so they can be closed at the end of the test. |
| // They could be closed immediately after use, but safest to keep them open, as browsers |
| // could use closing a window as a hint to close idle sockets that the window used. |
| var popup_windows = []; |
| |
| // Creates a popup window at |url| and waits for a test result. Returns a promise. |
| function create_popup_and_wait_for_result(url) { |
| return new Promise(function(resolve, reject) { |
| popup_windows.push(window.open(url)); |
| // Listen for the result |
| function message_listener(event) { |
| if (event.data.result === 'success') { |
| resolve(); |
| } else if (event.data.result === 'error') { |
| reject(event.data.details); |
| } else { |
| reject('Unexpected message.'); |
| } |
| } |
| window.addEventListener('message', message_listener, {once: 'true'}); |
| }); |
| } |
| |
| // Navigates iframe to url and waits for a test result. Returns a promise. |
| function navigate_iframe_and_wait_for_result(iframe, url) { |
| return new Promise(function(resolve, reject) { |
| iframe.src = url; |
| // Listen for the result |
| function message_listener(event) { |
| if (event.data.result === 'success') { |
| resolve(); |
| } else if (event.data.result === 'error') { |
| reject(event.data.details); |
| } else { |
| reject('Unexpected message.'); |
| } |
| } |
| window.addEventListener('message', message_listener, {once: 'true'}); |
| }); |
| } |
| |
| function make_test_function(test, index) { |
| var popup_params = test.popup_params[index]; |
| return function() { |
| var popup_path; |
| var additional_url_params = ''; |
| var origin = POPUP_ORIGINS[index]; |
| var partition_id = POPUP_ORIGINS[index]; |
| if (popup_params.type == 'main_frame') { |
| popup_path = 'resources/network-partition-checker.html'; |
| } else if (popup_params.type == 'iframe') { |
| popup_path = 'resources/network-partition-iframe-checker.html'; |
| additional_url_params = `&other_origin=${popup_params.iframe_origin}`; |
| } else if (popup_params.type == 'worker') { |
| popup_path = 'resources/network-partition-worker-checker.html'; |
| // The origin of the dedicated worker must mutch the page that loads it. |
| additional_url_params = `&other_origin=${POPUP_ORIGINS[index]}`; |
| } else if (popup_params.type == 'csp_sandbox') { |
| // For the Content-Security-Policy sandbox test, all requests are from the same origin, but |
| // the origin should be treated as an opaque origin, so sockets should not be reused. |
| origin = test.subresource_origin; |
| partition_id = index; |
| popup_path = 'resources/network-partition-checker.html'; |
| // Don't check partition of root document, since the document isn't sandboxed until the |
| // root document is fetched. |
| additional_url_params = '&sandbox=true&nocheck_partition=true' |
| } else if (popup_params.type=='opaque_about_blank') { |
| popup_path = 'resources/network-partition-about-blank-checker.html'; |
| } else if (popup_params.type == 'iframe') { |
| throw 'Unrecognized popup_params.type.'; |
| } |
| var url = create_script_url(origin, uuid, partition_id, 'fetch_file'); |
| url += `&subresource_origin=${test.subresource_origin}` |
| url += `&include_credentials=${test.include_credentials}` |
| url += `&path=${BASE_PATH.substring(1)}${popup_path}`; |
| url += additional_url_params; |
| |
| if (popup_params.type=='opaque_about_blank') { |
| return navigate_iframe_and_wait_for_result(iframe = document.getElementById('iframe' + index), url); |
| } |
| |
| return create_popup_and_wait_for_result(url); |
| } |
| } |
| |
| // Takes a Promise, and cleans up state when the promise has completed, successfully or not, re-throwing |
| // any exception from the passed in Promise. |
| async function clean_up_when_done(promise) { |
| var error; |
| try { |
| await promise; |
| } catch (e) { |
| error = e; |
| } |
| |
| popup_windows.map(function (win) { win.close(); }); |
| |
| try { |
| var cleanup_url = create_script_url(host.ORIGIN, uuid, host.ORIGIN, 'clean_up'); |
| var response = await fetch(cleanup_url, {credentials: 'omit', mode: 'cors'}); |
| assert_equals(await response.text(), 'cleanup complete', `Sever state cleanup failed`); |
| } catch (e) { |
| // Prefer error from the passed in Promise over errors from the fetch request to clean up server state. |
| error = error || e; |
| } |
| if (error) |
| throw error; |
| } |
| |
| return clean_up_when_done( |
| make_test_function(test, 0)() |
| .then(make_test_function(test, 1))); |
| } |
| |
| tests.forEach(function (test) { |
| promise_test( |
| function() { return run_test(test); }, |
| test.name); |
| }) |
| |
| </script> |
| </body> |
| </html> |