popup-test.js (3824B)
1 // To use the functions below, be sure to include the following files in your 2 // test: 3 // - "/common/get-host-info.sub.js" to get the different origin values. 4 // - "common.js" to have the origins easily available. 5 // - "/common/dispatcher/dispatcher.js" for cross-origin messaging. 6 // - "/common/utils.js" for token(). 7 8 function getExecutorPath(uuid, origin, headers) { 9 const executor_path = '/common/dispatcher/executor.html?'; 10 const coop_header = headers.coop ? 11 `|header(Cross-Origin-Opener-Policy,${encodeURIComponent(headers.coop)})` : ''; 12 const coep_header = headers.coep ? 13 `|header(Cross-Origin-Embedder-Policy,${encodeURIComponent(headers.coep)})` : ''; 14 return origin + 15 executor_path + 16 `uuid=${uuid}` + 17 '&pipe=' + coop_header + coep_header; 18 } 19 20 function getPopupHasOpener(popup_token) { 21 const reply_token = token(); 22 send(popup_token, `send('${reply_token}', window.opener != null);`); 23 return receive(reply_token); 24 } 25 26 // Return true if |object|.|property| can be called without throwing an error. 27 function canAccessProperty(object, property) { 28 try { 29 const unused = object[property]; 30 return true; 31 } catch (errors) { 32 return false; 33 } 34 } 35 36 // Verifies that a popup with origin `origin` and headers `headers` has 37 // the expected `opener_state` after being opened. 38 async function popup_test(description, origin, headers, expected_opener_state) { 39 promise_test(async t => { 40 const popup_token = token(); 41 const reply_token = token(); 42 43 const popup_url = getExecutorPath( 44 popup_token, 45 origin.origin, 46 headers); 47 48 // We open popup and then ping it, it will respond after loading. 49 const popup = window.open(popup_url); 50 send(popup_token, `send('${reply_token}', 'Popup loaded');`); 51 assert_equals(await receive(reply_token), 'Popup loaded'); 52 53 // Make sure the popup will be closed once the test has run, keeping a clean 54 // state. 55 t.add_cleanup(() => { 56 send(popup_token, `close()`); 57 }); 58 59 // Give some time for things to settle across processes etc. before 60 // proceeding with verifications. 61 await new Promise(resolve => { t.step_timeout(resolve, 500); }); 62 63 // Verify that the opener is in the state we expect it to be in. 64 switch (expected_opener_state) { 65 case 'preserved': { 66 assert_false(popup.closed, 'Popup is closed from opener?'); 67 assert_true(await getPopupHasOpener(popup_token) === "true", 68 'Popup has nulled opener?'); 69 assert_equals(canAccessProperty(popup, "document"), 70 origin === SAME_ORIGIN, 71 'Main page has dom access to the popup?'); 72 assert_true(canAccessProperty(popup, "frames"), 73 'Main page has cross origin access to the popup?'); 74 break; 75 } 76 case 'restricted': { 77 assert_false(popup.closed, 'Popup is closed from opener?'); 78 assert_true(await getPopupHasOpener(popup_token) === "true", 79 'Popup has nulled opener?'); 80 assert_false(canAccessProperty(popup, "document"), 81 'Main page has dom access to the popup?'); 82 assert_false(canAccessProperty(popup, "frames"), 83 'Main page has cross origin access to the popup?'); 84 assert_true(canAccessProperty(popup, "closed"), 85 'Main page has limited cross origin access to the popup?'); 86 break; 87 } 88 case 'severed': { 89 assert_true(popup.closed, 'Popup is closed from opener?'); 90 assert_false(await getPopupHasOpener(popup_token) === "true", 91 'Popup has nulled opener?'); 92 break; 93 } 94 default: 95 assert_unreached(true, "Unrecognized opener relationship: " + expected_opener_state); 96 } 97 }, description); 98 }