Bug 1504867 - Update to debugger release 100 r=loganfsmyth
authorDavid Walsh <dwalsh@mozilla.com>
Wed, 07 Nov 2018 12:33:53 -0600
changeset 445238 1f47cb09f1e64ffa9d530235eb8cfb0b3ae65f3c
parent 445237 e83c311e5293902be4db4ecea17cff87c633f7cf
child 445239 fb1241ca988c7bfe1ef4a8ec1f17e434d58194e4
push id109700
push userjlaster@mozilla.com
push dateThu, 08 Nov 2018 20:44:34 +0000
treeherdermozilla-inbound@1f47cb09f1e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersloganfsmyth
bugs1504867
milestone65.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 1504867 - Update to debugger release 100 r=loganfsmyth
devtools/client/debugger/new/README.mozilla
devtools/client/debugger/new/src/actions/file-search.js
devtools/client/debugger/new/src/components/App.js
devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointsContextMenu.js
devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Frame.js
devtools/client/debugger/new/src/components/SecondaryPanes/Frames/FrameMenu.js
devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
devtools/client/debugger/new/src/components/SecondaryPanes/Frames/index.js
devtools/client/debugger/new/src/components/SecondaryPanes/index.js
devtools/client/debugger/new/src/components/shared/PreviewFunction.js
devtools/client/debugger/new/src/selectors/breakpointSources.js
devtools/client/debugger/new/src/utils/editor/source-documents.js
devtools/client/debugger/new/src/utils/pause/frames/displayName.js
devtools/client/debugger/new/src/utils/source-maps.js
devtools/client/debugger/new/src/utils/source.js
devtools/client/debugger/new/src/utils/sources-tree/getDirectories.js
devtools/client/debugger/new/src/utils/wasm.js
devtools/client/debugger/new/test/mochitest/browser_dbg-asm.js
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 98
+Version 100
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-97...release-98
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-99...release-100
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.2
 - babel-preset-react @6.24.1
 - react @16.4.1
 - react-dom @16.4.1
 - webpack @3.12.0
--- a/devtools/client/debugger/new/src/actions/file-search.js
+++ b/devtools/client/debugger/new/src/actions/file-search.js
@@ -10,16 +10,17 @@ import {
   findNext,
   findPrev,
   removeOverlay,
   searchSourceForHighlight
 } from "../utils/editor";
 import { isWasm, renderWasmText } from "../utils/wasm";
 import { getMatches } from "../workers/search";
 import type { Action, FileTextSearchModifier, ThunkArgs } from "./types";
+import type { WasmSource } from "../types";
 
 import {
   getSelectedSource,
   getFileSearchModifiers,
   getFileSearchQuery,
   getFileSearchResults
 } from "../selectors";
 
@@ -103,20 +104,21 @@ export function searchContents(query: st
     const ctx = { ed: editor, cm: editor.codeMirror };
 
     if (!query) {
       clearSearch(ctx.cm, query);
       return;
     }
 
     const _modifiers = modifiers.toJS();
-    const sourceId = selectedSource.id;
-    const text = isWasm(sourceId)
-      ? renderWasmText(sourceId, selectedSource.text).join("\n")
-      : selectedSource.text;
+    let text = selectedSource.text;
+
+    if (isWasm(selectedSource.id)) {
+      text = renderWasmText(((selectedSource: any): WasmSource)).join("\n");
+    }
 
     const matches = await getMatches(query, text, _modifiers);
 
     const res = find(ctx, query, true, _modifiers);
     if (!res) {
       return;
     }
 
--- a/devtools/client/debugger/new/src/components/App.js
+++ b/devtools/client/debugger/new/src/components/App.js
@@ -1,16 +1,17 @@
 /* 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
-import PropTypes from "prop-types";
 import React, { Component } from "react";
 import { connect } from "react-redux";
+import PropTypes from "prop-types";
+
 import { features } from "../utils/prefs";
 import actions from "../actions";
 import A11yIntention from "./A11yIntention";
 import { ShortcutsModal } from "./ShortcutsModal";
 
 import {
   getSelectedSource,
   getPaneCollapse,
@@ -88,17 +89,17 @@ class App extends Component<Props, State
     this.state = {
       shortcutsModalEnabled: false,
       startPanelSize: 0,
       endPanelSize: 0
     };
   }
 
   getChildContext = () => {
-    return { shortcuts };
+    return { shortcuts, l10n: L10N };
   };
 
   componentDidMount() {
     horizontalLayoutBreakpoint.addListener(this.onLayoutChange);
     verticalLayoutBreakpoint.addListener(this.onLayoutChange);
     this.setOrientation();
 
     shortcuts.on(L10N.getStr("symbolSearch.search.key2"), (_, e) =>
@@ -311,17 +312,20 @@ class App extends Component<Props, State
           )}
           {this.renderShortcutsModal()}
         </A11yIntention>
       </div>
     );
   }
 }
 
-App.childContextTypes = { shortcuts: PropTypes.object };
+App.childContextTypes = {
+  shortcuts: PropTypes.object,
+  l10n: PropTypes.object
+};
 
 const mapStateToProps = state => ({
   selectedSource: getSelectedSource(state),
   startPanelCollapsed: getPaneCollapse(state, "start"),
   endPanelCollapsed: getPaneCollapse(state, "end"),
   activeSearch: getActiveSearch(state),
   quickOpenEnabled: getQuickOpenEnabled(state),
   orientation: getOrientation(state)
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
@@ -83,23 +83,16 @@ class SourcesTree extends Component<Prop
 
     this.state = createTree({
       projectRoot,
       debuggeeUrl,
       sources
     });
   }
 
-  componentDidMount() {
-    const { selectedSource } = this.props;
-    if (selectedSource) {
-      this.setHighlightFocusItems(selectedSource);
-    }
-  }
-
   componentWillReceiveProps(nextProps: Props) {
     const {
       projectRoot,
       debuggeeUrl,
       sources,
       shownSource,
       selectedSource
     } = this.props;
@@ -117,24 +110,29 @@ class SourcesTree extends Component<Prop
           sources: nextProps.sources,
           debuggeeUrl: nextProps.debuggeeUrl,
           projectRoot: nextProps.projectRoot
         })
       );
     }
 
     if (nextProps.shownSource && nextProps.shownSource != shownSource) {
-      return this.setHighlightFocusItems(nextProps.shownSource);
+      const listItems = getDirectories(nextProps.shownSource, sourceTree);
+      return this.setState({ listItems });
     }
 
     if (
       nextProps.selectedSource &&
       nextProps.selectedSource != selectedSource
     ) {
-      this.setHighlightFocusItems(nextProps.selectedSource);
+      const highlightItems = getDirectories(
+        nextProps.selectedSource,
+        sourceTree
+      );
+      this.setState({ highlightItems });
     }
 
     // NOTE: do not run this every time a source is clicked,
     // only when a new source is added
     if (nextProps.sources != this.props.sources) {
       this.setState(
         updateTree({
           newSources: nextProps.sources,
@@ -144,24 +142,16 @@ class SourcesTree extends Component<Prop
           uncollapsedTree,
           sourceTree,
           focusedItem: this.state.focusedItem
         })
       );
     }
   }
 
-  setHighlightFocusItems(source: ?Source) {
-    const { sourceTree, parentMap } = this.state;
-    if (source) {
-      const items = getDirectories(source, parentMap, sourceTree);
-      return this.setState({ listItems: items, highlightItems: items });
-    }
-  }
-
   focusItem = (item: TreeNode) => {
     this.setState({ focusedItem: item });
   };
 
   selectItem = (item: TreeNode) => {
     if (item.type == "source" && !Array.isArray(item.contents)) {
       this.props.selectSource(item.contents.id);
     }
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
@@ -1,147 +1,122 @@
 /* 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
 
 import React, { PureComponent } from "react";
 import { connect } from "react-redux";
-
+import { createSelector } from "reselect";
 import classnames from "classnames";
 
 import actions from "../../../actions";
 
 import showContextMenu from "./BreakpointsContextMenu";
 import { CloseButton } from "../../shared/Button";
 
 import { getLocationWithoutColumn } from "../../../utils/breakpoint";
-
+import { getSelectedLocation } from "../../../utils/source-maps";
 import { features } from "../../../utils/prefs";
 import { getEditor } from "../../../utils/editor";
-import { isGenerated } from "../../../utils/source";
 
 import type { BreakpointsMap } from "../../../reducers/types";
+import type { FormattedBreakpoint } from "../../../selectors/breakpointSources";
 
-import type {
-  Frame,
-  Source,
-  Breakpoint as BreakpointType,
-  MappedLocation
-} from "../../../types";
+import type { Frame, Source, Location } from "../../../types";
+
+type FormattedFrame = {
+  ...Frame,
+  selectedLocation: Location
+};
 
 import {
   getBreakpoints,
-  getSelectedSource,
-  getTopFrame
+  getSelectedFrame,
+  getSelectedSource
 } from "../../../selectors";
 
 type Props = {
-  breakpoint: BreakpointType,
+  breakpoint: FormattedBreakpoint,
   breakpoints: BreakpointsMap,
-  selectedSource: ?Source,
   source: Source,
-  frame: ?Frame,
+  frame: ?FormattedFrame,
   enableBreakpoint: typeof actions.enableBreakpoint,
   removeBreakpoint: typeof actions.removeBreakpoint,
   removeBreakpoints: typeof actions.removeBreakpoints,
   removeAllBreakpoints: typeof actions.removeAllBreakpoints,
   disableBreakpoint: typeof actions.disableBreakpoint,
   toggleAllBreakpoints: typeof actions.toggleAllBreakpoints,
   toggleBreakpoints: typeof actions.toggleBreakpoints,
   openConditionalPanel: typeof actions.openConditionalPanel,
   selectSpecificLocation: typeof actions.selectSpecificLocation
 };
 
-function getMappedLocation(mappedLocation: MappedLocation, selectedSource) {
-  return selectedSource && isGenerated(selectedSource)
-    ? mappedLocation.generatedLocation
-    : mappedLocation.location;
-}
-
 class Breakpoint extends PureComponent<Props> {
   onContextMenu = e => {
     showContextMenu({ ...this.props, contextMenuEvent: e });
   };
 
   onDoubleClick = () => {
     const { breakpoint, openConditionalPanel } = this.props;
     if (breakpoint.condition) {
-      openConditionalPanel(breakpoint.location.line);
+      openConditionalPanel(breakpoint.selectedLocation.line);
     }
   };
 
   selectBreakpoint = () => {
     const { breakpoint, selectSpecificLocation } = this.props;
-    selectSpecificLocation(breakpoint.location);
+    selectSpecificLocation(breakpoint.selectedLocation);
   };
 
   removeBreakpoint = event => {
     const { breakpoint, removeBreakpoint } = this.props;
 
     event.stopPropagation();
-    removeBreakpoint(breakpoint.location);
+    removeBreakpoint(breakpoint.selectedLocation);
   };
 
   handleBreakpointCheckbox = () => {
     const { breakpoint, enableBreakpoint, disableBreakpoint } = this.props;
-    if (breakpoint.loading) {
-      return;
-    }
-
     if (breakpoint.disabled) {
-      enableBreakpoint(breakpoint.location);
+      enableBreakpoint(breakpoint.selectedLocation);
     } else {
-      disableBreakpoint(breakpoint.location);
+      disableBreakpoint(breakpoint.selectedLocation);
     }
   };
 
   isCurrentlyPausedAtBreakpoint() {
-    const { frame, breakpoint, selectedSource } = this.props;
+    const { frame, breakpoint } = this.props;
     if (!frame) {
       return false;
     }
 
-    const bpId = getLocationWithoutColumn(
-      getMappedLocation(breakpoint, selectedSource)
-    );
-    const frameId = getLocationWithoutColumn(
-      getMappedLocation(frame, selectedSource)
-    );
+    const bpId = getLocationWithoutColumn(breakpoint.selectedLocation);
+    const frameId = getLocationWithoutColumn(frame.selectedLocation);
 
     return bpId == frameId;
   }
 
   getBreakpointLocation() {
-    const { breakpoint, source, selectedSource } = this.props;
-    const { column, line } = getMappedLocation(breakpoint, selectedSource);
+    const { breakpoint, source } = this.props;
+    const { column, line } = breakpoint.selectedLocation;
 
     const isWasm = source && source.isWasm;
     const columnVal = features.columnBreakpoints && column ? `:${column}` : "";
     const bpLocation = isWasm
       ? `0x${line.toString(16).toUpperCase()}`
       : `${line}${columnVal}`;
 
     return bpLocation;
   }
 
   getBreakpointText() {
-    const { selectedSource, breakpoint } = this.props;
-    const { condition } = breakpoint;
-
-    if (condition) {
-      return condition;
-    }
-
-    if (selectedSource && isGenerated(selectedSource)) {
-      return breakpoint.text;
-    }
-
-    return breakpoint.originalText;
+    const { breakpoint } = this.props;
+    return breakpoint.condition || breakpoint.text;
   }
 
   highlightText() {
     const text = this.getBreakpointText() || "";
     const editor = getEditor();
 
     if (!editor.CodeMirror) {
       return { __html: text };
@@ -187,20 +162,34 @@ class Breakpoint extends PureComponent<P
             tooltip={L10N.getStr("breakpoints.removeBreakpointTooltip")}
           />
         </div>
       </div>
     );
   }
 }
 
+const getFormattedFrame = createSelector(
+  getSelectedSource,
+  getSelectedFrame,
+  (selectedSource: Source, frame: Frame) => {
+    if (!frame) {
+      return null;
+    }
+
+    return {
+      ...frame,
+      selectedLocation: getSelectedLocation(frame, selectedSource)
+    };
+  }
+);
+
 const mapStateToProps = state => ({
   breakpoints: getBreakpoints(state),
-  frame: getTopFrame(state),
-  selectedSource: getSelectedSource(state)
+  frame: getFormattedFrame(state)
 });
 
 export default connect(
   mapStateToProps,
   {
     enableBreakpoint: actions.enableBreakpoint,
     removeBreakpoint: actions.removeBreakpoint,
     removeBreakpoints: actions.removeBreakpoints,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointsContextMenu.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointsContextMenu.js
@@ -84,17 +84,17 @@ export default function showContextMenu(
     b => b.disabled && b !== breakpoint
   );
 
   const deleteSelfItem = {
     id: "node-menu-delete-self",
     label: deleteSelfLabel,
     accesskey: deleteSelfKey,
     disabled: false,
-    click: () => removeBreakpoint(breakpoint.location)
+    click: () => removeBreakpoint(breakpoint.selectedLocation)
   };
 
   const deleteAllItem = {
     id: "node-menu-delete-all",
     label: deleteAllLabel,
     accesskey: deleteAllKey,
     disabled: false,
     click: () => removeAllBreakpoints()
@@ -108,17 +108,17 @@ export default function showContextMenu(
     click: () => removeBreakpoints(otherBreakpoints)
   };
 
   const enableSelfItem = {
     id: "node-menu-enable-self",
     label: enableSelfLabel,
     accesskey: enableSelfKey,
     disabled: false,
-    click: () => toggleDisabledBreakpoint(breakpoint.location.line)
+    click: () => toggleDisabledBreakpoint(breakpoint.selectedLocation.line)
   };
 
   const enableAllItem = {
     id: "node-menu-enable-all",
     label: enableAllLabel,
     accesskey: enableAllKey,
     disabled: false,
     click: () => toggleAllBreakpoints(false)
@@ -132,17 +132,17 @@ export default function showContextMenu(
     click: () => toggleBreakpoints(false, otherDisabledBreakpoints)
   };
 
   const disableSelfItem = {
     id: "node-menu-disable-self",
     label: disableSelfLabel,
     accesskey: disableSelfKey,
     disabled: false,
-    click: () => toggleDisabledBreakpoint(breakpoint.location.line)
+    click: () => toggleDisabledBreakpoint(breakpoint.selectedLocation.line)
   };
 
   const disableAllItem = {
     id: "node-menu-disable-all",
     label: disableAllLabel,
     accesskey: disableAllKey,
     disabled: false,
     click: () => toggleAllBreakpoints(true)
@@ -155,36 +155,36 @@ export default function showContextMenu(
     click: () => toggleBreakpoints(true, otherEnabledBreakpoints)
   };
 
   const removeConditionItem = {
     id: "node-menu-remove-condition",
     label: removeConditionLabel,
     accesskey: removeConditionKey,
     disabled: false,
-    click: () => setBreakpointCondition(breakpoint.location)
+    click: () => setBreakpointCondition(breakpoint.selectedLocation)
   };
 
   const addConditionItem = {
     id: "node-menu-add-condition",
     label: addConditionLabel,
     accesskey: addConditionKey,
     click: () => {
-      selectSpecificLocation(breakpoint.location);
-      openConditionalPanel(breakpoint.location.line);
+      selectSpecificLocation(breakpoint.selectedLocation);
+      openConditionalPanel(breakpoint.selectedLocation.line);
     }
   };
 
   const editConditionItem = {
     id: "node-menu-edit-condition",
     label: editConditionLabel,
     accesskey: editConditionKey,
     click: () => {
-      selectSpecificLocation(breakpoint.location);
-      openConditionalPanel(breakpoint.location.line);
+      selectSpecificLocation(breakpoint.selectedLocation);
+      openConditionalPanel(breakpoint.selectedLocation.line);
     }
   };
 
   const hideEnableSelfItem = !breakpoint.disabled;
   const hideEnableAllItem = disabledBreakpoints.size === 0;
   const hideEnableOthersItem = otherDisabledBreakpoints.size === 0;
   const hideDisableAllItem = enabledBreakpoints.size === 0;
   const hideDisableOthersItem = otherEnabledBreakpoints.size === 0;
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
@@ -99,17 +99,17 @@ class Breakpoints extends Component<Prop
               {getTruncatedFileName(source)}
               {path && <span>{`../${path}/..`}</span>}
             </div>
           </div>,
           ...breakpoints.map(breakpoint => (
             <Breakpoint
               breakpoint={breakpoint}
               source={source}
-              key={makeLocationId(breakpoint.location)}
+              key={makeLocationId(breakpoint.selectedLocation)}
             />
           ))
         ];
       })
     ];
   }
 
   render() {
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Frame.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Frame.js
@@ -1,31 +1,34 @@
 /* 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
 import React, { Component } from "react";
+import PropTypes from "prop-types";
+
 import classNames from "classnames";
 import Svg from "../../shared/Svg";
 
 import { formatDisplayName } from "../../../utils/pause/frames";
 import { getFilename, getFileURL } from "../../../utils/source";
 import FrameMenu from "./FrameMenu";
 
 import type { Frame } from "../../../types";
 import type { LocalFrame } from "./types";
 
 type FrameTitleProps = {
   frame: Frame,
-  options: Object
+  options: Object,
+  l10n: Object
 };
 
-function FrameTitle({ frame, options = {} }: FrameTitleProps) {
-  const displayName = formatDisplayName(frame, options);
+function FrameTitle({ frame, options = {}, l10n }: FrameTitleProps) {
+  const displayName = formatDisplayName(frame, options, l10n);
   return <div className="title">{displayName}</div>;
 }
 
 type FrameLocationProps = { frame: LocalFrame, displayFullUrl: boolean };
 
 function FrameLocation({ frame, displayFullUrl = false }: FrameLocationProps) {
   if (!frame.source) {
     return null;
@@ -56,47 +59,49 @@ type FrameComponentProps = {
   copyStackTrace: Function,
   toggleFrameworkGrouping: Function,
   selectFrame: Function,
   frameworkGroupingOn: boolean,
   hideLocation: boolean,
   shouldMapDisplayName: boolean,
   toggleBlackBox: Function,
   displayFullUrl: boolean,
-  getFrameTitle?: string => string
+  getFrameTitle?: string => string,
+  disableContextMenu: boolean
 };
 
 export default class FrameComponent extends Component<FrameComponentProps> {
   static defaultProps = {
     hideLocation: false,
-    shouldMapDisplayName: true
+    shouldMapDisplayName: true,
+    disableContextMenu: false
   };
 
-  onContextMenu(event: SyntheticKeyboardEvent<HTMLElement>) {
+  onContextMenu(event: SyntheticMouseEvent<HTMLElement>) {
     const {
       frame,
       copyStackTrace,
       toggleFrameworkGrouping,
       toggleBlackBox,
       frameworkGroupingOn
     } = this.props;
     FrameMenu(
       frame,
       frameworkGroupingOn,
       { copyStackTrace, toggleFrameworkGrouping, toggleBlackBox },
       event
     );
   }
 
   onMouseDown(
-    e: SyntheticKeyboardEvent<HTMLElement>,
+    e: SyntheticMouseEvent<HTMLElement>,
     frame: Frame,
     selectedFrame: Frame
   ) {
-    if (e.which == 3) {
+    if (e.button !== 0) {
       return;
     }
     this.props.selectFrame(frame);
   }
 
   onKeyUp(
     event: SyntheticKeyboardEvent<HTMLElement>,
     frame: Frame,
@@ -110,18 +115,20 @@ export default class FrameComponent exte
 
   render() {
     const {
       frame,
       selectedFrame,
       hideLocation,
       shouldMapDisplayName,
       displayFullUrl,
-      getFrameTitle
+      getFrameTitle,
+      disableContextMenu
     } = this.props;
+    const { l10n } = this.context;
 
     const className = classNames("frame", {
       selected: selectedFrame && selectedFrame.id === frame.id
     });
 
     const title = getFrameTitle
       ? getFrameTitle(
           `${getFileURL(frame.source, false)}:${frame.location.line}`
@@ -129,22 +136,27 @@ export default class FrameComponent exte
       : undefined;
 
     return (
       <li
         key={frame.id}
         className={className}
         onMouseDown={e => this.onMouseDown(e, frame, selectedFrame)}
         onKeyUp={e => this.onKeyUp(e, frame, selectedFrame)}
-        onContextMenu={e => this.onContextMenu(e)}
+        onContextMenu={disableContextMenu ? null : e => this.onContextMenu(e)}
         tabIndex={0}
         title={title}
       >
-        <FrameTitle frame={frame} options={{ shouldMapDisplayName }} />
+        <FrameTitle
+          frame={frame}
+          options={{ shouldMapDisplayName }}
+          l10n={l10n}
+        />
         {!hideLocation && (
           <FrameLocation frame={frame} displayFullUrl={displayFullUrl} />
         )}
       </li>
     );
   }
 }
 
 FrameComponent.displayName = "Frame";
+FrameComponent.contextTypes = { l10n: PropTypes.object };
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/FrameMenu.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/FrameMenu.js
@@ -55,17 +55,17 @@ function blackBoxSource(source, toggleBl
 
   return formatMenuElement(toggleBlackBoxString, () => toggleBlackBox(source));
 }
 
 export default function FrameMenu(
   frame: LocalFrame,
   frameworkGroupingOn: boolean,
   callbacks: Object,
-  event: SyntheticKeyboardEvent<HTMLElement>
+  event: SyntheticMouseEvent<HTMLElement>
 ) {
   event.stopPropagation();
   event.preventDefault();
 
   const menuOptions = [];
 
   const source = frame.source;
 
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
@@ -40,32 +40,33 @@ type Props = {
   group: LocalFrame[],
   selectedFrame: LocalFrame,
   selectFrame: Function,
   toggleFrameworkGrouping: Function,
   copyStackTrace: Function,
   toggleBlackBox: Function,
   frameworkGroupingOn: boolean,
   displayFullUrl: boolean,
-  getFrameTitle?: string => string
+  getFrameTitle?: string => string,
+  disableContextMenu: boolean
 };
 
 type State = {
   expanded: boolean
 };
 
 export default class Group extends Component<Props, State> {
   toggleFrames: Function;
 
   constructor(...args: any[]) {
     super(...args);
     this.state = { expanded: false };
   }
 
-  onContextMenu(event: SyntheticKeyboardEvent<HTMLElement>) {
+  onContextMenu(event: SyntheticMouseEvent<HTMLElement>) {
     const {
       group,
       copyStackTrace,
       toggleFrameworkGrouping,
       toggleBlackBox,
       frameworkGroupingOn
     } = this.props;
     const frame = group[0];
@@ -86,17 +87,18 @@ export default class Group extends Compo
       group,
       selectFrame,
       selectedFrame,
       toggleFrameworkGrouping,
       frameworkGroupingOn,
       toggleBlackBox,
       copyStackTrace,
       displayFullUrl,
-      getFrameTitle
+      getFrameTitle,
+      disableContextMenu
     } = this.props;
 
     const { expanded } = this.state;
     if (!expanded) {
       return null;
     }
 
     return (
@@ -110,16 +112,17 @@ export default class Group extends Compo
             key={frame.id}
             selectedFrame={selectedFrame}
             selectFrame={selectFrame}
             shouldMapDisplayName={false}
             toggleBlackBox={toggleBlackBox}
             toggleFrameworkGrouping={toggleFrameworkGrouping}
             displayFullUrl={displayFullUrl}
             getFrameTitle={getFrameTitle}
+            disableContextMenu={disableContextMenu}
           />
         ))}
       </div>
     );
   }
 
   renderDescription() {
     const frame = this.props.group[0];
@@ -137,20 +140,21 @@ export default class Group extends Compo
         </div>
         <FrameLocation frame={frame} />
       </li>
     );
   }
 
   render() {
     const { expanded } = this.state;
+    const { disableContextMenu } = this.props;
     return (
       <div
         className={classNames("frames-group", { expanded })}
-        onContextMenu={e => this.onContextMenu(e)}
+        onContextMenu={disableContextMenu ? null : e => this.onContextMenu(e)}
       >
         {this.renderDescription()}
         {this.renderFrames()}
       </div>
     );
   }
 }
 
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/index.js
@@ -1,16 +1,17 @@
 /* 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
 
 import React, { Component } from "react";
 import { connect } from "react-redux";
+import PropTypes from "prop-types";
 
 import type { Frame, Why } from "../../../types";
 
 import FrameComponent from "./Frame";
 import Group from "./Group";
 
 import renderWhyPaused from "./WhyPaused";
 
@@ -36,16 +37,17 @@ type Props = {
   frames: Array<Frame>,
   frameworkGroupingOn: boolean,
   selectedFrame: Object,
   why: Why,
   selectFrame: Function,
   toggleBlackBox: Function,
   toggleFrameworkGrouping: Function,
   disableFrameTruncate: boolean,
+  disableContextMenu: boolean,
   displayFullUrl: boolean,
   getFrameTitle?: string => string
 };
 
 type State = {
   showAllFrames: boolean
 };
 
@@ -96,33 +98,35 @@ class Frames extends Component<Props, St
       ? frames.length
       : NUM_FRAMES_SHOWN;
 
     return frames.slice(0, numFramesToShow);
   }
 
   copyStackTrace = () => {
     const { frames } = this.props;
-    const framesToCopy = frames.map(f => formatCopyName(f)).join("\n");
+    const { l10n } = this.context;
+    const framesToCopy = frames.map(f => formatCopyName(f, l10n)).join("\n");
     copyToTheClipboard(framesToCopy);
   };
 
   toggleFrameworkGrouping = () => {
     const { toggleFrameworkGrouping, frameworkGroupingOn } = this.props;
     toggleFrameworkGrouping(!frameworkGroupingOn);
   };
 
   renderFrames(frames: LocalFrame[]) {
     const {
       selectFrame,
       selectedFrame,
       toggleBlackBox,
       frameworkGroupingOn,
       displayFullUrl,
-      getFrameTitle
+      getFrameTitle,
+      disableContextMenu
     } = this.props;
 
     const framesOrGroups = this.truncateFrames(this.collapseFrames(frames));
     type FrameOrGroup = LocalFrame | LocalFrame[];
 
     return (
       <ul>
         {framesOrGroups.map(
@@ -134,40 +138,43 @@ class Frames extends Component<Props, St
                 copyStackTrace={this.copyStackTrace}
                 frameworkGroupingOn={frameworkGroupingOn}
                 selectFrame={selectFrame}
                 selectedFrame={selectedFrame}
                 toggleBlackBox={toggleBlackBox}
                 key={String(frameOrGroup.id)}
                 displayFullUrl={displayFullUrl}
                 getFrameTitle={getFrameTitle}
+                disableContextMenu={disableContextMenu}
               />
             ) : (
               <Group
                 group={frameOrGroup}
                 toggleFrameworkGrouping={this.toggleFrameworkGrouping}
                 copyStackTrace={this.copyStackTrace}
                 frameworkGroupingOn={frameworkGroupingOn}
                 selectFrame={selectFrame}
                 selectedFrame={selectedFrame}
                 toggleBlackBox={toggleBlackBox}
                 key={frameOrGroup[0].id}
                 displayFullUrl={displayFullUrl}
                 getFrameTitle={getFrameTitle}
+                disableContextMenu={disableContextMenu}
               />
             )
         )}
       </ul>
     );
   }
 
   renderToggleButton(frames: LocalFrame[]) {
+    const { l10n } = this.context;
     const buttonMessage = this.state.showAllFrames
-      ? L10N.getStr("callStack.collapse")
-      : L10N.getStr("callStack.expand");
+      ? l10n.getStr("callStack.collapse")
+      : l10n.getStr("callStack.expand");
 
     frames = this.collapseFrames(frames);
     if (frames.length <= NUM_FRAMES_SHOWN) {
       return null;
     }
 
     return (
       <div className="show-more-container">
@@ -196,30 +203,33 @@ class Frames extends Component<Props, St
         {this.renderFrames(frames)}
         {renderWhyPaused(why)}
         {disableFrameTruncate ? null : this.renderToggleButton(frames)}
       </div>
     );
   }
 }
 
+Frames.contextTypes = { l10n: PropTypes.object };
+
 const mapStateToProps = state => ({
   frames: getCallStackFrames(state),
   why: getPauseReason(state),
   frameworkGroupingOn: getFrameworkGroupingState(state),
   selectedFrame: getSelectedFrame(state),
   pause: getIsPaused(state)
 });
 
 export default connect(
   mapStateToProps,
   {
     selectFrame: actions.selectFrame,
     toggleBlackBox: actions.toggleBlackBox,
     toggleFrameworkGrouping: actions.toggleFrameworkGrouping,
     disableFrameTruncate: false,
+    disableContextMenu: false,
     displayFullUrl: false
   }
 )(Frames);
 
 // Export the non-connected component in order to use it outside of the debugger
 // panel (e.g. console, netmonitor, …).
 export { Frames };
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/index.js
@@ -1,15 +1,14 @@
 /* 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
 
-import PropTypes from "prop-types";
 import React, { Component } from "react";
 import { connect } from "react-redux";
 import { List } from "immutable";
 
 import actions from "../../actions";
 import {
   getTopFrame,
   getBreakpoints,
@@ -430,20 +429,16 @@ class SecondaryPanes extends Component<P
             : this.renderVerticalLayout()}
         </div>
         {this.renderUtilsBar()}
       </div>
     );
   }
 }
 
-SecondaryPanes.contextTypes = {
-  shortcuts: PropTypes.object
-};
-
 const mapStateToProps = state => ({
   expressions: getExpressions(state),
   extra: getExtra(state),
   hasFrames: !!getTopFrame(state),
   breakpoints: getBreakpoints(state),
   breakpointsDisabled: getBreakpointsDisabled(state),
   breakpointsLoading: getBreakpointsLoading(state),
   isWaitingOnBreak: getIsWaitingOnBreak(state),
--- a/devtools/client/debugger/new/src/components/shared/PreviewFunction.js
+++ b/devtools/client/debugger/new/src/components/shared/PreviewFunction.js
@@ -1,15 +1,16 @@
 /* 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
 
 import React, { Component } from "react";
+import PropTypes from "prop-types";
 
 import { times, zip, flatten } from "lodash";
 
 import { formatDisplayName } from "../../utils/pause/frames";
 
 import "./PreviewFunction.css";
 
 type FunctionType = {
@@ -18,17 +19,18 @@ type FunctionType = {
   userDisplayName?: string,
   parameterNames?: string[]
 };
 
 type Props = { func: FunctionType };
 
 export default class PreviewFunction extends Component<Props> {
   renderFunctionName(func: FunctionType) {
-    const name = formatDisplayName(func);
+    const { l10n } = this.context;
+    const name = formatDisplayName(func, undefined, l10n);
     return <span className="function-name">{name}</span>;
   }
 
   renderParams(func: FunctionType) {
     const { parameterNames = [] } = func;
     const params = parameterNames.filter(i => i).map(param => (
       <span className="param" key={param}>
         {param}
@@ -51,8 +53,10 @@ export default class PreviewFunction ext
         {this.renderFunctionName(this.props.func)}
         <span className="paren">(</span>
         {this.renderParams(this.props.func)}
         <span className="paren">)</span>
       </span>
     );
   }
 }
+
+PreviewFunction.contextTypes = { l10n: PropTypes.object };
--- a/devtools/client/debugger/new/src/selectors/breakpointSources.js
+++ b/devtools/client/debugger/new/src/selectors/breakpointSources.js
@@ -1,65 +1,98 @@
 /* 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
 
 import { sortBy, uniq } from "lodash";
 import { createSelector } from "reselect";
-import { getSources, getBreakpoints } from "../selectors";
-import { getFilename } from "../utils/source";
+import { getSources, getBreakpoints, getSelectedSource } from "../selectors";
+import { isGenerated, getFilename } from "../utils/source";
+import { getSelectedLocation } from "../utils/source-maps";
 
-import type { Source, Breakpoint } from "../types";
+import type { Source, Breakpoint, Location } from "../types";
 import type { SourcesMap, BreakpointsMap } from "../reducers/types";
 
 export type BreakpointSources = Array<{
   source: Source,
-  breakpoints: Breakpoint[]
+  breakpoints: FormattedBreakpoint[]
 }>;
 
+export type FormattedBreakpoint = {|
+  condition: ?string,
+  disabled: boolean,
+  text: string,
+  selectedLocation: Location
+|};
+
+function formatBreakpoint(
+  breakpoint: Breakpoint,
+  selectedSource: Source
+): FormattedBreakpoint {
+  const { condition, disabled } = breakpoint;
+
+  return {
+    condition,
+    disabled,
+    text:
+      selectedSource && isGenerated(selectedSource)
+        ? breakpoint.text
+        : breakpoint.originalText,
+    selectedLocation: getSelectedLocation(breakpoint, selectedSource)
+  };
+}
+
 function getBreakpointsForSource(
   source: Source,
+  selectedSource: Source,
   breakpoints: BreakpointsMap
 ): Breakpoint[] {
   const bpList = breakpoints.valueSeq();
-
   return bpList
+    .map(bp => formatBreakpoint(bp, selectedSource))
     .filter(
       bp =>
-        bp.location.sourceId == source.id &&
+        bp.selectedLocation.sourceId == source.id &&
         !bp.hidden &&
+        !bp.loading &&
         (bp.text || bp.originalText || bp.condition || bp.disabled)
     )
-    .sortBy(bp => bp.location.line)
+    .sortBy(bp => bp.selectedLocation.line)
     .toJS();
 }
 
 function findBreakpointSources(
   sources: SourcesMap,
+  selectedSource: Source,
   breakpoints: BreakpointsMap
 ): Source[] {
   const sourceIds: string[] = uniq(
     breakpoints
       .valueSeq()
-      .map(bp => bp.location.sourceId)
+      .map(bp => getSelectedLocation(bp, selectedSource).sourceId)
       .toJS()
   );
 
   const breakpointSources = sourceIds
     .map(id => sources[id])
     .filter(source => source && !source.isBlackBoxed);
 
   return sortBy(breakpointSources, (source: Source) => getFilename(source));
 }
 
 export const getBreakpointSources = createSelector(
   getBreakpoints,
   getSources,
-  (breakpoints: BreakpointsMap, sources: SourcesMap) =>
-    findBreakpointSources(sources, breakpoints)
+  getSelectedSource,
+  (breakpoints: BreakpointsMap, sources: SourcesMap, selectedSource: Source) =>
+    findBreakpointSources(sources, selectedSource, breakpoints)
       .map(source => ({
         source,
-        breakpoints: getBreakpointsForSource(source, breakpoints)
+        breakpoints: getBreakpointsForSource(
+          source,
+          selectedSource,
+          breakpoints
+        )
       }))
       .filter(({ breakpoints: bpSources }) => bpSources.length > 0)
 );
--- a/devtools/client/debugger/new/src/utils/editor/source-documents.js
+++ b/devtools/client/debugger/new/src/utils/editor/source-documents.js
@@ -98,24 +98,23 @@ export function showErrorMessage(editor:
   const doc = editor.createDocument();
   editor.replaceDocument(doc);
   editor.setText(error);
   editor.setMode({ name: "text" });
   resetLineNumberFormat(editor);
 }
 
 function setEditorText(editor: Object, source: Source) {
-  const { text, id: sourceId } = source;
   if (source.isWasm) {
-    const wasmLines = renderWasmText(sourceId, (text: any));
+    const wasmLines = renderWasmText(source);
     // cm will try to split into lines anyway, saving memory
     const wasmText = { split: () => wasmLines, match: () => false };
     editor.setText(wasmText);
   } else {
-    editor.setText(text);
+    editor.setText(source.text);
   }
 }
 
 function setMode(editor, source, symbols) {
   const mode = getMode(source, symbols);
   const currentMode = editor.codeMirror.getOption("mode");
   if (!currentMode || currentMode.name != mode.name) {
     editor.setMode(mode);
--- a/devtools/client/debugger/new/src/utils/pause/frames/displayName.js
+++ b/devtools/client/debugger/new/src/utils/pause/frames/displayName.js
@@ -78,26 +78,27 @@ function getFrameDisplayName(frame: Loca
   return originalDisplayName || userDisplayName || displayName || name;
 }
 
 type formatDisplayNameParams = {
   shouldMapDisplayName: boolean
 };
 export function formatDisplayName(
   frame: LocalFrame,
-  { shouldMapDisplayName = true }: formatDisplayNameParams = {}
+  { shouldMapDisplayName = true }: formatDisplayNameParams = {},
+  l10n: Object
 ): string {
   const { library } = frame;
   let displayName = getFrameDisplayName(frame);
   if (library && shouldMapDisplayName) {
     displayName = mapDisplayNames(frame, library);
   }
 
-  return simplifyDisplayName(displayName) || L10N.getStr("anonymousFunction");
+  return simplifyDisplayName(displayName) || l10n.getStr("anonymousFunction");
 }
 
-export function formatCopyName(frame: LocalFrame): string {
-  const displayName = formatDisplayName(frame);
+export function formatCopyName(frame: LocalFrame, l10n: Object): 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/source-maps.js
+++ b/devtools/client/debugger/new/src/utils/source-maps.js
@@ -1,17 +1,19 @@
 /* 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
 
 import { isOriginalId } from "devtools-source-map";
 import { getSource } from "../selectors";
-import type { Location, Source } from "../types";
+
+import type { Location, MappedLocation, Source } from "../types";
+import { isGenerated } from "../utils/source";
 
 export async function getGeneratedLocation(
   state: Object,
   source: Source,
   location: Location,
   sourceMaps: Object
 ): Promise<Location> {
   if (!isOriginalId(location.sourceId)) {
@@ -48,8 +50,17 @@ export async function getMappedLocation(
   }
 
   if (isOriginalId(location.sourceId)) {
     return getGeneratedLocation(state, source, location, sourceMaps);
   }
 
   return sourceMaps.getOriginalLocation(location, source);
 }
+
+export function getSelectedLocation(
+  mappedLocation: MappedLocation,
+  selectedSource: ?Source
+) {
+  return selectedSource && isGenerated(selectedSource)
+    ? mappedLocation.generatedLocation
+    : mappedLocation.location;
+}
--- a/devtools/client/debugger/new/src/utils/source.js
+++ b/devtools/client/debugger/new/src/utils/source.js
@@ -10,21 +10,23 @@
  */
 
 import { isOriginalId, isGeneratedId } from "devtools-source-map";
 import { getUnicodeUrl } from "devtools-modules";
 
 import { endTruncateStr } from "./utils";
 import { truncateMiddleText } from "../utils/text";
 import { parse as parseURL } from "../utils/url";
+import { renderWasmText } from "./wasm";
+import { toEditorPosition } from "./editor";
 export { isMinified } from "./isMinified";
 import { getURL, getFileExtension } from "./sources-tree";
 import { prefs } from "./prefs";
 
-import type { Source, Location } from "../types";
+import type { Source, Location, JsSource } from "../types";
 import type { SourceMetaDataType } from "../reducers/ast";
 import type { SymbolDeclarations } from "../workers/parser";
 
 type transformUrlCallback = string => string;
 
 export const sourceTypes = {
   coffee: "coffeescript",
   js: "javascript",
@@ -377,24 +379,31 @@ export function isLoaded(source: Source)
   return source.loadedState === "loaded";
 }
 
 export function isLoading(source: Source) {
   return source.loadedState === "loading";
 }
 
 export function getTextAtPosition(source: ?Source, location: Location) {
-  if (!source || source.isWasm || !source.text) {
+  if (!source || !source.text) {
     return "";
   }
 
   const line = location.line;
   const column = location.column || 0;
 
-  const lineText = source.text.split("\n")[line - 1];
+  if (source.isWasm) {
+    const { line: editorLine } = toEditorPosition(location);
+    const lines = renderWasmText(source);
+    return lines[editorLine];
+  }
+
+  const text = ((source: any): JsSource).text || "";
+  const lineText = text.split("\n")[line - 1];
   if (!lineText) {
     return "";
   }
 
   return lineText.slice(column, column + 100).trim();
 }
 
 export function getSourceClassnames(
--- a/devtools/client/debugger/new/src/utils/sources-tree/getDirectories.js
+++ b/devtools/client/debugger/new/src/utils/sources-tree/getDirectories.js
@@ -1,16 +1,16 @@
 /* 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
 
-// import { createParentMap } from "./utils";
-import type { TreeNode, TreeDirectory, ParentMap } from "./types";
+import { createParentMap } from "./utils";
+import type { TreeNode, TreeDirectory } from "./types";
 import type { Source } from "../../types";
 
 function _traverse(subtree: TreeNode, source: Source) {
   if (subtree.type === "source") {
     if (subtree.contents.id === source.id) {
       return subtree;
     }
 
@@ -20,38 +20,31 @@ function _traverse(subtree: TreeNode, so
   const matches = subtree.contents.map(child => _traverse(child, source));
   return matches && matches.filter(Boolean)[0];
 }
 
 function findSourceItem(sourceTree: TreeDirectory, source: Source): ?TreeNode {
   return _traverse(sourceTree, source);
 }
 
-function getAncestors(
-  sourceTree: TreeDirectory,
-  parentMap: ParentMap,
-  item: ?TreeNode
-) {
+function getAncestors(sourceTree: TreeDirectory, item: ?TreeNode) {
   if (!item) {
     return null;
   }
 
+  const parentMap = createParentMap(sourceTree);
   const directories = [];
 
   directories.push(item);
   while (true) {
     item = parentMap.get(item);
     if (!item) {
       return directories;
     }
     directories.push(item);
   }
 }
 
-export function getDirectories(
-  source: Source,
-  parentMap: ParentMap,
-  sourceTree: TreeDirectory
-) {
+export function getDirectories(source: Source, sourceTree: TreeDirectory) {
   const item = findSourceItem(sourceTree, source);
-  const ancestors = getAncestors(sourceTree, parentMap, item);
+  const ancestors = getAncestors(sourceTree, item);
   return ancestors || [sourceTree];
 }
--- a/devtools/client/debugger/new/src/utils/wasm.js
+++ b/devtools/client/debugger/new/src/utils/wasm.js
@@ -2,16 +2,17 @@
  * 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 */
 
 import { BinaryReader } from "wasmparser/dist/WasmParser";
 import { WasmDisassembler, NameSectionReader } from "wasmparser/dist/WasmDis";
 
+import type { WasmSource } from "../types";
 type WasmState = {
   lines: Array<number>,
   offsets: Array<number>
 };
 
 var wasmStates: { [string]: WasmState } = (Object.create(null): any);
 
 function maybeWasmSectionNameResolver(data: Uint8Array) {
@@ -132,22 +133,34 @@ export function wasmOffsetToLine(sourceI
 /**
  * @memberof utils/wasm
  * @static
  */
 export function clearWasmStates() {
   wasmStates = (Object.create(null): any);
 }
 
-export function renderWasmText(sourceId: string, { binary }: any) {
+const wasmLines: WeakMap<WasmSource, string[]> = new WeakMap();
+export function renderWasmText(source: WasmSource): string[] {
+  if (wasmLines.has(source)) {
+    return wasmLines.get(source) || [];
+  }
+
+  if (!source.text) {
+    return [];
+  }
+
   // binary does not survive as Uint8Array, converting from string
+  const { binary } = source.text;
   const data = new Uint8Array(binary.length);
   for (let i = 0; i < data.length; i++) {
     data[i] = binary.charCodeAt(i);
   }
-  const { lines } = getWasmText(sourceId, data);
+  const { lines } = getWasmText(source.id, data);
   const MAX_LINES = 1000000;
   if (lines.length > MAX_LINES) {
     lines.splice(MAX_LINES, lines.length - MAX_LINES);
     lines.push(";; .... text is truncated due to the size");
   }
+
+  wasmLines.set(source, lines);
   return lines;
 }
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-asm.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-asm.js
@@ -5,17 +5,17 @@ add_task(async function() {
   // After reload() we are getting getSources notifiction for old sources,
   // using the debugger statement to really stop are reloaded page.
   await waitForPaused(dbg);
   await resume(dbg);
 
   await waitForSources(dbg, "doc-asm.html", "asm.js");
 
   // Make sure sources appear.
-  is(findAllElements(dbg, "sourceNodes").length, 4);
+  is(findAllElements(dbg, "sourceNodes").length, 2);
 
   await selectSource(dbg, "asm.js");
 
   await addBreakpoint(dbg, "asm.js", 7);
   invokeInTab("runAsm");
 
   await waitForPaused(dbg);
   assertPausedLocation(dbg, "asm.js", 7);