noopener-helper.js (6167B)
1 const executor_path = '/common/dispatcher/executor.html?pipe='; 2 const coop_header = policy => { 3 return `|header(Cross-Origin-Opener-Policy,${policy})`; 4 }; 5 6 function getExecutorPath(uuid, origin, coop_header) { 7 return origin.origin + executor_path + coop_header + `&uuid=${uuid}`; 8 } 9 10 // Open a same-origin popup with `opener_coop` header, then open a popup with 11 // `openee_coop` header. Check whether the opener can script the openee. 12 const test_noopener_opening_popup = ( 13 opener_coop, 14 openee_coop, 15 origin, 16 opener_expectation 17 ) => { 18 promise_test(async t => { 19 // Set up dispatcher communications. 20 const popup_token = token(); 21 const reply_token = token(); 22 const popup_reply_token = token(); 23 const popup_openee_token = token(); 24 25 const popup_url = getExecutorPath( 26 popup_token, SAME_ORIGIN, coop_header(opener_coop)); 27 28 // We open a popup and then ping it, it will respond after loading. 29 const popup = window.open(popup_url); 30 t.add_cleanup(() => send(popup_token, 'window.close()')); 31 send(popup_token, `send('${reply_token}', 'Popup loaded');`); 32 assert_equals(await receive(reply_token), 'Popup loaded'); 33 34 if (opener_coop == 'noopener-allow-popups') { 35 // Assert that we can't script the popup. 36 await t.step_wait(() => popup.closed, 'Opener popup.closed') 37 } 38 39 // Ensure that the popup has no access to its opener. 40 send(popup_token, ` 41 let openerDOMAccessAllowed = false; 42 try { 43 openerDOMAccessAllowed = !!self.opener.document.URL; 44 } catch(ex) {} 45 const payload = { 46 opener: !!self.opener, 47 openerDOMAccess: openerDOMAccessAllowed 48 }; 49 send('${reply_token}', JSON.stringify(payload)); 50 `); 51 let payload = JSON.parse(await receive(reply_token)); 52 if (opener_coop == 'noopener-allow-popups') { 53 assert_false(payload.opener, 'popup opener'); 54 assert_false(payload.openerDOMAccess, 'popup DOM access'); 55 } 56 57 // Open another popup from inside the popup, and wait for it to load. 58 const popup_openee_url = getExecutorPath(popup_openee_token, origin, 59 coop_header(openee_coop)); 60 send(popup_token, ` 61 window.openee = open("${popup_openee_url}"); 62 await receive('${popup_reply_token}'); 63 const payload = { 64 openee: !!window.openee, 65 closed: window.openee.closed 66 }; 67 send('${reply_token}', JSON.stringify(payload)); 68 `); 69 t.add_cleanup(() => send(popup_token, 'window.openee.close()')); 70 // Notify the popup that its openee was loaded. 71 send(popup_openee_token, ` 72 send('${popup_reply_token}', 'popup openee opened'); 73 `); 74 75 // Assert that the popup has access to its openee. 76 payload = JSON.parse(await receive(reply_token)); 77 assert_true(payload.openee, 'popup openee'); 78 79 // Assert that the openee has access to its popup opener. 80 send(popup_openee_token, ` 81 let openerDOMAccessAllowed = false; 82 try { 83 openerDOMAccessAllowed = !!self.opener.document.URL; 84 } catch(ex) {} 85 const payload = { 86 opener: !!self.opener, 87 openerDOMAccess: openerDOMAccessAllowed 88 }; 89 send('${reply_token}', JSON.stringify(payload)); 90 `); 91 payload = JSON.parse(await receive(reply_token)); 92 if (opener_expectation) { 93 assert_true(payload.opener, 'Opener is not null'); 94 assert_true(payload.openerDOMAccess, 'No DOM access'); 95 } else { 96 assert_false(payload.opener, 'Opener is null'); 97 assert_false(payload.openerDOMAccess, 'No DOM access'); 98 } 99 }, 100 'noopener-allow-popups ensures that the opener cannot script the openee,' + 101 ' but further popups with no COOP can access their opener: ' + 102 opener_coop + '/' + openee_coop + ':' + origin == SAME_ORIGIN); 103 }; 104 105 // Open a same-origin popup with `popup_coop` header, then navigate away toward 106 // one with `noopener-allow-popups`. Check the opener can't script the openee. 107 const test_noopener_navigating_away = (popup_coop) => { 108 promise_test(async t => { 109 // Set up dispatcher communications. 110 const popup_token = token(); 111 const reply_token = token(); 112 const popup_reply_token = token(); 113 const popup_second_token = token(); 114 115 const popup_url = 116 getExecutorPath(popup_token, SAME_ORIGIN, coop_header(popup_coop)); 117 118 // We open a popup and then ping it, it will respond after loading. 119 const popup = window.open(popup_url); 120 send(popup_token, `send('${reply_token}', 'Popup loaded');`); 121 assert_equals(await receive(reply_token), 'Popup loaded'); 122 t.add_cleanup(() => send(popup_token, 'window.close()')); 123 124 // There's an open question if we should check that popup.window is null here. 125 // See https://github.com/whatwg/html/issues/10457 126 // Assert that we cannot script the popup. 127 assert_false(popup.closed, 'popup closed'); 128 129 // Ensure that the popup has no access to its opener. 130 send(popup_token, ` 131 let openerDOMAccessAllowed = false; 132 try { 133 openerDOMAccessAllowed = !!self.opener.document.URL; 134 } catch(ex) {} 135 const payload = { 136 opener: !!self.opener, 137 openerDOMAccess: openerDOMAccessAllowed 138 }; 139 send('${reply_token}', JSON.stringify(payload)); 140 `); 141 let payload = JSON.parse(await receive(reply_token)); 142 assert_true(payload.opener, 'popup opener'); 143 assert_true(payload.openerDOMAccess, 'popup DOM access'); 144 145 // Navigate the popup. 146 const popup_second_url = getExecutorPath( 147 popup_second_token, SAME_ORIGIN, 148 coop_header('noopener-allow-popups')); 149 150 send(popup_token, ` 151 window.location = '${popup_second_url}'; 152 `); 153 154 // Notify the popup that its openee was loaded. 155 send(popup_second_token, ` 156 send('${reply_token}', 'popup navigated away'); 157 `); 158 assert_equals(await receive(reply_token), 'popup navigated away'); 159 160 // There's an open question if we should check that popup.window is null here. 161 // See https://github.com/whatwg/html/issues/10457 162 assert_true(popup.closed, 'popup.closed'); 163 }, 164 'noopener-allow-popups ensures that the opener cannot script the openee,' + 165 ' even after a navigation: ' + popup_coop); 166 };