tor-browser

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

browser_browser_toolbox_debugger.js (7545B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 // This test asserts that the new debugger works from the browser toolbox process
      5 
      6 // There are shutdown issues for which multiple rejections are left uncaught.
      7 // See bug 1018184 for resolving these issues.
      8 const { PromiseTestUtils } = ChromeUtils.importESModule(
      9  "resource://testing-common/PromiseTestUtils.sys.mjs"
     10 );
     11 PromiseTestUtils.allowMatchingRejectionsGlobally(/File closed/);
     12 
     13 // On debug test runner, it takes about 50s to run the test.
     14 requestLongerTimeout(4);
     15 
     16 /* eslint-disable mozilla/no-arbitrary-setTimeout */
     17 
     18 const { fetch } = require("resource://devtools/shared/DevToolsUtils.js");
     19 
     20 const debuggerHeadURL =
     21  CHROME_URL_ROOT + "../../../debugger/test/mochitest/shared-head.js";
     22 
     23 add_task(async function runTest() {
     24  let { content: debuggerHead } = await fetch(debuggerHeadURL);
     25 
     26  // We remove its import of shared-head, which isn't available in browser toolbox process
     27  // And isn't needed thanks to testHead's symbols
     28  debuggerHead = debuggerHead.replace(
     29    /Services.scriptloader.loadSubScript[^\)]*\);/g,
     30    ""
     31  );
     32 
     33  await pushPref("devtools.browsertoolbox.scope", "everything");
     34 
     35  const ToolboxTask = await initBrowserToolboxTask();
     36  await ToolboxTask.importFunctions({
     37    // head.js uses this method
     38    registerCleanupFunction: () => {},
     39    waitForDispatch,
     40    waitUntil,
     41  });
     42  await ToolboxTask.importScript(debuggerHead);
     43 
     44  info("### First test breakpoint in the parent process script");
     45  const s = Cu.Sandbox("http://mozilla.org");
     46 
     47  // Use a unique id for the fake script name in order to be able to run
     48  // this test more than once. That's because the Sandbox is not immediately
     49  // destroyed and so the debugger would display only one file but not necessarily
     50  // connected to the latest sandbox.
     51  const id = new Date().getTime();
     52 
     53  // Pass a fake URL to evalInSandbox. If we just pass a filename,
     54  // Debugger is going to fail and only display root folder (`/`) listing.
     55  // But it won't try to fetch this url and use sandbox content as expected.
     56  const testUrl = `http://mozilla.org/browser-toolbox-test-${id}.js`;
     57  Cu.evalInSandbox(
     58    `this.plop = function plop() {
     59  const foo = 1;
     60  return foo;
     61 };`,
     62    s,
     63    "1.8",
     64    testUrl,
     65    0
     66  );
     67 
     68  // Execute the function every second in order to trigger the breakpoint
     69  const interval = setInterval(s.plop, 1000);
     70 
     71  await ToolboxTask.spawn(testUrl, async _testUrl => {
     72    /* global gToolbox, createDebuggerContext, waitForSources, waitForPaused,
     73          addBreakpoint, assertPausedAtSourceAndLine, stepIn, findSource,
     74          removeBreakpoint, resume, selectSource, assertNotPaused, assertBreakpoint,
     75          assertTextContentOnLine, waitForResumed */
     76    Services.prefs.clearUserPref("devtools.debugger.tabs");
     77    Services.prefs.clearUserPref("devtools.debugger.pending-selected-location");
     78 
     79    info("Waiting for debugger load");
     80    await gToolbox.selectTool("jsdebugger");
     81    const dbg = createDebuggerContext(gToolbox);
     82 
     83    await waitForSources(dbg, _testUrl);
     84 
     85    info("Loaded, selecting the test script to debug");
     86    const fileName = _testUrl.match(/browser-toolbox-test.*\.js/)[0];
     87    await selectSource(dbg, fileName);
     88 
     89    info("Add a breakpoint and wait to be paused");
     90    const onPaused = waitForPaused(dbg);
     91    await addBreakpoint(dbg, fileName, 2);
     92    await onPaused;
     93 
     94    const source = findSource(dbg, fileName);
     95    await assertPausedAtSourceAndLine(dbg, source.id, 2);
     96    assertTextContentOnLine(dbg, 2, "const foo = 1;");
     97    is(
     98      dbg.selectors.getBreakpointCount(),
     99      1,
    100      "There is exactly one breakpoint"
    101    );
    102 
    103    await stepIn(dbg);
    104 
    105    await assertPausedAtSourceAndLine(dbg, source.id, 3);
    106    assertTextContentOnLine(dbg, 3, "return foo;");
    107    is(
    108      dbg.selectors.getBreakpointCount(),
    109      1,
    110      "We still have only one breakpoint after step-in"
    111    );
    112 
    113    // Remove the breakpoint before resuming in order to prevent hitting the breakpoint
    114    // again during test closing.
    115    await removeBreakpoint(dbg, source.id, 2);
    116 
    117    await resume(dbg);
    118 
    119    // Let a change for the interval to re-execute
    120    await new Promise(r => setTimeout(r, 1000));
    121 
    122    is(dbg.selectors.getBreakpointCount(), 0, "There is no more breakpoints");
    123 
    124    assertNotPaused(dbg);
    125  });
    126 
    127  clearInterval(interval);
    128 
    129  info("### Now test breakpoint in a privileged content process script");
    130  const testUrl2 = `http://mozilla.org/content-process-test-${id}.js`;
    131  await SpecialPowers.spawn(gBrowser.selectedBrowser, [testUrl2], testUrl => {
    132    // Use a sandbox in order to have a URL to set a breakpoint
    133    const s = Cu.Sandbox("http://mozilla.org");
    134    Cu.evalInSandbox(
    135      `this.foo = function foo() {
    136  const plop = 1;
    137  return plop;
    138 };`,
    139      s,
    140      "1.8",
    141      testUrl,
    142      0
    143    );
    144    content.interval = content.setInterval(s.foo, 1000);
    145  });
    146  await ToolboxTask.spawn(testUrl2, async _testUrl => {
    147    const dbg = createDebuggerContext(gToolbox);
    148 
    149    const fileName = _testUrl.match(/content-process-test.*\.js/)[0];
    150    await waitForSources(dbg, _testUrl);
    151 
    152    await selectSource(dbg, fileName);
    153 
    154    const onPaused = waitForPaused(dbg);
    155    await addBreakpoint(dbg, fileName, 2);
    156    await onPaused;
    157 
    158    const source = findSource(dbg, fileName);
    159    await assertPausedAtSourceAndLine(dbg, source.id, 2);
    160    assertTextContentOnLine(dbg, 2, "const plop = 1;");
    161    await assertBreakpoint(dbg, 2);
    162    is(dbg.selectors.getBreakpointCount(), 1, "We have exactly one breakpoint");
    163 
    164    await stepIn(dbg);
    165 
    166    await assertPausedAtSourceAndLine(dbg, source.id, 3);
    167    assertTextContentOnLine(dbg, 3, "return plop;");
    168    is(
    169      dbg.selectors.getBreakpointCount(),
    170      1,
    171      "We still have only one breakpoint after step-in"
    172    );
    173 
    174    // Remove the breakpoint before resuming in order to prevent hitting the breakpoint
    175    // again during test closing.
    176    await removeBreakpoint(dbg, source.id, 2);
    177 
    178    await resume(dbg);
    179 
    180    // Let a change for the interval to re-execute
    181    await new Promise(r => setTimeout(r, 1000));
    182 
    183    is(dbg.selectors.getBreakpointCount(), 0, "There is no more breakpoints");
    184 
    185    assertNotPaused(dbg);
    186  });
    187 
    188  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
    189    content.clearInterval(content.interval);
    190  });
    191 
    192  info("Trying pausing in a content process that crashes");
    193 
    194  const crashingUrl =
    195    "data:text/html,<script>setTimeout(()=>{debugger;})</script>";
    196  const crashingTab = await addTab(crashingUrl);
    197  await ToolboxTask.spawn(crashingUrl, async url => {
    198    const dbg = createDebuggerContext(gToolbox);
    199    await waitForPaused(dbg);
    200    const source = findSource(dbg, url);
    201    await assertPausedAtSourceAndLine(dbg, source.id, 1);
    202    const thread = dbg.selectors.getThread(dbg.selectors.getCurrentThread());
    203    is(thread.isTopLevel, false, "The current thread is not the top level one");
    204    is(thread.targetType, "process", "The current thread is the tab one");
    205  });
    206 
    207  info(
    208    "Crash the tab and ensure the debugger resumes and switch to the main thread"
    209  );
    210  await BrowserTestUtils.crashFrame(crashingTab.linkedBrowser);
    211 
    212  await ToolboxTask.spawn(null, async () => {
    213    const dbg = createDebuggerContext(gToolbox);
    214    await waitForResumed(dbg);
    215    const thread = dbg.selectors.getThread(dbg.selectors.getCurrentThread());
    216    is(thread.isTopLevel, true, "The current thread is the top level one");
    217  });
    218 
    219  await removeTab(crashingTab);
    220 
    221  await ToolboxTask.destroy();
    222 });