| <!doctype html> | |
| <meta charset=utf-8> | |
| <script src="/resources/testharness.js"></script> | |
| <script src="/resources/testharnessreport.js"></script> | |
| <script src="/common/get-host-info.sub.js"></script> | |
| <script src="/common/utils.js"></script> | |
| <div id=log></div> | |
| <script> | |
| const script = ` | |
| <script> | |
| top.postMessage({event: "loaded", type: location.protocol}, "*"); | |
| <\/script>`; | |
| const test_cases = [ | |
| {name: "data", url: encodeURI(`data:text/html,${script}`)}, | |
| {name: "blob", url: URL.createObjectURL(new Blob([script], { type: "text/html" }))}, | |
| {name: "about", url: "about:blank"}, | |
| ]; | |
| const observeReports = async (frame) => { | |
| const reports = []; | |
| const observer = new frame.contentWindow.ReportingObserver( | |
| rs => reports.push(...rs.map(r => r.toJSON())) | |
| ); | |
| observer.observe(); | |
| // Wait for reports. Use a timeout to catch both expected and unexpected | |
| // reports. | |
| await new Promise(resolve => step_timeout(resolve, 3000)); | |
| return reports; | |
| }; | |
| promise_test(async t => { | |
| const this_window_token = token(); | |
| // Expect the nested frame to not load, since they inherit COEP: none from the | |
| // top frame, which is incompatible with first_frame's COEP: require-corp. | |
| const received_events = []; | |
| addEventListener("message", event => { | |
| if(event.data.event == "loaded") | |
| received_events.push(`Nested ${event.data.type} loaded!`); | |
| }); | |
| // Create an iframe with COEP: require-corp | |
| const first_iframe = document.createElement("iframe"); | |
| t.add_cleanup( () => first_iframe.remove() ); | |
| first_iframe.src = "/common/blank.html?pipe=header(cross-origin-embedder-policy,require-corp)"; | |
| let iframe_load_promise = new Promise( resolve => first_iframe.addEventListener("load", resolve) ); | |
| document.body.append(first_iframe); | |
| await iframe_load_promise; | |
| const reportPromise = observeReports(first_iframe); | |
| // 1. Create nested frames. | |
| // They initially navigate to blank.html and have COEP: require-corp set. | |
| // This initial navigation is required because it uses the parent frame as the | |
| // initiator. That is first_iframe is the initiator, while we want top to be | |
| // the initiator for this test, which will be done in step 4 with a second | |
| // navigation from that blank.html document to the local scheme one. | |
| const nested_frames = {}; | |
| const nested_frames_promises = []; | |
| test_cases.forEach(test => { | |
| nested_frame = document.createElement("iframe"); | |
| nested_frame.src = "/common/blank.html?pipe=header(cross-origin-embedder-policy,require-corp)"; | |
| t.add_cleanup( () => nested_frame.remove() ); | |
| nested_frames_promises.push(new Promise( resolve => nested_frame.addEventListener("load", resolve) ) ); | |
| first_iframe.contentDocument.body.append(nested_frame); | |
| nested_frames[test.name] = nested_frame; | |
| }); | |
| // 2. Wait for the loads of all iframes to complete. | |
| await Promise.all(nested_frames_promises); | |
| // 3. Navigate a dummy frame. This is required because some browsers (Chrome) | |
| // might consider the first navigation in 4. as a redirect otherwise. | |
| const dummy_Frame = document.createElement("iframe"); | |
| t.add_cleanup( () => dummy_Frame.remove() ); | |
| dummy_Frame.src = "/common/blank.html"; | |
| iframe_load_promise = new Promise( resolve => dummy_Frame.addEventListener("load", resolve) ); | |
| document.body.append(dummy_Frame); | |
| await iframe_load_promise; | |
| // 4. Navigate nested frames to a local scheme document. | |
| // COEP should be inherited from the initiator or blobURL's creator (top in both | |
| // cases), this results in COEP being none and the documents not being allowed | |
| // to load under the COEP: require-corp iframe (first_iframe). | |
| test_cases.forEach(test => { | |
| // Top navigates nested_frame_[test.name] to a test.url | |
| const frame = nested_frames[test.name]; | |
| // Use frame.contentDocument.location to ensure the initiator is this (top) | |
| // frame. frame.src is not used here as this makes the parent of the nested | |
| // frame (first_iframe) the initiator. | |
| frame.contentDocument.location = test.url; | |
| }); | |
| // 5. Wait and validate reports. | |
| const reports = await reportPromise; | |
| assert_equals(reports.length, test_cases.length); | |
| test_cases.forEach(test => { | |
| assert_true(reports.some( report => { | |
| return report.type == 'coep' && | |
| report.body.type == 'navigation' && | |
| report.body.blockedURL == test.url; | |
| }), `No report matched for test "${test.name}"`); | |
| }); | |
| // Also verify that no message was sent by the nested frames and stored in | |
| // received_events. | |
| assert_equals(received_events.length, 0); | |
| }, "Prevent local scheme documents from loading within a COEP: require-corp iframe if they inherit COEP: none"); | |
| </script> |