Bug 1517572 - [release 116] [Frames] Fix CallStack Group with anonymous function. (#7580). r=dwalsh
authorNicolas Chevobbe <nchevobbe@users.noreply.github.com>
Thu, 03 Jan 2019 15:11:07 -0500
changeset 509596 d3524cb549ec299c3922177bd6b077ca3237ea8f
parent 509595 8d53ab973baa4b2f633f0eac2f6fb2766c427ce7
child 509597 9b43f4aa8ed042124e8c101e0bbea42f63cd8f41
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdwalsh
bugs1517572
milestone66.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 1517572 - [release 116] [Frames] Fix CallStack Group with anonymous function. (#7580). r=dwalsh
devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
devtools/client/debugger/new/src/utils/pause/frames/displayName.js
devtools/client/debugger/new/src/utils/pause/frames/index.js
devtools/client/debugger/new/src/utils/pause/scopes/getScope.js
devtools/client/debugger/new/test/mochitest/browser_dbg-call-stack.js
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
@@ -122,23 +122,24 @@ export default class Group extends Compo
             disableContextMenu={disableContextMenu}
           />
         ))}
       </div>
     );
   }
 
   renderDescription() {
+    const { l10n } = this.context;
+
     const frame = this.props.group[0];
-    const displayName = formatDisplayName(frame);
+    const displayName = formatDisplayName(frame, undefined, l10n);
 
     const l10NEntry = this.state.expanded
       ? "callStack.group.collapseTooltip"
       : "callStack.group.expandTooltip";
-    const { l10n } = this.context;
     const title = l10n.getFormatStr(l10NEntry, frame.library);
 
     return (
       <li
         key={frame.id}
         className={classNames("group")}
         onClick={this.toggleFrames}
         tabIndex={0}
--- a/devtools/client/debugger/new/src/utils/pause/frames/displayName.js
+++ b/devtools/client/debugger/new/src/utils/pause/frames/displayName.js
@@ -79,26 +79,26 @@ function getFrameDisplayName(frame: Loca
 }
 
 type formatDisplayNameParams = {
   shouldMapDisplayName: boolean
 };
 export function formatDisplayName(
   frame: LocalFrame,
   { shouldMapDisplayName = true }: formatDisplayNameParams = {},
-  l10n: Object
+  l10n: typeof L10N
 ): string {
   const { library } = frame;
   let displayName = getFrameDisplayName(frame);
   if (library && shouldMapDisplayName) {
     displayName = mapDisplayNames(frame, library);
   }
 
   return simplifyDisplayName(displayName) || l10n.getStr("anonymousFunction");
 }
 
-export function formatCopyName(frame: LocalFrame, l10n: Object): string {
+export function formatCopyName(frame: LocalFrame, l10n: typeof L10N): string {
   const displayName = formatDisplayName(frame, undefined, l10n);
   const fileName = getFilename(frame.source);
   const frameLocation = frame.location.line;
 
   return `${displayName} (${fileName}#${frameLocation})`;
 }
--- a/devtools/client/debugger/new/src/utils/pause/frames/index.js
+++ b/devtools/client/debugger/new/src/utils/pause/frames/index.js
@@ -1,9 +1,11 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 export * from "./annotateFrames";
 export * from "./collapseFrames";
 export * from "./displayName";
 export * from "./getFrameUrl";
 export * from "./getLibraryFromUrl";
--- a/devtools/client/debugger/new/src/utils/pause/scopes/getScope.js
+++ b/devtools/client/debugger/new/src/utils/pause/scopes/getScope.js
@@ -78,17 +78,17 @@ export function getScope(
       const this_ = getThisVariable(thisDesc_, key);
 
       if (this_) {
         vars.push(this_);
       }
     }
 
     if (vars && vars.length) {
-      const title = getScopeTitle(type, scope);
+      const title = getScopeTitle(type, scope) || "";
       vars.sort((a, b) => a.name.localeCompare(b.name));
       return {
         name: title,
         path: key,
         contents: vars,
         type: NODE_TYPES.BLOCK
       };
     }
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-call-stack.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-call-stack.js
@@ -12,16 +12,53 @@ function isFrameSelected(dbg, index, tit
   return elSelected && titleSelected;
 }
 
 function toggleButton(dbg) {
   const callStackBody = findElement(dbg, "callStackBody");
   return callStackBody.querySelector(".show-more");
 }
 
+// Create an HTTP server to simulate an angular app with anonymous functions
+// and return the URL.
+function createMockAngularPage() {
+  const httpServer = createTestHTTPServer();
+
+  httpServer.registerContentType("html", "text/html");
+  httpServer.registerContentType("js", "application/javascript");
+
+  const htmlFilename = "angular-mock.html"
+  httpServer.registerPathHandler(`/${htmlFilename}`, function(request, response) {
+      response.setStatusLine(request.httpVersion, 200, "OK");
+      response.write(`
+        <html>
+            <button class="pause">Click me</button>
+            <script type="text/javascript" src="angular.js"></script>
+        </html>`
+      );
+    }
+  );
+
+  // Register an angular.js file in order to create a Group with anonymous functions in
+  // the callstack panel.
+  httpServer.registerPathHandler("/angular.js", function(request, response) {
+    response.setHeader("Content-Type", "application/javascript");
+    response.write(`
+      document.querySelector("button.pause").addEventListener("click", () => {
+        (function() {
+          debugger;
+        })();
+      })
+    `);
+  });
+
+  const port = httpServer.identity.primaryPort;
+  return `http://localhost:${port}/${htmlFilename}`;
+}
+
 add_task(async function() {
   const dbg = await initDebugger("doc-script-switching.html");
 
   const found = findElement(dbg, "callStackBody");
   is(found, null, "Call stack is hidden");
 
   invokeInTab("firstCall");
   await waitForPaused(dbg);
@@ -47,8 +84,32 @@ add_task(async function() {
   button.click();
 
   button = toggleButton(dbg);
   frames = findAllElements(dbg, "frames");
   is(button.innerText, "Collapse rows", "toggle button should be collapsed");
   is(frames.length, 22, "All of the frames should be shown");
   await waitForSelectedSource(dbg, "frames.js");
 });
+
+
+add_task(async function() {
+  const url = createMockAngularPage();
+  const tab = await addTab(url);
+  info("Open debugger");
+  const toolbox = await openToolboxForTab(tab, "jsdebugger");
+  const dbg = createDebuggerContext(toolbox);
+
+  const found = findElement(dbg, "callStackBody");
+  is(found, null, "Call stack is hidden");
+
+  ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+    content.document.querySelector("button.pause").click();
+  });
+
+  await waitForPaused(dbg);
+  const $group = findElementWithSelector(dbg, ".frames .frames-group");
+  is($group.querySelector(".title").textContent,
+    "<anonymous>", "Group has expected frame title");
+  is($group.querySelector(".badge").textContent, "2", "Group has expected badge");
+  is($group.querySelector(".location").textContent, "Angular",
+    "Group has expected location");
+});