script-focus.https.html (6823B)
1 <!DOCTYPE html> 2 <title>Test Script-Based Focus for Fenced Frames</title> 3 <script src="/resources/testharness.js"></script> 4 <script src="/resources/testharnessreport.js"></script> 5 <script src="/resources/testdriver.js"></script> 6 <script src="/resources/testdriver-actions.js"></script> 7 <script src="/resources/testdriver-vendor.js"></script> 8 <script src="/common/utils.js"></script> 9 <script src="resources/utils.js"></script> 10 <script src="/common/dispatcher/dispatcher.js"></script> 11 12 <script src="/common/get-host-info.sub.js"></script> 13 14 <body> 15 <script> 16 async function AttemptButtonFocus(frame, expecting_focus) { 17 await frame.execute(async (expecting_focus) => { 18 const button = document.createElement("button"); 19 document.body.append(button); 20 button.focus(); 21 assert_equals(document.activeElement == button, expecting_focus, 22 "Button's focus should match expected focus"); 23 }, [expecting_focus]); 24 } 25 26 async function ClickOn(element, actions) { 27 // Wait until the window size is initialized. 28 while (window.innerWidth == 0) { 29 await new Promise(resolve => requestAnimationFrame(resolve)); 30 } 31 await actions.pointerMove(0, 0, {origin: element}) 32 .pointerDown() 33 .pointerUp() 34 .send(); 35 } 36 37 async function SetupTest(click=true) { 38 // Clean up any leftover frames from prior tests. 39 document.querySelectorAll("fencedframe").forEach(e => { 40 e.remove(); 41 }) 42 43 const actions = new test_driver.Actions(); 44 45 const frame = attachFencedFrameContext(); 46 const fencedframe_element = frame.element; 47 48 if (click) 49 await ClickOn(document.body, actions); 50 51 return [actions, frame, fencedframe_element]; 52 } 53 54 promise_test(async () => { 55 const [actions, ff1, ff1_element] = await SetupTest(false); 56 57 await ClickOn(ff1_element, actions); 58 await AttemptButtonFocus(ff1, true); 59 60 const button = document.createElement("button"); 61 document.body.append(button); 62 button.focus(); 63 assert_true(document.activeElement == button, 64 "The button should have focus"); 65 assert_false(navigator.userActivation.isActive, 66 "Window should not have user activation"); 67 }, "An embedder can focus out of a fenced frame"); 68 69 promise_test(async () => { 70 const [actions, frame, fencedframe_element] = await SetupTest(); 71 72 await AttemptButtonFocus(frame, false); 73 await ClickOn(fencedframe_element, actions); 74 await AttemptButtonFocus(frame, true); 75 }, "Fenced frames can't pull script focus until getting user activation"); 76 77 promise_test(async t => { 78 const [actions, frame, fencedframe_element] = await SetupTest(); 79 80 await ClickOn(fencedframe_element, actions); 81 await ClickOn(document.body, actions); 82 83 await AttemptButtonFocus(frame, true); 84 85 // Give the browser time to receive the focus event before attempting 86 // another focus. 87 await t.step_timeout(async () => {await AttemptButtonFocus(frame, true);}, 88 500); 89 }, "Focused fenced frames can move programmatic focus within frame"); 90 91 promise_test(async () => { 92 const [actions, frame, fencedframe_element] = await SetupTest(); 93 94 await ClickOn(fencedframe_element, actions); 95 await ClickOn(document.body, actions); 96 97 // This will pull focus across a frame boundary and consume user activation. 98 await AttemptButtonFocus(frame, true); 99 100 await ClickOn(document.body, actions); 101 await AttemptButtonFocus(frame, false); 102 }, "Script focus into a fenced frame consumes user activation"); 103 104 promise_test(async () => { 105 const [actions, ff1, ff1_element] = await SetupTest(); 106 107 const ff2 = attachFencedFrameContext(); 108 const ff2_element = ff2.element; 109 110 await ClickOn(ff1_element, actions); 111 112 await AttemptButtonFocus(ff1, true); 113 await AttemptButtonFocus(ff2, false); 114 }, "Another fenced frame cannot pull focus out of a focused fenced frame"); 115 116 promise_test(async () => { 117 const [actions, ff1, ff1_element] = await SetupTest(); 118 119 await ClickOn(ff1_element, actions); 120 await AttemptButtonFocus(ff1, true); 121 122 await ff1.execute(async () => { 123 const ff2 = attachFencedFrameContext(); 124 125 await ff2.execute(async () => { 126 const button = document.createElement("button"); 127 document.body.append(button); 128 button.focus(); 129 assert_false(document.activeElement == button, 130 "The button should not have focus"); 131 assert_false(navigator.userActivation.isActive, 132 "The fenced frame should not have user activation"); 133 }); 134 }); 135 }, "A fenced frame nested in another fenced frame cannot pull focus"); 136 137 promise_test(async () => { 138 const [actions, ff1, ff1_element] = await SetupTest(); 139 140 await ClickOn(document.body, actions); 141 142 const button = document.createElement("button"); 143 document.body.append(button); 144 button.focus(); 145 assert_equals(document.activeElement, button, 146 "The button in the main page should have focus."); 147 148 await ff1.execute(async () => { 149 assert_false(navigator.userActivation.isActive, 150 "The fenced frame should not have user activation."); 151 window.focus(); 152 }); 153 154 assert_equals(document.activeElement, button, 155 "The button in the main page should still have focus."); 156 }, "A fenced frame cannot pull window.focus() without user activation"); 157 158 promise_test(async () => { 159 const [actions, ff1, ff1_element] = await SetupTest(); 160 161 await ClickOn(ff1_element, actions); 162 await ClickOn(document.body, actions); 163 164 const button = document.createElement("button"); 165 document.body.append(button); 166 button.focus(); 167 assert_equals(document.activeElement, button, 168 "The button should have focus."); 169 170 await ff1.execute(async () => { 171 assert_true(navigator.userActivation.isActive, 172 "The fenced frame should have user activation."); 173 window.focus(); 174 assert_false(navigator.userActivation.isActive, 175 "The fenced frame's user activation should be consumed by the focus"); 176 }); 177 178 assert_equals(document.activeElement, document.body, 179 "The main page's focus should be pulled away from the button."); 180 }, "A fenced frame can pull window.focus() after user activation"); 181 182 promise_test(async () => { 183 var actions = new test_driver.Actions(); 184 185 const frame = attachIFrameContext( 186 {origin: get_host_info().HTTPS_REMOTE_ORIGIN}); 187 const iframe_element = 188 document.body.getElementsByTagName('iframe')[0]; 189 190 await frame.execute(async () => { 191 const button = document.createElement("button"); 192 document.body.append(button); 193 button.focus(); 194 assert_equals(document.activeElement, button, 195 "The button in the iframe should have focus."); 196 }, [true]); 197 198 const button = document.createElement("button"); 199 document.body.append(button); 200 button.focus(); 201 assert_equals(document.activeElement, button, 202 "The button in the main page should have focus."); 203 }, "An cross-origin iframe can pull focus back and forth without activation"); 204 205 </script> 206 </body>