Bug 1656726 - Pause On Exceptions, DOM mutation breakpoints, debugger statements pause in blackboxed original sources r=davidwalsh
authorwartmanm <wartmanm@tutanota.com>
Tue, 04 Aug 2020 18:41:53 +0000
changeset 543244 06949fab0b88274bdb19e0c1346d216cd92daa69
parent 543243 dfc9b2ea97c76338aaae5626189f4a576c4ad706
child 543245 37030207d352cbcb23069ace6c70ee76a04a805c
push id37668
push userbtara@mozilla.com
push dateWed, 05 Aug 2020 03:14:17 +0000
treeherdermozilla-central@451800aa75df [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdavidwalsh
bugs1656726
milestone81.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1656726 - Pause On Exceptions, DOM mutation breakpoints, debugger statements pause in blackboxed original sources r=davidwalsh Differential Revision: https://phabricator.services.mozilla.com/D85718
devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints.js
devtools/client/debugger/test/mochitest/examples/doc-dom-mutation.html
devtools/client/debugger/test/mochitest/examples/dom-mutation.js
devtools/client/debugger/test/mochitest/examples/dom-mutation.js.map
devtools/server/actors/thread.js
devtools/server/tests/xpcshell/test_blackboxing-03.js
devtools/server/tests/xpcshell/test_blackboxing-05.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints.js
@@ -68,12 +68,33 @@ add_task(async function() {
 
   info("Changing subtree to trigger debugger pause");
   SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
     content.document.querySelector("#subtree").click();
   });
   await waitForPaused(dbg);
   await resume(dbg);
 
+  info("Blackboxing the source prevents debugger pause");
+  await waitForSource(dbg, "dom-mutation.original.js");
+
+  const source = findSource(dbg, "dom-mutation.original.js");
+
+  await selectSource(dbg, source);
+  await clickElement(dbg, "blackbox");
+  await waitForDispatch(dbg, "BLACKBOX");
+
+  SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
+    content.document.querySelector("#blackbox").click();
+  });
+
+  await waitForPaused(dbg, "click.js");
+  await resume(dbg);
+
+  await selectSource(dbg, source);
+  await clickElement(dbg, "blackbox");
+  await waitForDispatch(dbg, "BLACKBOX");
+
   info("Removing breakpoints works");
   dbg.win.document.querySelector(".dom-mutation-list .close-btn").click();
   await waitForAllElements(dbg, "domMutationItem", 1, true);
+
 });
--- a/devtools/client/debugger/test/mochitest/examples/doc-dom-mutation.html
+++ b/devtools/client/debugger/test/mochitest/examples/doc-dom-mutation.html
@@ -6,19 +6,12 @@
   <head>
     <meta charset="utf-8"/>
     <title>Debugger test page</title>
   </head>
 
   <body>
     <button title="Hello" id="attribute" onclick="changeAttribute()">Click me!</button>
     <button id="subtree" onclick="changeSubtree()">Click me!</button>
-    <script>
-      function changeAttribute() {
-        document.body.setAttribute("title", "Goodbye");
-      }
-
-      function changeSubtree() {
-        document.body.appendChild(document.createElement("div"));
-      }
-    </script>
+    <button id="blackbox" onclick="changeAttribute();&#xA;debugger;&#xA;//# sourceURL=click.js">Click me!</button>
+    <script src="dom-mutation.js"></script>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/examples/dom-mutation.js
@@ -0,0 +1,9 @@
+function changeAttribute() {
+  const title = document.body.title === "Goodbye" ? "Hello" : "Goodbye";
+  document.body.setAttribute("title", title);
+}
+
+function changeSubtree() {
+  document.body.appendChild(document.createElement("div"));
+}
+//# sourceMappingURL=dom-mutation.js.map
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/examples/dom-mutation.js.map
@@ -0,0 +1,1 @@
+{"version":3,"sources":["dom-mutation.original.js"],"names":[],"mappings":"AAAA,QAAQ,CAAC,eAAe,IAAI;EAC1B,KAAK,CAAC,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,MAAM,OAAO,KAAK,KAAK,KAAK,OAAO,EAAE;EACtE,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,GAAG,KAAK,EAAE;CAC5C;AACD;AACA,QAAQ,CAAC,aAAa,IAAI;EACxB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,aAAa,EAAE,GAAG,IAAI;CAC1D;IACG,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG;AACxC","file":"dom-mutation.js","sourcesContent":["function changeAttribute() {\n  const title = document.body.title === \"Goodbye\" ? \"Hello\" : \"Goodbye\";\n  document.body.setAttribute(\"title\", title);\n}\n\nfunction changeSubtree() {\n  document.body.appendChild(document.createElement(\"div\"));\n}\n"]}
\ No newline at end of file
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -760,34 +760,28 @@ const ThreadActor = ActorClassWithSpec(t
         eventBreakpoint
       );
     } else if (notification.phase === "post" && this._activeEventPause) {
       this._restoreDebuggerHooks(this._activeEventPause);
       this._activeEventPause = null;
     } else if (!notification.phase && !this._activeEventPause) {
       const frame = this.dbg.getNewestFrame();
       if (frame) {
-        const { sourceActor } = this.sources.getFrameLocation(frame);
-        const { url } = sourceActor;
-        if (this.sources.isBlackBoxed(url)) {
+        if (this.sources.isFrameBlackBoxed(frame)) {
           return;
         }
 
         this._pauseAndRespondEventBreakpoint(frame, eventBreakpoint);
       }
     }
   },
 
   _makeEventBreakpointEnterFrame(eventBreakpoint) {
     return frame => {
-      const location = this.sources.getFrameLocation(frame);
-      const { sourceActor, line, column } = location;
-      const { url } = sourceActor;
-
-      if (this.sources.isBlackBoxed(url, line, column)) {
+      if (this.sources.isFrameBlackBoxed(frame)) {
         return undefined;
       }
 
       this._restoreDebuggerHooks(this._activeEventPause);
       this._activeEventPause = null;
 
       return this._pauseAndRespondEventBreakpoint(frame, eventBreakpoint);
     };
@@ -1800,19 +1794,17 @@ const ThreadActor = ActorClassWithSpec(t
       throw new Error("Unexpected mutation breakpoint type");
     }
 
     const frame = this.dbg.getNewestFrame();
     if (!frame) {
       return undefined;
     }
 
-    const location = this.sources.getFrameLocation(frame);
-
-    if (this.skipBreakpoints || this.sources.isBlackBoxed(location.sourceUrl)) {
+    if (this.skipBreakpoints || this.sources.isFrameBlackBoxed(frame)) {
       return undefined;
     }
 
     const global = (targetNode.ownerDocument || targetNode).defaultView;
     assert(global && this.dbg.hasDebuggee(global));
 
     const targetObj = this.dbg
       .makeGlobalObjectReference(global)
@@ -1847,27 +1839,25 @@ const ThreadActor = ActorClassWithSpec(t
   /**
    * A function that the engine calls when a debugger statement has been
    * executed in the specified frame.
    *
    * @param frame Debugger.Frame
    *        The stack frame that contained the debugger statement.
    */
   onDebuggerStatement: function(frame) {
-    const location = this.sources.getFrameLocation(frame);
-
     // Don't pause if
     // 1. we have not moved since the last pause
     // 2. breakpoints are disabled
     // 3. the source is blackboxed
     // 4. there is a breakpoint at the same location
     if (
       !this.hasMoved(frame, "debuggerStatement") ||
       this.skipBreakpoints ||
-      this.sources.isBlackBoxed(location.sourceUrl) ||
+      this.sources.isFrameBlackBoxed(frame) ||
       this.atBreakpointLocation(frame)
     ) {
       return undefined;
     }
 
     return this._pauseAndRespond(frame, { type: "debuggerStatement" });
   },
 
@@ -1917,26 +1907,23 @@ const ThreadActor = ActorClassWithSpec(t
 
     // NS_ERROR_NO_INTERFACE exceptions are a special case in browser code,
     // since they're almost always thrown by QueryInterface functions, and
     // handled cleanly by native code.
     if (!isWorker && value == Cr.NS_ERROR_NO_INTERFACE) {
       return undefined;
     }
 
-    const { sourceActor } = this.sources.getFrameLocation(youngestFrame);
-    const url = sourceActor ? sourceActor.url : null;
-
     // Don't pause on exceptions thrown while inside an evaluation being done on
     // behalf of the client.
     if (this.insideClientEvaluation) {
       return undefined;
     }
 
-    if (this.skipBreakpoints || this.sources.isBlackBoxed(url)) {
+    if (this.skipBreakpoints || this.sources.isFrameBlackBoxed(youngestFrame)) {
       return undefined;
     }
 
     // Now that we've decided to pause, ignore this exception if it's thrown by
     // any older frames.
     for (let frame = youngestFrame.older; frame != null; frame = frame.older) {
       this._handledFrameExceptions.set(frame, value);
     }
--- a/devtools/server/tests/xpcshell/test_blackboxing-03.js
+++ b/devtools/server/tests/xpcshell/test_blackboxing-03.js
@@ -50,16 +50,39 @@ add_task(
     );
 
     Assert.equal(
       packet3.why.type,
       "debuggerStatement",
       "We should stop at the debugger statement again"
     );
     await threadFront.resume();
+
+    // Test the debugger statement in the black boxed range
+    threadFront.setBreakpoint({ sourceUrl: source.url, line: 4 }, {});
+
+    await blackBox(sourceFront, {
+      start: { line: 1, column: 0 },
+      end: { line: 9, column: 0 },
+    });
+
+    const packet4 = await executeOnNextTickAndWaitForPause(
+      debuggee.runTest,
+      threadFront
+    );
+
+    Assert.equal(
+      packet4.why.type,
+      "breakpoint",
+      "We should pass over the debugger statement."
+    );
+
+    threadFront.removeBreakpoint({ sourceUrl: source.url, line: 4 }, {});
+    await unBlackBox(sourceFront);
+    await threadFront.resume();
   })
 );
 
 const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
 const SOURCE_URL = "http://example.com/source.js";
 
 function evalCode(debuggee) {
   /* eslint-disable no-multi-spaces, no-undef */
--- a/devtools/server/tests/xpcshell/test_blackboxing-05.js
+++ b/devtools/server/tests/xpcshell/test_blackboxing-05.js
@@ -26,16 +26,40 @@ add_task(
     const packet = await waitForPause(threadFront);
     const source = await getSourceById(threadFront, packet.frame.where.actor);
 
     Assert.equal(
       source.url,
       SOURCE_URL,
       "We shouldn't pause while in the black boxed source."
     );
+
+    await unBlackBox(sourceFront);
+    await blackBox(sourceFront, {
+      start: { line: 1, column: 0 },
+      end: { line: 4, column: 0 },
+    });
+
+    await threadFront.resume();
+
+    await executeOnNextTickAndWaitForPause(
+      () => evalCode(debuggee),
+      threadFront
+    );
+
+    threadFront.resume();
+    const packet2 = await waitForPause(threadFront);
+    const source2 = await getSourceById(threadFront, packet2.frame.where.actor);
+
+    Assert.equal(
+      source2.url,
+      SOURCE_URL,
+      "We shouldn't pause while in the black boxed source."
+    );
+
     await threadFront.resume();
   })
 );
 
 const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
 const SOURCE_URL = "http://example.com/source.js";
 
 function evalCode(debuggee) {