browser_markup_events_toggle.js (10425B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 /* import-globals-from helper_events_test_runner.js */ 4 5 "use strict"; 6 7 // Test that event listeners can be disabled and re-enabled from the markup view event bubble. 8 9 const TEST_URL = URL_ROOT_SSL + "doc_markup_events_toggle.html"; 10 11 loadHelperScript("helper_events_test_runner.js"); 12 13 add_task(async function () { 14 const { inspector, toolbox } = await openInspectorForURL(TEST_URL); 15 const { resourceCommand } = toolbox.commands; 16 await inspector.markup.expandAll(); 17 await selectNode("#target", inspector); 18 19 info( 20 "Click on the target element to make sure the event listeners are properly set" 21 ); 22 // There's a "mouseup" event listener that is `console.info` (so we can check "native" events). 23 // In order to know if it was called, we listen for the next console.info resource. 24 let { onResource: onConsoleInfoMessage } = 25 await resourceCommand.waitForNextResource( 26 resourceCommand.TYPES.CONSOLE_MESSAGE, 27 { 28 ignoreExistingResources: true, 29 predicate(resource) { 30 return resource.level == "info"; 31 }, 32 } 33 ); 34 await safeSynthesizeMouseEventAtCenterInContentPage("#target"); 35 36 let data = await getTargetElementHandledEventData(); 37 is(data.click, 1, `target handled one "click" event`); 38 is(data.mousedown, 1, `target handled one "mousedown" event`); 39 await onConsoleInfoMessage; 40 ok(true, `the "mouseup" event listener (console.info) was called`); 41 42 info("Check that the event tooltip has the expected content"); 43 const container = await getContainerForSelector("#target", inspector); 44 const eventTooltipBadge = container.elt.querySelector( 45 ".inspector-badge.interactive[data-event]" 46 ); 47 ok(eventTooltipBadge, "The event tooltip badge is displayed"); 48 49 const tooltip = inspector.markup.eventDetailsTooltip; 50 let onTooltipShown = tooltip.once("shown"); 51 eventTooltipBadge.click(); 52 await onTooltipShown; 53 ok(true, "The tooltip is shown"); 54 55 Assert.deepEqual( 56 getAsciiHeadersViz(tooltip), 57 ["click [x]", "mousedown [x]", "mouseup [x]"], 58 "The expected events are displayed, all enabled" 59 ); 60 ok( 61 !eventTooltipBadge.classList.contains("has-disabled-events"), 62 "The event badge does not have the has-disabled-events class" 63 ); 64 65 const [clickHeader, mousedownHeader, mouseupHeader] = 66 getHeadersInEventTooltip(tooltip); 67 68 info("Uncheck the mousedown event checkbox"); 69 await toggleEventListenerCheckbox(tooltip, mousedownHeader); 70 Assert.deepEqual( 71 getAsciiHeadersViz(tooltip), 72 ["click [x]", "mousedown []", "mouseup [x]"], 73 "mousedown checkbox was unchecked" 74 ); 75 ok( 76 eventTooltipBadge.classList.contains("has-disabled-events"), 77 "Unchecking an event applied the has-disabled-events class to the badge" 78 ); 79 ({ onResource: onConsoleInfoMessage } = 80 await resourceCommand.waitForNextResource( 81 resourceCommand.TYPES.CONSOLE_MESSAGE, 82 { 83 ignoreExistingResources: true, 84 predicate(resource) { 85 return resource.level == "info"; 86 }, 87 } 88 )); 89 await safeSynthesizeMouseEventAtCenterInContentPage("#target"); 90 data = await getTargetElementHandledEventData(); 91 is(data.click, 2, `target handled another "click" event…`); 92 is(data.mousedown, 1, `… but not a mousedown one`); 93 await onConsoleInfoMessage; 94 95 info( 96 "Check that the event badge style is reset when re-enabling all disabled events" 97 ); 98 await toggleEventListenerCheckbox(tooltip, mousedownHeader); 99 Assert.deepEqual( 100 getAsciiHeadersViz(tooltip), 101 ["click [x]", "mousedown [x]", "mouseup [x]"], 102 "mousedown checkbox is checked again" 103 ); 104 ok( 105 !eventTooltipBadge.classList.contains("has-disabled-events"), 106 "The event badge does not have the has-disabled-events class after re-enabling disabled event" 107 ); 108 info("Disable mousedown again for the rest of the test"); 109 await toggleEventListenerCheckbox(tooltip, mousedownHeader); 110 Assert.deepEqual( 111 getAsciiHeadersViz(tooltip), 112 ["click [x]", "mousedown []", "mouseup [x]"], 113 "mousedown checkbox is unchecked again" 114 ); 115 116 info("Uncheck the click event checkbox"); 117 await toggleEventListenerCheckbox(tooltip, clickHeader); 118 Assert.deepEqual( 119 getAsciiHeadersViz(tooltip), 120 ["click []", "mousedown []", "mouseup [x]"], 121 "click checkbox was unchecked" 122 ); 123 ok( 124 eventTooltipBadge.classList.contains("has-disabled-events"), 125 "event badge still has the has-disabled-events class" 126 ); 127 ({ onResource: onConsoleInfoMessage } = 128 await resourceCommand.waitForNextResource( 129 resourceCommand.TYPES.CONSOLE_MESSAGE, 130 { 131 ignoreExistingResources: true, 132 predicate(resource) { 133 return resource.level == "info"; 134 }, 135 } 136 )); 137 await safeSynthesizeMouseEventAtCenterInContentPage("#target"); 138 data = await getTargetElementHandledEventData(); 139 is(data.click, 2, `click event listener was disabled`); 140 is(data.mousedown, 1, `and mousedown still is disabled as well`); 141 await onConsoleInfoMessage; 142 ok(true, `the "click" event listener (console.info) was called`); 143 144 info("Uncheck the mouseup event checkbox"); 145 await toggleEventListenerCheckbox(tooltip, mouseupHeader); 146 Assert.deepEqual( 147 getAsciiHeadersViz(tooltip), 148 ["click []", "mousedown []", "mouseup []"], 149 "mouseup checkbox was unchecked" 150 ); 151 152 ({ onResource: onConsoleInfoMessage } = 153 await resourceCommand.waitForNextResource( 154 resourceCommand.TYPES.CONSOLE_MESSAGE, 155 { 156 ignoreExistingResources: true, 157 predicate(resource) { 158 return resource.level == "info"; 159 }, 160 } 161 )); 162 const onTimeout = wait(500).then(() => "TIMEOUT"); 163 await safeSynthesizeMouseEventAtCenterInContentPage("#target"); 164 const raceResult = await Promise.race([onConsoleInfoMessage, onTimeout]); 165 is( 166 raceResult, 167 "TIMEOUT", 168 "The mouseup event didn't trigger a console.info call, meaning the event listener was disabled" 169 ); 170 171 info("Re-enable the mousedown event"); 172 await toggleEventListenerCheckbox(tooltip, mousedownHeader); 173 Assert.deepEqual( 174 getAsciiHeadersViz(tooltip), 175 ["click []", "mousedown [x]", "mouseup []"], 176 "mousedown checkbox is checked again" 177 ); 178 ok( 179 eventTooltipBadge.classList.contains("has-disabled-events"), 180 "event badge still has the has-disabled-events class" 181 ); 182 await safeSynthesizeMouseEventAtCenterInContentPage("#target"); 183 data = await getTargetElementHandledEventData(); 184 is(data.click, 2, `no additional "click" event were handled`); 185 is( 186 data.mousedown, 187 2, 188 `but we did get a new "mousedown", the event listener was re-enabled` 189 ); 190 191 info("Hide the tooltip and show it again"); 192 const tooltipHidden = tooltip.once("hidden"); 193 tooltip.hide(); 194 await tooltipHidden; 195 196 onTooltipShown = tooltip.once("shown"); 197 eventTooltipBadge.click(); 198 await onTooltipShown; 199 ok(true, "The tooltip is shown again"); 200 201 Assert.deepEqual( 202 getAsciiHeadersViz(tooltip), 203 ["click []", "mousedown [x]", "mouseup []"], 204 "Only mousedown checkbox is checked" 205 ); 206 207 info("Re-enable mouseup events"); 208 await toggleEventListenerCheckbox( 209 tooltip, 210 getHeadersInEventTooltip(tooltip).at(-1) 211 ); 212 Assert.deepEqual( 213 getAsciiHeadersViz(tooltip), 214 ["click []", "mousedown [x]", "mouseup [x]"], 215 "mouseup is checked again" 216 ); 217 218 ({ onResource: onConsoleInfoMessage } = 219 await resourceCommand.waitForNextResource( 220 resourceCommand.TYPES.CONSOLE_MESSAGE, 221 { 222 ignoreExistingResources: true, 223 predicate(resource) { 224 return resource.level == "info"; 225 }, 226 } 227 )); 228 await safeSynthesizeMouseEventAtCenterInContentPage("#target"); 229 await onConsoleInfoMessage; 230 ok(true, "The mouseup event was re-enabled"); 231 data = await getTargetElementHandledEventData(); 232 is(data.click, 2, `"click" is still disabled`); 233 is( 234 data.mousedown, 235 3, 236 `we received a new "mousedown" event as part of the click` 237 ); 238 239 info("Close DevTools to check that event listeners are re-enabled"); 240 await closeToolboxIfOpen(); 241 await safeSynthesizeMouseEventAtCenterInContentPage("#target"); 242 data = await getTargetElementHandledEventData(); 243 is( 244 data.click, 245 3, 246 `a new "click" event was handled after the devtools was closed` 247 ); 248 is( 249 data.mousedown, 250 4, 251 `a new "mousedown" event was handled after the devtools was closed` 252 ); 253 }); 254 255 function getHeadersInEventTooltip(tooltip) { 256 return Array.from(tooltip.panel.querySelectorAll(".event-header")); 257 } 258 259 /** 260 * Get an array of string representing a header in its state, e.g. 261 * [ 262 * "click [x]", 263 * "mousedown []", 264 * ] 265 * 266 * represents an event tooltip with a click and a mousedown event, where the mousedown 267 * event has been disabled. 268 * 269 * @param {EventTooltip} tooltip 270 * @returns Array<String> 271 */ 272 function getAsciiHeadersViz(tooltip) { 273 return getHeadersInEventTooltip(tooltip).map( 274 el => 275 `${el.querySelector(".event-tooltip-event-type").textContent} [${ 276 getHeaderCheckbox(el).checked ? "x" : "" 277 }]` 278 ); 279 } 280 281 function getHeaderCheckbox(headerEl) { 282 return headerEl.querySelector("input[type=checkbox]"); 283 } 284 285 async function toggleEventListenerCheckbox(tooltip, headerEl) { 286 const onEventToggled = tooltip.eventTooltip.once( 287 "event-tooltip-listener-toggled" 288 ); 289 const checkbox = getHeaderCheckbox(headerEl); 290 const previousValue = checkbox.checked; 291 EventUtils.synthesizeMouseAtCenter( 292 getHeaderCheckbox(headerEl), 293 {}, 294 headerEl.ownerGlobal 295 ); 296 await onEventToggled; 297 is(checkbox.checked, !previousValue, "The checkbox was toggled"); 298 is( 299 headerEl.classList.contains("content-expanded"), 300 false, 301 "Clicking on the checkbox did not expand the header" 302 ); 303 } 304 305 /** 306 * @returns Promise<Object> The object keys are event names (e.g. "click", "mousedown"), and 307 * the values are number representing the number of time the event was handled. 308 * Note that "mouseup" isn't handled here. 309 */ 310 function getTargetElementHandledEventData() { 311 return SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { 312 // In doc_markup_events_toggle.html , we count the events handled by the target in 313 // a stringified object in dataset.handledEvents. 314 return JSON.parse( 315 content.document.getElementById("target").dataset.handledEvents 316 ); 317 }); 318 }