tor-browser

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

head.js (7762B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
      4 
      5 /* eslint-disable no-unused-vars */
      6 
      7 "use strict";
      8 
      9 // This head.js file is only imported by debugger mochitests.
     10 // Anything that is meant to be used by tests of other panels should be moved to shared-head.js
     11 // Also, any symbol that may conflict with other test symbols should stay in head.js
     12 // (like EXAMPLE_URL)
     13 
     14 const EXAMPLE_URL =
     15  "https://example.com/browser/devtools/client/debugger/test/mochitest/examples/";
     16 
     17 // This URL is remote compared to EXAMPLE_URL, as one uses .com and the other uses .org
     18 // Note that this depends on initDebugger to always use EXAMPLE_URL
     19 const EXAMPLE_REMOTE_URL =
     20  "https://example.org/browser/devtools/client/debugger/test/mochitest/examples/";
     21 
     22 const EXAMPLE_URL_WITH_PORT =
     23  "http://mochi.test:8888/browser/devtools/client/debugger/test/mochitest/examples/";
     24 
     25 // shared-head.js handles imports, constants, and utility functions
     26 Services.scriptloader.loadSubScript(
     27  "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
     28  this
     29 );
     30 
     31 Services.scriptloader.loadSubScript(
     32  "chrome://mochitests/content/browser/devtools/client/debugger/test/mochitest/shared-head.js",
     33  this
     34 );
     35 
     36 Services.scriptloader.loadSubScript(
     37  "chrome://mochitests/content/browser/devtools/client/webconsole/test/browser/shared-head.js",
     38  this
     39 );
     40 
     41 // Cleanup preferences set by the tracer.
     42 registerCleanupFunction(() => {
     43  for (const pref of ["logging.console", "logging.PageMessages"]) {
     44    Services.prefs.clearUserPref(pref);
     45  }
     46 });
     47 
     48 /**
     49 * Install a Web Extension which will run a content script against any test page
     50 * served from https://example.com
     51 *
     52 * This content script is meant to be debuggable when devtools.chrome.enabled is true.
     53 */
     54 async function installAndStartContentScriptExtension() {
     55  function contentScript() {
     56    console.log("content script loads");
     57 
     58    // This listener prevents the source from being garbage collected
     59    // and be missing from the scripts returned by `dbg.findScripts()`
     60    // in `ThreadActor._discoverSources`.
     61    window.onload = () => {};
     62  }
     63 
     64  const extension = ExtensionTestUtils.loadExtension({
     65    manifest: {
     66      name: "Test content script extension",
     67      content_scripts: [
     68        {
     69          js: ["content_script.js"],
     70          matches: ["https://example.com/*"],
     71          run_at: "document_start",
     72        },
     73      ],
     74    },
     75    files: {
     76      "content_script.js": contentScript,
     77    },
     78  });
     79 
     80  await extension.startup();
     81 
     82  return extension;
     83 }
     84 
     85 /**
     86 * Return the text content for a given line in the Source Tree.
     87 *
     88 * @param {object} dbg
     89 * @param {number} index
     90 *        Line number in the source tree
     91 */
     92 function getSourceTreeLabel(dbg, index) {
     93  return (
     94    findElement(dbg, "sourceNode", index)
     95      .textContent.trim()
     96      // There is some special whitespace character which aren't removed by trim()
     97      .replace(/^[\s\u200b]*/g, "")
     98  );
     99 }
    100 
    101 /**
    102 * Find and assert the source tree node with the specified text
    103 * exists on the source tree.
    104 *
    105 * @param {object} dbg
    106 * @param {string} text The node text displayed
    107 */
    108 async function assertSourceTreeNode(dbg, text) {
    109  let node = null;
    110  await waitUntil(() => {
    111    node = findSourceNodeWithText(dbg, text);
    112    return !!node;
    113  });
    114  ok(!!node, `Source tree node with text "${text}" exists`);
    115 }
    116 
    117 /**
    118 * Assert precisely the list of all breakable line for a given source
    119 *
    120 * @param {object} dbg
    121 * @param {object | string} file
    122 *        The source name or source object to review
    123 * @param {number} numberOfLines
    124 *        The expected number of lines for this source.
    125 * @param {Array<number>} breakableLines
    126 *        This list of all breakable line numbers
    127 */
    128 async function assertBreakableLines(
    129  dbg,
    130  source,
    131  numberOfLines,
    132  breakableLines
    133 ) {
    134  await selectSource(dbg, source);
    135  is(
    136    getLineCount(dbg),
    137    numberOfLines,
    138    `We show the expected number of lines in CodeMirror for ${source}`
    139  );
    140  for (let line = 1; line <= numberOfLines; line++) {
    141    await assertLineIsBreakable(
    142      dbg,
    143      source,
    144      line,
    145      breakableLines.includes(line)
    146    );
    147  }
    148 }
    149 
    150 /**
    151 * Helper alongside assertBreakable lines to ease defining list of breakable lines.
    152 *
    153 * @param {number} start
    154 * @param {number} end
    155 * @return {Array<number>}
    156 *         Returns an array of decimal numbers starting from `start` and ending with `end`.
    157 */
    158 function getRange(start, end) {
    159  const range = [];
    160  for (let i = start; i <= end; i++) {
    161    range.push(i);
    162  }
    163  return range;
    164 }
    165 
    166 /**
    167 * Get the currently selected line number displayed in the editor's footer.
    168 */
    169 function assertCursorPosition(dbg, expectedLine, expectedColumn, message) {
    170  const cursorPosition = findElementWithSelector(dbg, ".cursor-position");
    171  if (!cursorPosition) {
    172    ok(false, message + " (no cursor displayed in footer)");
    173  }
    174  // Cursor position text has the following shape: (L, C)
    175  // where L is the line number, and C the column number
    176  const match = cursorPosition.innerText.match(/\((\d+), (\d+)\)/);
    177  if (!match) {
    178    ok(
    179      false,
    180      message +
    181        ` (wrong cursor content in footer : '${cursorPosition.innerText}')`
    182    );
    183  }
    184  const [_, line, column] = match;
    185  is(parseInt(line, 10), expectedLine, message + " (footer line)");
    186  is(parseInt(column, 10), expectedColumn, message + " (footer column)");
    187  const cursor = getCMEditor(dbg).getSelectionCursor();
    188  is(cursor.from.line, expectedLine, message + " (actual cursor line)");
    189  // CodeMirror column is 0-based while the location mentioned in test 1-based.
    190  is(cursor.from.ch + 1, expectedColumn, message + " (actual cursor column)");
    191 }
    192 
    193 /**
    194 * @see selectDebuggerContextMenuItem in debugger/test/mochitest/shared-head.js
    195 */
    196 function selectContextMenuItem(dbg, selector) {
    197  return selectDebuggerContextMenuItem(dbg, selector);
    198 }
    199 
    200 function getEventListenersPanel(dbg) {
    201  return findElementWithSelector(dbg, ".event-listeners-pane .event-listeners");
    202 }
    203 
    204 async function toggleEventBreakpoint(
    205  dbg,
    206  eventBreakpointGroup,
    207  eventBreakpointName
    208 ) {
    209  const eventCheckbox = await getEventBreakpointCheckbox(
    210    dbg,
    211    eventBreakpointGroup,
    212    eventBreakpointName
    213  );
    214  eventCheckbox.scrollIntoView();
    215  info(`Toggle ${eventBreakpointName} breakpoint`);
    216  const onEventListenersUpdate = waitForDispatch(
    217    dbg.store,
    218    "UPDATE_EVENT_LISTENERS"
    219  );
    220  const checked = eventCheckbox.checked;
    221  eventCheckbox.click();
    222  await onEventListenersUpdate;
    223 
    224  info("Wait for the event breakpoint checkbox to be toggled");
    225  // Wait for he UI to be toggled, otherwise, the reducer may not be fully updated
    226  await waitFor(() => {
    227    return eventCheckbox.checked == !checked;
    228  });
    229 }
    230 
    231 async function getEventBreakpointCheckbox(
    232  dbg,
    233  eventBreakpointGroup,
    234  eventBreakpointName
    235 ) {
    236  if (!getEventListenersPanel(dbg)) {
    237    // Event listeners panel is collapsed, expand it
    238    findElementWithSelector(
    239      dbg,
    240      `.event-listeners-pane ._header .header-label`
    241    ).click();
    242    await waitFor(() => getEventListenersPanel(dbg));
    243  }
    244 
    245  const groupCheckbox = findElementWithSelector(
    246    dbg,
    247    `input[value="${eventBreakpointGroup}"]`
    248  );
    249  const groupEl = groupCheckbox.closest(".event-listener-group");
    250  let groupEventsUl = groupEl.querySelector("ul");
    251  if (!groupEventsUl) {
    252    info(
    253      `Expand ${eventBreakpointGroup} and wait for the sub list to be displayed`
    254    );
    255    groupEl.querySelector(".event-listener-expand").click();
    256    groupEventsUl = await waitFor(() => groupEl.querySelector("ul"));
    257  }
    258 
    259  return findElementWithSelector(dbg, `input[value="${eventBreakpointName}"]`);
    260 }