教程:使用 Debugger 设置断点

此页面展示了如何使用 Firefox 的 Scratchpad 自己尝试 Debugger API。我们使用 Debugger 在函数中设置断点,然后在每次断点命中时计算表达式。

本教程已针对 Firefox 58 Beta 和 Nightly 版本进行了测试。它不适用于 Firefox 57。

  1. 由于 Debugger API 仅对特权 JavaScript 代码可用,因此您需要使用浏览器内容工具箱来试用它。为此,请打开 Firefox 开发者工具,单击工具箱右上角的选项齿轮,并确保选中“启用浏览器 chrome 和附加组件调试工具箱”和“启用远程调试”。这些位于选项面板的右下角;您可能需要滚动才能看到它们。选中后,您可以关闭开发者工具。

  2. 将以下文本保存到 HTML 文件中

    <div onclick="report('the best div');">Click me!</div>
    <div onclick="report('another great div');">Or me!</div>
    <script>
    function report(what) {
      console.log('clicked: ' + what);
    }
    </script>
    
  3. 在浏览器中访问 HTML 文件,并通过打开 Firefox 菜单、选择“浏览器工具”,然后选择“浏览器内容工具箱”来打开浏览器内容工具箱。如果“浏览器工具”菜单中未显示该项目,请确保您已选中两个框以启用浏览器内容工具箱,如步骤 1 中所述。

  4. 我们的示例代码足够长,因此运行它的最佳方法是使用 Scratchpad 面板,该面板默认情况下未启用。要启用它,请单击浏览器内容工具箱右上角的选项齿轮,并确保选中左侧“默认开发者工具”部分中的“Scratchpad”框。Scratchpad 面板应显示在工具箱顶部,与控制台、调试器和内存面板并排。

  5. 单击 Scratchpad 面板并输入以下代码

    const { addDebuggerToGlobal } = ChromeUtils.importESModule(
      "resource://gre/modules/jsdebugger.sys.mjs"
    );
    
    // This simply defines 'Debugger' in this Scratchpad;
    // it doesn't actually start debugging anything.
    addDebuggerToGlobal(globalThis);
    
    // Create a 'Debugger' instance.
    var dbg = new Debugger;
    
    // Make the tab's top window a debuggee, and get a
    // Debugger.Object referring to the window.
    var windowDO = dbg.addDebuggee(tabs[0].content);
    
    // Get a Debugger.Object referring to the window's `report`
    // function.
    var reportDO = windowDO.getOwnPropertyDescriptor('report').value;
    
    // Set a breakpoint at the entry point of `report`.
    reportDO.script.setBreakpoint(0, {
        hit: function (frame) {
            console.log('hit breakpoint in ' + frame.callee.name);
            console.log('what = ' + frame.eval('what').return);
        }
    });
    
    console.log('Finished setting breakpoint!');
    
  6. 在 Scratchpad 中,确保没有选中任何文本,然后按“运行”按钮。

    现在,单击网页中显示的“点击我!”文本。这将运行 div 元素的 onclick 处理程序。当控制权到达 report 函数的开头时,Debugger 调用断点处理程序的 hit 方法,并传递一个 Debugger.Frame 实例。 hit 方法将断点命中记录到浏览器内容工具箱的控制台。然后它在给定的堆栈帧中计算表达式 what,并记录其结果。工具箱的控制台现在如下所示

    The breakpoint handler's console output

    您也可以单击显示“或者我!”的文本,以查看从不同处理程序调用的 report

    如果 Debugger 无法找到 report 函数,或者控制台输出未显示,请在控制台中计算表达式 tabs[0].content.document.location 以确保 tabs[0] 确实引用了您访问的 HTML 文件。如果您有多个选项卡访问 file: URL,则它们都共享一个内容进程,因此您可能需要使用数组的不同元素作为调试对象。

  7. 再次在 Scratchpad 中按“运行”。现在,单击“点击我!”会导致断点命中被记录两次——每个 Debugger 实例一次。

    多个 Debugger 实例可以观察同一个调试对象。在 Scratchpad 中重新运行代码会创建一个新的 Debugger 实例,将同一个网页添加为其调试对象,然后设置一个新的断点。当您单击 div 元素时,两个 Debugger 的断点都会命中,并且两个处理程序都会运行。

    这表明任意数量的基于 Debugger 的工具可以同时观察单个网页。实际上,您可以使用浏览器内容工具箱的调试器面板在 report 中设置自己的断点,它将与前两个一起触发。但是请记住,当多个调试器共享一个调试对象时,其处理程序运行的顺序未指定。如果多个工具试图影响调试对象的行为,则它们的组合行为可能是不可预测的。

  8. 关闭网页和浏览器内容工具箱。

    由于 Scratchpad 的全局对象和调试对象窗口现在都消失了,因此 Debugger 实例将被垃圾回收,因为它们不再对 Firefox 的行为产生任何可见影响。 Debugger API 尝试尽可能透明地与垃圾回收交互;例如,如果 Debugger.Object 实例及其引用都不可访问,则它们都将被回收,即使属于该阴影的 Debugger 实例继续存在。