tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }