tor-browser

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

browser_dbg-javascript-tracer.js (10850B)


      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 // Tests the Javascript Tracing feature.
      6 
      7 "use strict";
      8 
      9 add_task(async function () {
     10  const dbg = await initDebugger("doc-scripts.html");
     11 
     12  // This test covers the Web Console, whereas it is no longer the default output
     13  await toggleJsTracerMenuItem(dbg, "#jstracer-menu-item-console");
     14 
     15  // Add an iframe before starting the tracer to later check for key event on it
     16  const preExistingIframeBrowsingContext = await SpecialPowers.spawn(
     17    gBrowser.selectedBrowser,
     18    [],
     19    async function () {
     20      const iframe = content.document.createElement("iframe");
     21      iframe.src = `data:text/html,<input type="text" value="pre existing iframe" onkeydown="console.log('keydown')" />`;
     22      content.document.body.appendChild(iframe);
     23      await new Promise(resolve => (iframe.onload = resolve));
     24      return iframe.contentWindow.browsingContext;
     25    }
     26  );
     27 
     28  info("Enable the tracing");
     29  await toggleJsTracer(dbg.toolbox);
     30 
     31  ok(
     32    dbg.toolbox.splitConsole,
     33    "Split console is automatically opened when tracing to the console"
     34  );
     35 
     36  await hasConsoleMessage(dbg, "Started tracing to Web Console");
     37 
     38  invokeInTab("main");
     39 
     40  info("Wait for console messages for the whole trace");
     41  // `main` calls `foo` which calls `bar`
     42  await hasConsoleMessage(dbg, "λ main");
     43  await hasConsoleMessage(dbg, "λ foo");
     44  await hasConsoleMessage(dbg, "λ bar");
     45 
     46  const linkEl = await waitForConsoleMessageLink(
     47    dbg.toolbox,
     48    "λ main",
     49    "simple1.js:1:17"
     50  );
     51  linkEl.click();
     52  info("Wait for the main function to be highlighted in the debugger");
     53  await waitForSelectedSource(dbg, "simple1.js");
     54  await waitForSelectedLocation(dbg, 1, 17);
     55 
     56  // Trigger a click to verify we do trace DOM events
     57  BrowserTestUtils.synthesizeMouseAtCenter(
     58    "button",
     59    {},
     60    gBrowser.selectedBrowser
     61  );
     62 
     63  await hasConsoleMessage(dbg, "DOM | node.click");
     64  await hasConsoleMessage(dbg, "λ simple");
     65 
     66  const iframeBrowsingContext = await SpecialPowers.spawn(
     67    gBrowser.selectedBrowser,
     68    [],
     69    async function () {
     70      const iframe = content.document.createElement("iframe");
     71      iframe.src = `data:text/html,<input type="text" value="new iframe" onkeypress="console.log('keypress')" />`;
     72      content.document.body.appendChild(iframe);
     73      await new Promise(resolve => (iframe.onload = resolve));
     74      iframe.contentWindow.document.querySelector("input").focus();
     75      return iframe.contentWindow.browsingContext;
     76    }
     77  );
     78 
     79  await BrowserTestUtils.synthesizeKey("x", {}, iframeBrowsingContext);
     80  await hasConsoleMessage(dbg, "DOM | node.keypress");
     81  await hasConsoleMessage(dbg, "λ onkeypress");
     82 
     83  await SpecialPowers.spawn(
     84    preExistingIframeBrowsingContext,
     85    [],
     86    async function () {
     87      content.document.querySelector("input").focus();
     88    }
     89  );
     90  await BrowserTestUtils.synthesizeKey(
     91    "x",
     92    {},
     93    preExistingIframeBrowsingContext
     94  );
     95  await hasConsoleMessage(dbg, "DOM | node.keydown");
     96  await hasConsoleMessage(dbg, "λ onkeydown");
     97 
     98  // Test Blackboxing
     99  info("Clear the console from previous traces");
    100  const { hud } = await dbg.toolbox.getPanel("webconsole");
    101  hud.ui.clearOutput();
    102  await waitFor(
    103    async () => !(await findConsoleMessages(dbg.toolbox, "λ main")).length,
    104    "Wait for console to be cleared"
    105  );
    106 
    107  info(
    108    "Now blackbox only the source where main function is (simple1.js), but foo and bar are in another module"
    109  );
    110  await clickElement(dbg, "blackbox");
    111  await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES");
    112 
    113  info("Trigger some code from simple1 and simple2");
    114  invokeInTab("main");
    115 
    116  info("Only methods from simple2 are logged");
    117  await hasConsoleMessage(dbg, "λ foo");
    118  await hasConsoleMessage(dbg, "λ bar");
    119  is(
    120    (await findConsoleMessages(dbg.toolbox, "λ main")).length,
    121    0,
    122    "Traces from simple1.js, related to main function are not logged"
    123  );
    124 
    125  info("Revert blackboxing");
    126  await clickElement(dbg, "blackbox");
    127  await waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES");
    128 
    129  // Test Disabling tracing
    130  info("Disable the tracing");
    131  await toggleJsTracer(dbg.toolbox);
    132 
    133  invokeInTab("inline_script2");
    134 
    135  // Let some time for the tracer to appear if we failed disabling the tracing
    136  await wait(1000);
    137 
    138  const messages = await findConsoleMessages(dbg.toolbox, "inline_script2");
    139  is(
    140    messages.length,
    141    0,
    142    "We stopped recording traces, an the function call isn't logged in the console"
    143  );
    144 
    145  // Test Navigations
    146  await navigate(dbg, "doc-sourcemaps2.html", "main.js", "main.min.js");
    147 
    148  info("Re-enable the tracing after navigation");
    149  await toggleJsTracer(dbg.toolbox);
    150 
    151  invokeInTab("logMessage");
    152 
    153  // Test clicking on the function to open the precise related location
    154  const linkEl2 = await waitForConsoleMessageLink(
    155    dbg.toolbox,
    156    "λ logMessage",
    157    "main.js:4:3"
    158  );
    159  linkEl2.click();
    160 
    161  info("Wait for the 'logMessage' function to be highlighted in the debugger");
    162  await waitForSelectedSource(dbg, "main.js");
    163  await waitForSelectedLocation(dbg, 4, 3);
    164  ok(true, "The selected source and location is on the original file");
    165 
    166  await dbg.toolbox.closeToolbox();
    167 });
    168 
    169 add_task(async function testPersitentLogMethod() {
    170  let dbg = await initDebugger("doc-scripts.html");
    171 
    172  is(
    173    dbg.commands.tracerCommand.getTracingOptions().logMethod,
    174    "console",
    175    "By default traces are logged to the console"
    176  );
    177 
    178  info("Change the log method to stdout");
    179  await toggleJsTracerMenuItem(dbg, "#jstracer-menu-item-stdout");
    180 
    181  await dbg.toolbox.closeToolbox();
    182 
    183  dbg = await initDebugger("doc-scripts.html");
    184  is(
    185    dbg.commands.tracerCommand.getTracingOptions().logMethod,
    186    "stdout",
    187    "The new setting has been persisted"
    188  );
    189 
    190  info("Reset back to the default value");
    191  await toggleJsTracerMenuItem(dbg, "#jstracer-menu-item-console");
    192 });
    193 
    194 add_task(async function testPageKeyShortcut() {
    195  // Ensures that the key shortcut emitted in the content process bubbles up to the parent process
    196  await pushPref("test.events.async.enabled", true);
    197 
    198  // Fake DevTools being opened by a real user interaction.
    199  // Tests are bypassing DevToolsStartup to open the tools by calling gDevTools directly.
    200  // By doing so DevToolsStartup considers itself as uninitialized,
    201  // whereas we want it to handle the key shortcut we trigger in this test.
    202  const DevToolsStartup = Cc["@mozilla.org/devtools/startup-clh;1"].getService(
    203    Ci.nsISupports
    204  ).wrappedJSObject;
    205  DevToolsStartup.initialized = true;
    206  registerCleanupFunction(() => {
    207    DevToolsStartup.initialized = false;
    208  });
    209 
    210  const dbg = await initDebuggerWithAbsoluteURL("data:text/html,key-shortcut");
    211 
    212  const button = dbg.toolbox.doc.getElementById("command-button-jstracer");
    213  ok(!button.classList.contains("checked"), "The trace button is off on start");
    214 
    215  info(
    216    "Focus the page in order to assert that the page keeps the focus when enabling the tracer"
    217  );
    218  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    219    content.focus();
    220  });
    221  await waitFor(
    222    () => Services.focus.focusedElement == gBrowser.selectedBrowser
    223  );
    224  is(
    225    Services.focus.focusedElement,
    226    gBrowser.selectedBrowser,
    227    "The tab is still focused before enabling tracing"
    228  );
    229 
    230  info("Toggle ON the tracing via the key shortcut from the web page");
    231  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    232    EventUtils.synthesizeKey(
    233      "VK_5",
    234      { ctrlKey: true, shiftKey: true },
    235      content
    236    );
    237  });
    238 
    239  info("Wait for tracing to be enabled");
    240  await waitFor(() => button.classList.contains("checked"));
    241 
    242  is(
    243    Services.focus.focusedElement,
    244    gBrowser.selectedBrowser,
    245    "The tab is still focused after enabling tracing"
    246  );
    247 
    248  info("Toggle it back off, with the same shortcut");
    249  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    250    EventUtils.synthesizeKey(
    251      "VK_5",
    252      { ctrlKey: true, shiftKey: true },
    253      content
    254    );
    255  });
    256 
    257  info("Wait for tracing to be disabled");
    258  await waitFor(() => !button.classList.contains("checked"));
    259 });
    260 
    261 add_task(async function testPageKeyShortcutWithoutDebugger() {
    262  // Ensures that the key shortcut emitted in the content process bubbles up to the parent process
    263  await pushPref("test.events.async.enabled", true);
    264 
    265  // Fake DevTools being opened by a real user interaction.
    266  // Tests are bypassing DevToolsStartup to open the tools by calling gDevTools directly.
    267  // By doing so DevToolsStartup considers itself as uninitialized,
    268  // whereas we want it to handle the key shortcut we trigger in this test.
    269  const DevToolsStartup = Cc["@mozilla.org/devtools/startup-clh;1"].getService(
    270    Ci.nsISupports
    271  ).wrappedJSObject;
    272  DevToolsStartup.initialized = true;
    273  registerCleanupFunction(() => {
    274    DevToolsStartup.initialized = false;
    275  });
    276 
    277  const toolbox = await openNewTabAndToolbox(
    278    "data:text/html,tracer",
    279    "webconsole"
    280  );
    281 
    282  info(
    283    "Focus the page in order to assert that the page keeps the focus when enabling the tracer"
    284  );
    285  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    286    content.focus();
    287  });
    288  await waitFor(
    289    () => Services.focus.focusedElement == gBrowser.selectedBrowser
    290  );
    291  is(
    292    Services.focus.focusedElement,
    293    gBrowser.selectedBrowser,
    294    "The tab is still focused before enabling tracing"
    295  );
    296 
    297  info("Toggle ON the tracing via the key shortcut from the web page");
    298  const { resourceCommand } = toolbox.commands;
    299  const { onResource: onTracingStateEnabled } =
    300    await resourceCommand.waitForNextResource(
    301      resourceCommand.TYPES.JSTRACER_STATE,
    302      {
    303        ignoreExistingResources: true,
    304        predicate(resource) {
    305          return resource.enabled;
    306        },
    307      }
    308    );
    309  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    310    EventUtils.synthesizeKey(
    311      "VK_5",
    312      { ctrlKey: true, shiftKey: true },
    313      content
    314    );
    315  });
    316  info("Wait for tracing to be enabled");
    317  await onTracingStateEnabled;
    318 
    319  info("Toggle it back off, with the same shortcut");
    320  const { onResource: onTracingStateDisabled } =
    321    await resourceCommand.waitForNextResource(
    322      resourceCommand.TYPES.JSTRACER_STATE,
    323      {
    324        ignoreExistingResources: true,
    325        predicate(resource) {
    326          return !resource.enabled;
    327        },
    328      }
    329    );
    330  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    331    EventUtils.synthesizeKey(
    332      "VK_5",
    333      { ctrlKey: true, shiftKey: true },
    334      content
    335    );
    336  });
    337 
    338  info("Wait for tracing to be disabled");
    339  await onTracingStateDisabled;
    340 });