browser_target_configuration_command_touch_events.js (7809B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Test touch event simulation. 7 const TEST_DOCUMENT = "target_configuration_test_doc.sjs"; 8 const TEST_URI = URL_ROOT_COM_SSL + TEST_DOCUMENT; 9 10 add_task(async function () { 11 // Disable click hold and double tap zooming as it might interfere with the test 12 await pushPref("ui.click_hold_context_menus", false); 13 await pushPref("apz.allow_double_tap_zooming", false); 14 15 const tab = await addTab(TEST_URI); 16 17 info("Create commands for the tab"); 18 const commands = await CommandsFactory.forTab(tab); 19 20 const targetConfigurationCommand = commands.targetConfigurationCommand; 21 const targetCommand = commands.targetCommand; 22 await targetCommand.startListening(); 23 24 info("Touch simulation is disabled at the beginning"); 25 await checkTopLevelDocumentTouchSimulation({ enabled: false }); 26 await checkIframeTouchSimulation({ 27 enabled: false, 28 }); 29 30 info("Enable touch simulation"); 31 await targetConfigurationCommand.updateConfiguration({ 32 touchEventsOverride: "enabled", 33 }); 34 await checkTopLevelDocumentTouchSimulation({ enabled: true }); 35 await checkIframeTouchSimulation({ 36 enabled: true, 37 }); 38 39 info("Reload the page"); 40 await BrowserTestUtils.reloadTab(tab, { 41 includeSubFrames: true, 42 }); 43 44 is( 45 await topLevelDocumentMatchesCoarsePointerAtStartup(), 46 true, 47 "The touch simulation was enabled in the content page when it loaded after reloading" 48 ); 49 await checkTopLevelDocumentTouchSimulation({ enabled: true }); 50 51 is( 52 await iframeMatchesCoarsePointerAtStartup(), 53 true, 54 "The touch simulation was enabled in the iframe when it loaded after reloading" 55 ); 56 await checkIframeTouchSimulation({ 57 enabled: true, 58 }); 59 60 info( 61 "Create another commands instance and check that destroying it won't reset the touch simulation" 62 ); 63 const otherCommands = await CommandsFactory.forTab(tab); 64 const otherTargetConfigurationCommand = 65 otherCommands.targetConfigurationCommand; 66 const otherTargetCommand = otherCommands.targetCommand; 67 68 await otherTargetCommand.startListening(); 69 // Watch targets so we wait for server communication to settle (e.g. attach calls), as 70 // this could cause intermittent failures. 71 await otherTargetCommand.watchTargets({ 72 types: [otherTargetCommand.TYPES.FRAME], 73 onAvailable: () => {}, 74 }); 75 76 // Let's update the configuration with this commands instance to make sure we hit the TargetConfigurationActor 77 await otherTargetConfigurationCommand.updateConfiguration({ 78 colorSchemeSimulation: "dark", 79 }); 80 81 otherTargetCommand.destroy(); 82 await otherCommands.destroy(); 83 84 await checkTopLevelDocumentTouchSimulation({ enabled: true }); 85 await checkIframeTouchSimulation({ 86 enabled: true, 87 }); 88 89 const previousBrowsingContextId = gBrowser.selectedBrowser.browsingContext.id; 90 info( 91 "Check that navigating to a page that forces the creation of a new browsing context keep the simulation enabled" 92 ); 93 94 const onBrowserLoaded = BrowserTestUtils.browserLoaded( 95 gBrowser.selectedBrowser, 96 true 97 ); 98 BrowserTestUtils.startLoadingURIString( 99 gBrowser.selectedBrowser, 100 URL_ROOT_ORG_SSL + TEST_DOCUMENT + "?crossOriginIsolated=true" 101 ); 102 await onBrowserLoaded; 103 104 isnot( 105 gBrowser.selectedBrowser.browsingContext.id, 106 previousBrowsingContextId, 107 "A new browsing context was created" 108 ); 109 110 is( 111 await topLevelDocumentMatchesCoarsePointerAtStartup(), 112 true, 113 "The touch simulation was enabled in the content page when it loaded after navigating to a new browsing context" 114 ); 115 await checkTopLevelDocumentTouchSimulation({ 116 enabled: true, 117 }); 118 119 is( 120 await iframeMatchesCoarsePointerAtStartup(), 121 true, 122 "The touch simulation was enabled in the iframe when it loaded after navigating to a new browsing context" 123 ); 124 await checkIframeTouchSimulation({ 125 enabled: true, 126 }); 127 128 info( 129 "Check that destroying the commands we enabled the simulation in will disable the simulation" 130 ); 131 targetCommand.destroy(); 132 await commands.destroy(); 133 134 await checkTopLevelDocumentTouchSimulation({ enabled: false }); 135 await checkIframeTouchSimulation({ 136 enabled: false, 137 }); 138 }); 139 140 function matchesCoarsePointer(browserOrBrowsingContext) { 141 return SpecialPowers.spawn( 142 browserOrBrowsingContext, 143 [], 144 () => content.matchMedia("(pointer: coarse)").matches 145 ); 146 } 147 148 function matchesCoarsePointerAtStartup(browserOrBrowsingContext) { 149 return SpecialPowers.spawn( 150 browserOrBrowsingContext, 151 [], 152 () => content.wrappedJSObject.initialMatchesCoarsePointer 153 ); 154 } 155 156 async function isTouchEventEmitted(browserOrBrowsingContext) { 157 const onTimeout = wait(1000).then(() => "TIMEOUT"); 158 const onTouchEvent = SpecialPowers.spawn( 159 browserOrBrowsingContext, 160 [], 161 async () => { 162 content.touchStartController = new content.AbortController(); 163 const el = content.document.querySelector("button"); 164 165 let gotTouchEndEvent = false; 166 167 const promise = new Promise(resolve => { 168 el.addEventListener( 169 "touchend", 170 () => { 171 gotTouchEndEvent = true; 172 resolve(); 173 }, 174 { 175 signal: content.touchStartController.signal, 176 once: true, 177 } 178 ); 179 }); 180 181 // For some reason, it might happen that the event is properly registered and transformed 182 // in the touch simulator, but not received by the event listener we set up just before. 183 // So here let's try to "tap" 3 times to give us more chance to catch the event. 184 for (let i = 0; i < 3; i++) { 185 if (gotTouchEndEvent) { 186 break; 187 } 188 189 // Simulate a "tap" with mousedown and then mouseup. 190 EventUtils.synthesizeMouseAtCenter( 191 el, 192 { type: "mousedown", isSynthesized: false }, 193 content 194 ); 195 196 await new Promise(res => content.setTimeout(res, 10)); 197 EventUtils.synthesizeMouseAtCenter( 198 el, 199 { type: "mouseup", isSynthesized: false }, 200 content 201 ); 202 await new Promise(res => content.setTimeout(res, 50)); 203 } 204 205 return promise; 206 } 207 ); 208 209 const result = await Promise.race([onTimeout, onTouchEvent]); 210 211 // Remove the event listener 212 await SpecialPowers.spawn(browserOrBrowsingContext, [], () => { 213 content.touchStartController.abort(); 214 delete content.touchStartController; 215 }); 216 217 return result !== "TIMEOUT"; 218 } 219 220 async function checkTopLevelDocumentTouchSimulation({ enabled }) { 221 is( 222 await matchesCoarsePointer(gBrowser.selectedBrowser), 223 enabled, 224 `The touch simulation is ${ 225 enabled ? "enabled" : "disabled" 226 } on the top level document` 227 ); 228 229 is( 230 await isTouchEventEmitted(gBrowser.selectedBrowser), 231 enabled, 232 `touch events are ${enabled ? "" : "not "}emitted on the top level document` 233 ); 234 } 235 236 function topLevelDocumentMatchesCoarsePointerAtStartup() { 237 return matchesCoarsePointerAtStartup(gBrowser.selectedBrowser); 238 } 239 240 function getIframeBrowsingContext() { 241 return SpecialPowers.spawn( 242 gBrowser.selectedBrowser, 243 [], 244 () => content.document.querySelector("iframe").browsingContext 245 ); 246 } 247 248 async function checkIframeTouchSimulation({ enabled }) { 249 const iframeBC = await getIframeBrowsingContext(); 250 is( 251 await matchesCoarsePointer(iframeBC), 252 enabled, 253 `The touch simulation is ${enabled ? "enabled" : "disabled"} on the iframe` 254 ); 255 256 is( 257 await isTouchEventEmitted(iframeBC), 258 enabled, 259 `touch events are ${enabled ? "" : "not "}emitted on the iframe` 260 ); 261 } 262 263 async function iframeMatchesCoarsePointerAtStartup() { 264 const iframeBC = await getIframeBrowsingContext(); 265 return matchesCoarsePointerAtStartup(iframeBC); 266 }