Merge mozilla-inbound to mozilla-central. a=merge
authorCosmin Sabou <csabou@mozilla.com>
Thu, 13 Dec 2018 05:56:48 +0200
changeset 450371 8d6e7614d2f0a37511a98a399cd72df97c9bc653
parent 450370 1e7843022450c852030788360f3a314de6678dac (current diff)
parent 450314 008fb8183cac0505185e73348609be9065a17e53 (diff)
child 450372 3ecc407c0cc8705ba7b60fbbe8964e794c0a5651
child 450411 273b6d2127671a017d770ed0c54483fbd90467d7
push id110479
push usercsabou@mozilla.com
push dateThu, 13 Dec 2018 04:02:11 +0000
treeherdermozilla-inbound@3ecc407c0cc8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
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
Merge mozilla-inbound to mozilla-central. a=merge
js/src/tests/ecma_6/Date/parse-from-tostring-methods.js
toolkit/themes/osx/mozapps/extensions/toolbarbutton-dropmarker.png
--- 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 106
+Version 107
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-105...release-106
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-106...release-107
 
 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/ast/setPausePoints.js
+++ b/devtools/client/debugger/new/src/actions/ast/setPausePoints.js
@@ -1,20 +1,22 @@
 /* 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 { getSourceFromId } from "../../selectors";
 import * as parser from "../../workers/parser";
 import { isGenerated } from "../../utils/source";
 import { mapPausePoints } from "../../utils/pause/pausePoints";
 import { features } from "../../utils/prefs";
 
-import type { SourceId } from "../types";
-import type { ThunkArgs, Action } from "./types";
+import type { SourceId } from "../../types";
+import type { ThunkArgs, Action } from "../types";
 
 function compressPausePoints(pausePoints) {
   const compressed = {};
   for (const line in pausePoints) {
     compressed[line] = {};
     for (const col in pausePoints[line]) {
       const { types } = pausePoints[line][col];
       compressed[line][col] = (types.break ? 1 : 0) | (types.step ? 2 : 0);
--- a/devtools/client/debugger/new/src/actions/breakpoints/addBreakpoint.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/addBreakpoint.js
@@ -1,31 +1,41 @@
 /* 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 {
   locationMoved,
   breakpointExists,
   assertBreakpoint,
   createBreakpoint,
   getASTLocation,
   assertLocation
 } from "../../utils/breakpoint";
 import { PROMISE } from "../utils/middleware/promise";
 import { getSource, getSymbols, getBreakpoint } from "../../selectors";
 import { getGeneratedLocation } from "../../utils/source-maps";
 import { getTextAtPosition } from "../../utils/source";
 import { recordEvent } from "../../utils/telemetry";
 
+import type { SourceLocation } from "../../types";
+import type { ThunkArgs } from "../types";
+import type { addBreakpointOptions } from "./";
+
 async function addBreakpointPromise(getState, client, sourceMaps, breakpoint) {
   const state = getState();
   const source = getSource(state, breakpoint.location.sourceId);
 
+  if (!source) {
+    throw new Error(`Unable to find source: ${breakpoint.location.sourceId}`);
+  }
+
   const location = {
     ...breakpoint.location,
     sourceId: source.id,
     sourceUrl: source.url
   };
 
   const generatedLocation = await getGeneratedLocation(
     state,
--- a/devtools/client/debugger/new/src/actions/breakpoints/index.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/index.js
@@ -31,17 +31,17 @@ import { isEmptyLineInSource } from "../
 
 // this will need to be changed so that addCLientBreakpoint is removed
 
 import type { ThunkArgs, Action } from "../types";
 import type { Breakpoint, SourceLocation, XHRBreakpoint } from "../../types";
 
 import { recordEvent } from "../../utils/telemetry";
 
-type addBreakpointOptions = {
+export type addBreakpointOptions = {
   condition?: string,
   hidden?: boolean
 };
 
 /**
  * Remove a single breakpoint
  *
  * @memberof actions/breakpoints
@@ -259,22 +259,22 @@ export function setBreakpointCondition(
       ({
         type: "SET_BREAKPOINT_CONDITION",
         breakpoint: newBreakpoint
       }: Action)
     );
   };
 }
 
-export function toggleBreakpoint(line: ?number, column?: number) {
+export function toggleBreakpoint(line: number, column?: number) {
   return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const state = getState();
     const selectedSource = getSelectedSource(state);
 
-    if (!line || !selectedSource) {
+    if (!selectedSource) {
       return;
     }
 
     const bp = getBreakpointAtLocation(state, { line, column });
     const isEmptyLine = isEmptyLineInSource(state, line, selectedSource.id);
 
     if ((!bp && isEmptyLine) || (bp && bp.loading)) {
       return;
@@ -302,17 +302,17 @@ export function toggleBreakpoint(line: ?
   };
 }
 
 export function toggleBreakpointsAtLine(line: number, column?: number) {
   return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const state = getState();
     const selectedSource = getSelectedSource(state);
 
-    if (!line || !selectedSource) {
+    if (!selectedSource) {
       return;
     }
 
     const bps = getBreakpointsAtLine(state, line);
     const isEmptyLine = isEmptyLineInSource(state, line, selectedSource.id);
 
     if (bps.length === 0 && !isEmptyLine) {
       return dispatch(
@@ -324,21 +324,21 @@ export function toggleBreakpointsAtLine(
         })
       );
     }
 
     return Promise.all(bps.map(bp => dispatch(removeBreakpoint(bp.location))));
   };
 }
 
-export function addOrToggleDisabledBreakpoint(line: ?number, column?: number) {
+export function addOrToggleDisabledBreakpoint(line: number, column?: number) {
   return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const selectedSource = getSelectedSource(getState());
 
-    if (!line || !selectedSource) {
+    if (!selectedSource) {
       return;
     }
 
     const bp = getBreakpointAtLocation(getState(), { line, column });
 
     if (bp && bp.loading) {
       return;
     }
--- a/devtools/client/debugger/new/src/actions/ui.js
+++ b/devtools/client/debugger/new/src/actions/ui.js
@@ -12,16 +12,17 @@ import {
   getFileSearchQuery,
   getProjectDirectoryRoot
 } from "../selectors";
 import { selectSource } from "../actions/sources/select";
 import type { ThunkArgs, panelPositionType } from "./types";
 import { getEditor, getLocationsInViewport } from "../utils/editor";
 import { searchContents } from "./file-search";
 
+import type { SourceLocation } from "../types";
 import type {
   ActiveSearchType,
   OrientationType,
   SelectedPrimaryPaneTabType
 } from "../reducers/ui";
 
 export function setContextMenu(type: string, event: any) {
   return ({ dispatch }: ThunkArgs) => {
@@ -150,24 +151,24 @@ export function flashLineRange(location:
  * @static
  */
 export function clearHighlightLineRange() {
   return {
     type: "CLEAR_HIGHLIGHT_LINES"
   };
 }
 
-export function openConditionalPanel(line: ?number) {
-  if (!line) {
+export function openConditionalPanel(location: ?SourceLocation) {
+  if (!location) {
     return;
   }
 
   return {
     type: "OPEN_CONDITIONAL_PANEL",
-    line
+    location
   };
 }
 
 export function closeConditionalPanel() {
   return {
     type: "CLOSE_CONDITIONAL_PANEL"
   };
 }
--- a/devtools/client/debugger/new/src/components/Editor/ConditionalPanel.js
+++ b/devtools/client/debugger/new/src/components/Editor/ConditionalPanel.js
@@ -6,26 +6,26 @@
 import React, { PureComponent } from "react";
 import ReactDOM from "react-dom";
 import { connect } from "react-redux";
 import "./ConditionalPanel.css";
 import { toEditorLine } from "../../utils/editor";
 import actions from "../../actions";
 
 import {
-  getSelectedLocation,
-  getBreakpointForLine,
-  getConditionalPanelLine
+  getBreakpointForLocation,
+  getConditionalPanelLocation
 } from "../../selectors";
 
+import type { SourceLocation } from "../../types";
+
 type Props = {
   breakpoint: ?Object,
-  selectedLocation: Object,
   setBreakpointCondition: Function,
-  line: number,
+  location: SourceLocation,
   editor: Object,
   openConditionalPanel: () => void,
   closeConditionalPanel: () => void
 };
 
 export class ConditionalPanel extends PureComponent<Props> {
   cbPanel: null | Object;
   input: ?HTMLInputElement;
@@ -55,19 +55,17 @@ export class ConditionalPanel extends Pu
     if (e.key === "Enter") {
       this.saveAndClose();
     } else if (e.key === "Escape") {
       this.props.closeConditionalPanel();
     }
   };
 
   setBreakpoint(condition: string) {
-    const { selectedLocation, line } = this.props;
-    const sourceId = selectedLocation ? selectedLocation.sourceId : "";
-    const location = { sourceId, line };
+    const { location } = this.props;
     return this.props.setBreakpointCondition(location, { condition });
   }
 
   clearConditionalPanel() {
     if (this.cbPanel) {
       this.cbPanel.clear();
       this.cbPanel = null;
     }
@@ -79,51 +77,42 @@ export class ConditionalPanel extends Pu
   repositionOnScroll = () => {
     if (this.panelNode && this.scrollParent) {
       const { scrollLeft } = this.scrollParent;
       this.panelNode.style.transform = `translateX(${scrollLeft}px)`;
     }
   };
 
   componentWillMount() {
-    if (this.props.line) {
-      return this.renderToWidget(this.props);
-    }
+    return this.renderToWidget(this.props);
   }
 
-  componentWillUpdate(nextProps: Props) {
-    if (nextProps.line) {
-      return this.renderToWidget(nextProps);
-    }
+  componentWillUpdate() {
     return this.clearConditionalPanel();
   }
 
   componentDidUpdate(prevProps: Props) {
     this.keepFocusOnInput();
   }
 
   componentWillUnmount() {
     // This is called if CodeMirror is re-initializing itself before the
     // user closes the conditional panel. Clear the widget, and re-render it
     // as soon as this component gets remounted
     return this.clearConditionalPanel();
   }
 
   renderToWidget(props: Props) {
     if (this.cbPanel) {
-      if (this.props.line && this.props.line == props.line) {
-        return props.closeConditionalPanel();
-      }
       this.clearConditionalPanel();
     }
 
-    const { selectedLocation, line, editor } = props;
-    const sourceId = selectedLocation ? selectedLocation.sourceId : "";
+    const { location, editor } = props;
 
-    const editorLine = toEditorLine(sourceId, line);
+    const editorLine = toEditorLine(location.sourceId, location.line || 0);
     this.cbPanel = editor.codeMirror.addLineWidget(
       editorLine,
       this.renderConditionalPanel(props),
       {
         coverGutter: true,
         noHScroll: false
       }
     );
@@ -175,23 +164,21 @@ export class ConditionalPanel extends Pu
   }
 
   render() {
     return null;
   }
 }
 
 const mapStateToProps = state => {
-  const line = getConditionalPanelLine(state);
-  const selectedLocation = getSelectedLocation(state);
+  const location = getConditionalPanelLocation(state);
 
   return {
-    selectedLocation,
-    breakpoint: getBreakpointForLine(state, selectedLocation.sourceId, line),
-    line
+    breakpoint: getBreakpointForLocation(state, location),
+    location
   };
 };
 
 const {
   setBreakpointCondition,
   openConditionalPanel,
   closeConditionalPanel
 } = actions;
--- a/devtools/client/debugger/new/src/components/Editor/EditorMenu.js
+++ b/devtools/client/debugger/new/src/components/Editor/EditorMenu.js
@@ -198,20 +198,16 @@ function getMenuItems(
   }
 
   return menuItems;
 }
 
 class EditorMenu extends Component {
   props: Props;
 
-  constructor() {
-    super();
-  }
-
   shouldComponentUpdate(nextProps) {
     return nextProps.contextMenu.type === "Editor";
   }
 
   componentWillUpdate(nextProps) {
     // clear the context menu since it is open
     this.props.setContextMenu("", null);
     return this.showMenu(nextProps);
--- a/devtools/client/debugger/new/src/components/Editor/GutterMenu.js
+++ b/devtools/client/debugger/new/src/components/Editor/GutterMenu.js
@@ -27,17 +27,18 @@ export function gutterMenu({
   line,
   event,
   isPaused,
   toggleBreakpoint,
   openConditionalPanel,
   toggleDisabledBreakpoint,
   isCbPanelOpen,
   closeConditionalPanel,
-  continueToHere
+  continueToHere,
+  sourceId
 }) {
   event.stopPropagation();
   event.preventDefault();
 
   const gutterItems = {
     addBreakpoint: {
       id: "node-menu-add-breakpoint",
       label: L10N.getStr("editor.addBreakpoint")
@@ -79,17 +80,21 @@ export function gutterMenu({
     },
     accelerator: L10N.getStr("toggleBreakpoint.key"),
     ...(breakpoint ? gutterItems.removeBreakpoint : gutterItems.addBreakpoint)
   };
 
   const conditionalBreakpoint = {
     accesskey: L10N.getStr("editor.addConditionalBreakpoint.accesskey"),
     disabled: false,
-    click: () => openConditionalPanel(line),
+    // Leaving column undefined so pause points can be detected
+    click: () =>
+      openConditionalPanel(
+        breakpoint ? breakpoint.location : { line, sourceId }
+      ),
     accelerator: L10N.getStr("toggleCondPanel.key"),
     ...(breakpoint && breakpoint.condition
       ? gutterItems.editConditional
       : gutterItems.addConditional)
   };
 
   const items = [toggleBreakpointItem, conditionalBreakpoint];
 
@@ -116,20 +121,16 @@ export function gutterMenu({
   }
 
   showMenu(event, items);
 }
 
 class GutterContextMenuComponent extends Component {
   props: Props;
 
-  constructor() {
-    super();
-  }
-
   shouldComponentUpdate(nextProps) {
     return nextProps.contextMenu.type === "Gutter";
   }
 
   componentWillUpdate(nextProps) {
     // clear the context menu since it is open
     this.props.setContextMenu("", null);
     return this.showMenu(nextProps);
--- a/devtools/client/debugger/new/src/components/Editor/HighlightLines.js
+++ b/devtools/client/debugger/new/src/components/Editor/HighlightLines.js
@@ -11,20 +11,16 @@ import { getHighlightedLineRange } from 
 type Props = {
   highlightedLineRange: Object,
   editor: Object
 };
 
 class HighlightLines extends Component<Props> {
   highlightLineRange: Function;
 
-  constructor() {
-    super();
-  }
-
   componentDidMount() {
     this.highlightLineRange();
   }
 
   componentWillUpdate() {
     this.clearHighlightRange();
   }
 
--- a/devtools/client/debugger/new/src/components/Editor/index.js
+++ b/devtools/client/debugger/new/src/components/Editor/index.js
@@ -15,17 +15,17 @@ import { isLoaded } from "../../utils/so
 import { isFirefox } from "devtools-environment";
 import { features } from "../../utils/prefs";
 import { getIndentation } from "../../utils/indentation";
 
 import {
   getActiveSearch,
   getSelectedLocation,
   getSelectedSource,
-  getConditionalPanelLine,
+  getConditionalPanelLocation,
   getSymbols
 } from "../../selectors";
 
 // Redux actions
 import actions from "../../actions";
 
 import Footer from "./Footer";
 import SearchBar from "./SearchBar";
@@ -76,27 +76,27 @@ const cssVars = {
 
 export type Props = {
   selectedLocation: ?SourceLocation,
   selectedSource: ?Source,
   searchOn: boolean,
   horizontal: boolean,
   startPanelSize: number,
   endPanelSize: number,
-  conditionalPanelLine: number,
+  conditionalPanelLocation: SourceLocation,
   symbols: SymbolDeclarations,
 
   // Actions
-  openConditionalPanel: (?number) => void,
+  openConditionalPanel: (?SourceLocation) => void,
   closeConditionalPanel: void => void,
   setContextMenu: (string, any) => void,
-  continueToHere: (?number) => void,
-  toggleBreakpoint: (?number) => void,
-  toggleBreakpointsAtLine: (?number) => void,
-  addOrToggleDisabledBreakpoint: (?number) => void,
+  continueToHere: number => void,
+  toggleBreakpoint: number => void,
+  toggleBreakpointsAtLine: number => void,
+  addOrToggleDisabledBreakpoint: number => void,
   jumpToMappedLocation: any => void,
   traverseResults: (boolean, Object) => void,
   updateViewport: void => void
 };
 
 type State = {
   editor: SourceEditor
 };
@@ -251,38 +251,45 @@ class Editor extends PureComponent<Props
 
     const line = getCursorLine(codeMirror);
     return toSourceLine(selectedSource.id, line);
   }
 
   onToggleBreakpoint = (key, e: KeyboardEvent) => {
     e.preventDefault();
     e.stopPropagation();
-    const { selectedSource, conditionalPanelLine } = this.props;
+    const { selectedSource, conditionalPanelLocation } = this.props;
 
     if (!selectedSource) {
       return;
     }
 
     const line = this.getCurrentLine();
+    if (typeof line !== "number") {
+      return;
+    }
 
     if (e.shiftKey) {
       this.toggleConditionalPanel(line);
-    } else if (!conditionalPanelLine) {
+    } else if (!conditionalPanelLocation) {
       this.props.toggleBreakpoint(line);
     } else {
       this.toggleConditionalPanel(line);
       this.props.toggleBreakpoint(line);
     }
   };
 
   onToggleConditionalPanel = (key, e: KeyboardEvent) => {
     e.stopPropagation();
     e.preventDefault();
     const line = this.getCurrentLine();
+    if (typeof line !== "number") {
+      return;
+    }
+
     this.toggleConditionalPanel(line);
   };
 
   onEditorScroll = debounce(this.props.updateViewport, 200);
 
   onKeyDown(e: KeyboardEvent) {
     const { codeMirror } = this.state.editor;
     const { key, target } = e;
@@ -338,42 +345,45 @@ class Editor extends PureComponent<Props
   onGutterClick = (
     cm: Object,
     line: number,
     gutter: string,
     ev: MouseEvent
   ) => {
     const {
       selectedSource,
-      conditionalPanelLine,
+      conditionalPanelLocation,
       closeConditionalPanel,
       addOrToggleDisabledBreakpoint,
       toggleBreakpointsAtLine,
       continueToHere
     } = this.props;
 
     // ignore right clicks in the gutter
     if (
       (ev.ctrlKey && ev.button === 0) ||
       ev.button === 2 ||
       (selectedSource && selectedSource.isBlackBoxed) ||
       !selectedSource
     ) {
       return;
     }
 
-    if (conditionalPanelLine) {
+    if (conditionalPanelLocation) {
       return closeConditionalPanel();
     }
 
     if (gutter === "CodeMirror-foldgutter") {
       return;
     }
 
     const sourceLine = toSourceLine(selectedSource.id, line);
+    if (typeof sourceLine !== "number") {
+      return;
+    }
 
     if (ev.metaKey) {
       return continueToHere(sourceLine);
     }
 
     if (ev.shiftKey) {
       return addOrToggleDisabledBreakpoint(sourceLine);
     }
@@ -397,26 +407,26 @@ class Editor extends PureComponent<Props
         e
       );
       jumpToMappedLocation(sourceLocation);
     }
   }
 
   toggleConditionalPanel = line => {
     const {
-      conditionalPanelLine,
+      conditionalPanelLocation,
       closeConditionalPanel,
       openConditionalPanel
     } = this.props;
 
-    if (conditionalPanelLine) {
+    if (conditionalPanelLocation) {
       return closeConditionalPanel();
     }
 
-    return openConditionalPanel(line);
+    return openConditionalPanel(conditionalPanelLocation);
   };
 
   closeConditionalPanel = () => {
     return this.props.closeConditionalPanel();
   };
 
   shouldScrollToLocation(nextProps) {
     const { selectedLocation, selectedSource } = this.props;
@@ -530,17 +540,17 @@ class Editor extends PureComponent<Props
       height:
         subtractions.length === 0
           ? "100%"
           : `calc(100% - ${subtractions.join(" - ")})`
     };
   }
 
   renderItems() {
-    const { horizontal, selectedSource } = this.props;
+    const { horizontal, selectedSource, conditionalPanelLocation } = this.props;
     const { editor } = this.state;
 
     if (!editor || !selectedSource) {
       return null;
     }
 
     return (
       <div>
@@ -548,17 +558,17 @@ class Editor extends PureComponent<Props
         <HighlightLine />
         <EmptyLines editor={editor} />
         <Breakpoints editor={editor} />
         <Preview editor={editor} editorRef={this.$editorWrapper} />;
         <Footer editor={editor} horizontal={horizontal} />
         <HighlightLines editor={editor} />
         <EditorMenu editor={editor} />
         <GutterMenu editor={editor} />
-        <ConditionalPanel editor={editor} />
+        {conditionalPanelLocation ? <ConditionalPanel editor={editor} /> : null}
         {features.columnBreakpoints ? (
           <ColumnBreakpoints editor={editor} />
         ) : null}
       </div>
     );
   }
 
   renderSearchBar() {
@@ -594,17 +604,17 @@ Editor.contextTypes = {
 
 const mapStateToProps = state => {
   const selectedSource = getSelectedSource(state);
 
   return {
     selectedLocation: getSelectedLocation(state),
     selectedSource,
     searchOn: getActiveSearch(state) === "file",
-    conditionalPanelLine: getConditionalPanelLine(state),
+    conditionalPanelLocation: getConditionalPanelLocation(state),
     symbols: getSymbols(state, selectedSource)
   };
 };
 
 export default connect(
   mapStateToProps,
   {
     openConditionalPanel: actions.openConditionalPanel,
--- a/devtools/client/debugger/new/src/components/QuickOpenModal.js
+++ b/devtools/client/debugger/new/src/components/QuickOpenModal.js
@@ -121,25 +121,21 @@ export class QuickOpenModal extends Comp
     const { sources } = this.props;
     const results =
       query == "" ? sources : filter(sources, this.dropGoto(query));
     return this.setState({ results });
   };
 
   searchSymbols = (query: string) => {
     const {
-      symbols: { functions, identifiers }
+      symbols: { functions }
     } = this.props;
 
     let results = functions;
-    if (this.isVariableQuery()) {
-      results = identifiers;
-    } else {
-      results = results.filter(result => result.title !== "anonymous");
-    }
+    results = results.filter(result => result.title !== "anonymous");
 
     if (query === "@" || query === "#") {
       return this.setState({ results });
     }
 
     this.setState({ results: filter(results, query.slice(1)) });
   };
 
@@ -214,34 +210,21 @@ export class QuickOpenModal extends Comp
           item.location && item.location.start ? item.location.start.line : 0
       });
     }
 
     this.gotoLocation({ sourceId: item.id, line: 0 });
   };
 
   onSelectResultItem = (item: QuickOpenResult) => {
-    const {
-      selectSpecificLocation,
-      selectedSource,
-      highlightLineRange
-    } = this.props;
+    const { selectedSource, highlightLineRange } = this.props;
     if (!this.isSymbolSearch() || selectedSource == null) {
       return;
     }
 
-    if (this.isVariableQuery()) {
-      const line =
-        item.location && item.location.start ? item.location.start.line : 0;
-      return selectSpecificLocation({
-        sourceId: selectedSource.id,
-        line
-      });
-    }
-
     if (this.isFunctionQuery()) {
       return highlightLineRange({
         ...(item.location != null
           ? { start: item.location.start.line, end: item.location.end.line }
           : {}),
         sourceId: selectedSource.id
       });
     }
@@ -317,18 +300,17 @@ export class QuickOpenModal extends Comp
 
   getResultCount = () => {
     const results = this.state.results;
     return results && results.length ? results.length : 0;
   };
 
   // Query helpers
   isFunctionQuery = () => this.props.searchType === "functions";
-  isVariableQuery = () => this.props.searchType === "variables";
-  isSymbolSearch = () => this.isFunctionQuery() || this.isVariableQuery();
+  isSymbolSearch = () => this.isFunctionQuery();
   isGotoQuery = () => this.props.searchType === "goto";
   isGotoSourceQuery = () => this.props.searchType === "gotoSource";
   isShortcutQuery = () => this.props.searchType === "shortcuts";
   isSourcesQuery = () => this.props.searchType === "sources";
   isSourceSearch = () => this.isSourcesQuery() || this.isGotoSourceQuery();
 
   /* eslint-disable react/no-danger */
   renderHighlight(
@@ -374,20 +356,17 @@ export class QuickOpenModal extends Comp
     }
     return !this.getResultCount() && !!query;
   }
 
   getSummaryMessage() {
     let summaryMsg = "";
     if (this.isGotoQuery()) {
       summaryMsg = L10N.getStr("shortcuts.gotoLine");
-    } else if (
-      (this.isFunctionQuery() || this.isVariableQuery()) &&
-      this.props.symbolsLoading
-    ) {
+    } else if (this.isFunctionQuery() && this.props.symbolsLoading) {
       summaryMsg = L10N.getStr("loadingText");
     }
     return summaryMsg;
   }
 
   render() {
     const { enabled, query } = this.props;
     const { selectedIndex, results } = this.state;
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
@@ -60,17 +60,17 @@ type Props = {
 class Breakpoint extends PureComponent<Props> {
   onContextMenu = e => {
     showContextMenu({ ...this.props, contextMenuEvent: e });
   };
 
   onDoubleClick = () => {
     const { breakpoint, openConditionalPanel } = this.props;
     if (breakpoint.condition) {
-      openConditionalPanel(breakpoint.selectedLocation.line);
+      openConditionalPanel(breakpoint.selectedLocation);
     }
   };
 
   selectBreakpoint = () => {
     const { breakpoint, selectSpecificLocation } = this.props;
     selectSpecificLocation(breakpoint.selectedLocation);
   };
 
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointsContextMenu.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointsContextMenu.js
@@ -1,27 +1,48 @@
 /* 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 { buildMenu, showMenu } from "devtools-contextmenu";
 
-export default function showContextMenu(props) {
+import actions from "../../../actions";
+import type { Breakpoint } from "../../../types";
+import type { FormattedBreakpoint } from "../../../selectors/breakpointSources";
+
+type Props = {
+  breakpoint: FormattedBreakpoint,
+  breakpoints: Breakpoint[],
+  removeBreakpoint: typeof actions.removeBreakpoint,
+  removeBreakpoints: typeof actions.removeBreakpoints,
+  removeAllBreakpoints: typeof actions.removeAllBreakpoints,
+  toggleBreakpoints: typeof actions.toggleBreakpoints,
+  toggleAllBreakpoints: typeof actions.toggleAllBreakpoints,
+  toggleDisabledBreakpoint: typeof actions.toggleDisabledBreakpoint,
+  selectSpecificLocation: typeof actions.selectSpecificLocation,
+  setBreakpointCondition: typeof actions.setBreakpointCondition,
+  openConditionalPanel: typeof actions.openConditionalPanel,
+  contextMenuEvent: SyntheticEvent<HTMLElement>
+};
+
+export default function showContextMenu(props: Props) {
   const {
+    breakpoint,
+    breakpoints,
     removeBreakpoint,
     removeBreakpoints,
     removeAllBreakpoints,
     toggleBreakpoints,
     toggleAllBreakpoints,
     toggleDisabledBreakpoint,
     selectSpecificLocation,
     setBreakpointCondition,
     openConditionalPanel,
-    breakpoints,
-    breakpoint,
     contextMenuEvent
   } = props;
 
   contextMenuEvent.preventDefault();
 
   const deleteSelfLabel = L10N.getStr("breakpointMenuItem.deleteSelf2.label");
   const deleteAllLabel = L10N.getStr("breakpointMenuItem.deleteAll2.label");
   const deleteOthersLabel = L10N.getStr(
@@ -164,27 +185,27 @@ export default function showContextMenu(
   };
 
   const addConditionItem = {
     id: "node-menu-add-condition",
     label: addConditionLabel,
     accesskey: addConditionKey,
     click: () => {
       selectSpecificLocation(breakpoint.selectedLocation);
-      openConditionalPanel(breakpoint.selectedLocation.line);
+      openConditionalPanel(breakpoint.selectedLocation);
     }
   };
 
   const editConditionItem = {
     id: "node-menu-edit-condition",
     label: editConditionLabel,
     accesskey: editConditionKey,
     click: () => {
       selectSpecificLocation(breakpoint.selectedLocation);
-      openConditionalPanel(breakpoint.selectedLocation.line);
+      openConditionalPanel(breakpoint.selectedLocation);
     }
   };
 
   const hideEnableSelfItem = !breakpoint.disabled;
   const hideEnableAllItem = disabledBreakpoints.length === 0;
   const hideEnableOthersItem = otherDisabledBreakpoints.length === 0;
   const hideDisableAllItem = enabledBreakpoints.length === 0;
   const hideDisableOthersItem = otherEnabledBreakpoints.length === 0;
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
@@ -74,17 +74,17 @@ export default class Group extends Compo
     FrameMenu(
       frame,
       frameworkGroupingOn,
       { copyStackTrace, toggleFrameworkGrouping, toggleBlackBox },
       event
     );
   }
 
-  toggleFrames = (event) => {
+  toggleFrames = event => {
     event.stopPropagation();
     this.setState(prevState => ({ expanded: !prevState.expanded }));
   };
 
   renderFrames() {
     const {
       group,
       selectFrame,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/XHRBreakpoints.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/XHRBreakpoints.js
@@ -33,16 +33,22 @@ type Props = {
 type State = {
   editing: boolean,
   inputValue: string,
   inputMethod: string,
   editIndex: number,
   focused: boolean
 };
 
+// At present, the "Pause on any URL" checkbox creates an xhrBreakpoint
+// of "ANY" with no path, so we can remove that before creating the list
+function getExplicitXHRBreakpoints(xhrBreakpoints) {
+  return xhrBreakpoints.filter(bp => bp.path !== "");
+}
+
 class XHRBreakpoints extends Component<Props, State> {
   _input: ?HTMLInputElement;
 
   constructor(props: Props) {
     super(props);
 
     this.state = {
       editing: false,
@@ -202,37 +208,35 @@ class XHRBreakpoints extends Component<P
           <CloseButton handleClick={e => removeXHRBreakpoint(index)} />
         </div>
       </li>
     );
   };
 
   renderBreakpoints = () => {
     const { showInput, xhrBreakpoints } = this.props;
-
-    // At present, the "Pause on any URL" checkbox creates an xhrBreakpoint
-    // of "ANY" with no path, so we can remove that before creating the list
-    const explicitXhrBreakpoints = xhrBreakpoints.filter(bp => bp.path !== "");
+    const explicitXhrBreakpoints = getExplicitXHRBreakpoints(xhrBreakpoints);
 
     return (
       <ul className="pane expressions-list">
         {explicitXhrBreakpoints.map(this.renderBreakpoint)}
         {(showInput || explicitXhrBreakpoints.size === 0) &&
           this.renderXHRInput(this.handleNewSubmit)}
       </ul>
     );
   };
 
   renderCheckbox = () => {
     const { shouldPauseOnAny, togglePauseOnAny, xhrBreakpoints } = this.props;
+    const explicitXhrBreakpoints = getExplicitXHRBreakpoints(xhrBreakpoints);
 
     return (
       <div
         className={classnames("breakpoints-exceptions-options", {
-          empty: xhrBreakpoints.size === 0
+          empty: explicitXhrBreakpoints.size === 0
         })}
       >
         <ExceptionOption
           className="breakpoints-exceptions"
           label={L10N.getStr("pauseOnAnyXHR")}
           isChecked={shouldPauseOnAny}
           onChange={() => togglePauseOnAny()}
         />
--- a/devtools/client/debugger/new/src/components/shared/ResultList.js
+++ b/devtools/client/debugger/new/src/components/shared/ResultList.js
@@ -23,20 +23,16 @@ type Props = {
 export default class ResultList extends Component<Props> {
   displayName: "ResultList";
 
   static defaultProps = {
     size: "small",
     role: "listbox"
   };
 
-  constructor(props: Props) {
-    super(props);
-  }
-
   renderListItem = (item: any, index: number) => {
     if (item.value === "/" && item.title === "") {
       item.title = "(index)";
     }
 
     const { selectItem, selected } = this.props;
     const props = {
       onClick: event => selectItem(event, item, index),
--- a/devtools/client/debugger/new/src/reducers/breakpoints.js
+++ b/devtools/client/debugger/new/src/reducers/breakpoints.js
@@ -7,16 +7,18 @@
 /**
  * Breakpoints reducer
  * @module reducers/breakpoints
  */
 
 import * as I from "immutable";
 
 import { isGeneratedId } from "devtools-source-map";
+import { isEqual } from "lodash";
+
 import { makeLocationId } from "../utils/breakpoint";
 
 import type { XHRBreakpoint, Breakpoint, SourceLocation } from "../types";
 import type { Action, DonePromiseAction } from "../actions/types";
 
 export type BreakpointsMap = { [string]: Breakpoint };
 export type XHRBreakpointsList = I.List<XHRBreakpoint>;
 
@@ -246,16 +248,20 @@ function remapBreakpoints(state, action)
 }
 
 function removeBreakpoint(state, action): BreakpointsState {
   const { breakpoint } = action;
   const id = makeLocationId(breakpoint.location);
   return unsetBreakpoint(state, id);
 }
 
+function isMatchingLocation(location1, location2) {
+  return isEqual(location1, location2);
+}
+
 // Selectors
 // TODO: these functions should be moved out of the reducer
 
 type OuterState = { breakpoints: BreakpointsState };
 
 export function getBreakpointsMap(state: OuterState): BreakpointsMap {
   return state.breakpoints.breakpoints;
 }
@@ -300,26 +306,30 @@ export function getBreakpointsForSource(
   return breakpoints.filter(bp => {
     const location = isGeneratedSource
       ? bp.generatedLocation || bp.location
       : bp.location;
     return location.sourceId === sourceId;
   });
 }
 
-export function getBreakpointForLine(
+export function getBreakpointForLocation(
   state: OuterState,
-  sourceId: string,
-  line: number | null
+  location: SourceLocation | null
 ): ?Breakpoint {
-  if (!sourceId) {
+  if (!location || !location.sourceId) {
     return undefined;
   }
+
+  const loc = { ...location };
+
   const breakpoints = getBreakpointsList(state);
-  return breakpoints.find(breakpoint => breakpoint.location.line === line);
+  return breakpoints.find(breakpoint =>
+    isMatchingLocation(loc, breakpoint.location)
+  );
 }
 
 export function getHiddenBreakpoint(state: OuterState): ?Breakpoint {
   const breakpoints = getBreakpointsList(state);
   return breakpoints.find(bp => bp.hidden);
 }
 
 export function getHiddenBreakpointLocation(
--- a/devtools/client/debugger/new/src/reducers/quick-open.js
+++ b/devtools/client/debugger/new/src/reducers/quick-open.js
@@ -7,22 +7,17 @@
  * @module reducers/quick-open
  */
 
 import makeRecord from "../utils/makeRecord";
 import { parseQuickOpenQuery } from "../utils/quick-open";
 import type { Action } from "../actions/types";
 import type { Record } from "../utils/makeRecord";
 
-export type QuickOpenType =
-  | "sources"
-  | "functions"
-  | "variables"
-  | "goto"
-  | "gotoSource";
+export type QuickOpenType = "sources" | "functions" | "goto" | "gotoSource";
 
 type QuickOpenState = {
   enabled: boolean,
   query: string,
   searchType: QuickOpenType
 };
 
 export const createQuickOpenState = makeRecord({
--- a/devtools/client/debugger/new/src/reducers/ui.js
+++ b/devtools/client/debugger/new/src/reducers/ui.js
@@ -7,17 +7,17 @@
 /**
  * UI reducer
  * @module reducers/ui
  */
 
 import makeRecord from "../utils/makeRecord";
 import { prefs } from "../utils/prefs";
 
-import type { Source, PartialRange } from "../types";
+import type { Source, PartialRange, SourceLocation } from "../types";
 
 import type { Action, panelPositionType } from "../actions/types";
 import type { Record } from "../utils/makeRecord";
 
 export type ActiveSearchType = "project" | "file";
 
 export type OrientationType = "horizontal" | "vertical";
 
@@ -35,30 +35,30 @@ export type UIState = {
   frameworkGroupingOn: boolean,
   orientation: OrientationType,
   viewport: ?Viewport,
   highlightedLineRange?: {
     start?: number,
     end?: number,
     sourceId?: number
   },
-  conditionalPanelLine: null | number
+  conditionalPanelLocation: null | SourceLocation
 };
 
 export const createUIState = makeRecord(
   ({
     selectedPrimaryPaneTab: "sources",
     activeSearch: null,
     contextMenu: {},
     shownSource: null,
     startPanelCollapsed: prefs.startPanelCollapsed,
     endPanelCollapsed: prefs.endPanelCollapsed,
     frameworkGroupingOn: prefs.frameworkGroupingOn,
     highlightedLineRange: undefined,
-    conditionalPanelLine: null,
+    conditionalPanelLocation: null,
     orientation: "horizontal",
     viewport: null
   }: UIState)
 );
 
 function update(
   state: Record<UIState> = createUIState(),
   action: Action
@@ -105,20 +105,20 @@ function update(
 
       return state.set("highlightedLineRange", lineRange);
 
     case "CLOSE_QUICK_OPEN":
     case "CLEAR_HIGHLIGHT_LINES":
       return state.set("highlightedLineRange", {});
 
     case "OPEN_CONDITIONAL_PANEL":
-      return state.set("conditionalPanelLine", action.line);
+      return state.set("conditionalPanelLocation", action.location);
 
     case "CLOSE_CONDITIONAL_PANEL":
-      return state.set("conditionalPanelLine", null);
+      return state.set("conditionalPanelLocation", null);
 
     case "SET_PRIMARY_PANE_TAB":
       return state.set("selectedPrimaryPaneTab", action.tabName);
 
     case "CLOSE_PROJECT_SEARCH": {
       if (state.get("activeSearch") === "project") {
         return state.set("activeSearch", null);
       }
@@ -175,18 +175,20 @@ export function getPaneCollapse(
 
   return state.ui.get("endPanelCollapsed");
 }
 
 export function getHighlightedLineRange(state: OuterState) {
   return state.ui.get("highlightedLineRange");
 }
 
-export function getConditionalPanelLine(state: OuterState): null | number {
-  return state.ui.get("conditionalPanelLine");
+export function getConditionalPanelLocation(
+  state: OuterState
+): null | SourceLocation {
+  return state.ui.get("conditionalPanelLocation");
 }
 
 export function getOrientation(state: OuterState): boolean {
   return state.ui.get("orientation");
 }
 
 export function getViewport(state: OuterState) {
   return state.ui.get("viewport");
--- a/devtools/client/debugger/new/src/utils/breakpoint/astBreakpointLocation.js
+++ b/devtools/client/debugger/new/src/utils/breakpoint/astBreakpointLocation.js
@@ -2,23 +2,22 @@
  * 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 { getSymbols } from "../../workers/parser";
 import { findClosestFunction } from "../ast";
 
-import type { SymbolDeclarations } from "../../workers/parser";
-
 import type { SourceLocation, Source, ASTLocation } from "../../types";
+import type { Symbols } from "../../reducers/ast";
 
 export function getASTLocation(
   source: Source,
-  symbols: SymbolDeclarations,
+  symbols: ?Symbols,
   location: SourceLocation
 ): ASTLocation {
   if (source.isWasm || !symbols || symbols.loading) {
     return { name: undefined, offset: location };
   }
 
   const scope = findClosestFunction(symbols, location);
   if (scope) {
--- a/devtools/client/debugger/new/src/utils/breakpoint/index.js
+++ b/devtools/client/debugger/new/src/utils/breakpoint/index.js
@@ -182,23 +182,14 @@ export function createPendingBreakpoint(
     disabled: bp.disabled,
     location: pendingLocation,
     astLocation: bp.astLocation,
     generatedLocation: pendingGeneratedLocation
   };
 }
 
 export function sortBreakpoints(breakpoints: FormattedBreakpoint[]) {
-  breakpoints = breakpoints.map(bp => ({
-    ...bp,
-    selectedLocation: {
-      ...bp.selectedLocation,
-      // If a breakpoint has an undefined column, we must provide a 0 value
-      // or the breakpoint will display after all explicit column numbers
-      column: bp.selectedLocation.column || 0
-    }
-  }));
-
   return sortBy(breakpoints, [
     "selectedLocation.line",
-    "selectedLocation.column"
+    ({ selectedLocation }) =>
+      selectedLocation.column === undefined || selectedLocation.column
   ]);
 }
--- a/devtools/client/debugger/new/src/utils/editor/index.js
+++ b/devtools/client/debugger/new/src/utils/editor/index.js
@@ -153,27 +153,24 @@ function isVisible(codeMirror: any, top:
     top,
     scrollTop,
     scrollTop + scrollArea.clientHeight - fontHeight
   );
 
   return inXView && inYView;
 }
 
-export function getLocationsInViewport(_editor: any) {
+export function getLocationsInViewport({ codeMirror }: Object) {
   // Get scroll position
-  const charWidth = _editor.codeMirror.defaultCharWidth();
-  const scrollArea = _editor.codeMirror.getScrollInfo();
-  const { scrollLeft } = _editor.codeMirror.doc;
-  const rect = _editor.codeMirror.getWrapperElement().getBoundingClientRect();
-  const topVisibleLine = _editor.codeMirror.lineAtHeight(rect.top, "window");
-  const bottomVisibleLine = _editor.codeMirror.lineAtHeight(
-    rect.bottom,
-    "window"
-  );
+  const charWidth = codeMirror.defaultCharWidth();
+  const scrollArea = codeMirror.getScrollInfo();
+  const { scrollLeft } = codeMirror.doc;
+  const rect = codeMirror.getWrapperElement().getBoundingClientRect();
+  const topVisibleLine = codeMirror.lineAtHeight(rect.top, "window");
+  const bottomVisibleLine = codeMirror.lineAtHeight(rect.bottom, "window");
 
   const leftColumn = Math.floor(scrollLeft > 0 ? scrollLeft / charWidth : 0);
   const rightPosition = scrollLeft + (scrollArea.clientWidth - 30);
   const rightCharacter = Math.floor(rightPosition / charWidth);
 
   return {
     start: {
       line: topVisibleLine,
@@ -181,61 +178,69 @@ export function getLocationsInViewport(_
     },
     end: {
       line: bottomVisibleLine,
       column: rightCharacter
     }
   };
 }
 
-export function markText(_editor: any, className, { start, end }: EditorRange) {
-  return _editor.codeMirror.markText(
+export function markText(
+  { codeMirror }: Object,
+  className,
+  { start, end }: EditorRange
+) {
+  return codeMirror.markText(
     { ch: start.column, line: start.line },
     { ch: end.column, line: end.line },
     { className }
   );
 }
 
-export function lineAtHeight(_editor, sourceId, event) {
-  const _editorLine = _editor.codeMirror.lineAtHeight(event.clientY);
+export function lineAtHeight({ codeMirror }, sourceId, event) {
+  const _editorLine = codeMirror.lineAtHeight(event.clientY);
   return toSourceLine(sourceId, _editorLine);
 }
 
 export function getSourceLocationFromMouseEvent(
-  _editor: Object,
+  { codeMirror }: Object,
   selectedLocation: SourceLocation,
   e: MouseEvent
 ) {
-  const { line, ch } = _editor.codeMirror.coordsChar({
+  const { line, ch } = codeMirror.coordsChar({
     left: e.clientX,
     top: e.clientY
   });
 
   return {
     sourceId: selectedLocation.sourceId,
     line: line + 1,
     column: ch + 1
   };
 }
 
-export function forEachLine(codeMirror, iter) {
+export function forEachLine(codeMirror: Object, iter) {
   codeMirror.operation(() => {
     codeMirror.doc.iter(0, codeMirror.lineCount(), iter);
   });
 }
 
-export function removeLineClass(codeMirror, line, className) {
+export function removeLineClass(
+  codeMirror: Object,
+  line: number,
+  className: string
+) {
   codeMirror.removeLineClass(line, "line", className);
 }
 
-export function clearLineClass(codeMirror, className) {
+export function clearLineClass(codeMirror: Object, className: string) {
   forEachLine(codeMirror, line => {
     removeLineClass(codeMirror, line, className);
   });
 }
 
-export function getTextForLine(codeMirror, line) {
+export function getTextForLine(codeMirror: Object, line: number): string {
   return codeMirror.getLine(line - 1).trim();
 }
 
-export function getCursorLine(codeMirror): number {
+export function getCursorLine(codeMirror: Object): number {
   return codeMirror.getCursor().line;
 }
--- a/devtools/client/debugger/new/src/utils/pause/frames/displayName.js
+++ b/devtools/client/debugger/new/src/utils/pause/frames/displayName.js
@@ -6,17 +6,17 @@
 
 // eslint-disable-next-line max-len
 import type { LocalFrame } from "../../../components/SecondaryPanes/Frames/types";
 import { getFilename } from "../../source";
 
 // Decodes an anonymous naming scheme that
 // spider monkey implements based on "Naming Anonymous JavaScript Functions"
 // http://johnjbarton.github.io/nonymous/index.html
-const objectProperty = /([\w\d]+)$/;
+const objectProperty = /([\w\d\$]+)$/;
 const arrayProperty = /\[(.*?)\]$/;
 const functionProperty = /([\w\d]+)[\/\.<]*?$/;
 const annonymousProperty = /([\w\d]+)\(\^\)$/;
 
 export function simplifyDisplayName(displayName: string | void): string | void {
   // if the display name has a space it has already been mapped
   if (!displayName || /\s/.exec(displayName)) {
     return displayName;
--- a/devtools/client/debugger/new/src/utils/pause/pausePoints.js
+++ b/devtools/client/debugger/new/src/utils/pause/pausePoints.js
@@ -37,20 +37,25 @@ export function formatPausePoints(text: 
     const { break: breakPoint, step } = node.types;
     const types = `${breakPoint ? "b" : ""}${step ? "s" : ""}`;
     lines[line - 1] = insertStrtAt(lines[line - 1], column, `/*${types}*/`);
   });
 
   return lines.join("\n");
 }
 
-export async function mapPausePoints(pausePoints, iteratee) {
+export async function mapPausePoints<T>(
+  pausePoints: PausePoints,
+  iteratee: PausePoint => T
+) {
   const results = await Promise.all(convertToList(pausePoints).map(iteratee));
 
+  const newPausePoints = {};
   for (const line in pausePoints) {
     const linePoints = pausePoints[line];
+    const newLinePoints = (newPausePoints[line] = {});
     for (const column in linePoints) {
-      linePoints[column] = results.shift();
+      newLinePoints[column] = results.shift();
     }
   }
 
-  return pausePoints;
+  return newPausePoints;
 }
--- a/devtools/client/debugger/new/src/utils/prefs.js
+++ b/devtools/client/debugger/new/src/utils/prefs.js
@@ -110,17 +110,17 @@ export const features = new PrefsHelper(
   codeFolding: ["Bool", "code-folding"],
   pausePoints: ["Bool", "pause-points"],
   skipPausing: ["Bool", "skip-pausing"],
   autocompleteExpression: ["Bool", "autocomplete-expressions"],
   mapExpressionBindings: ["Bool", "map-expression-bindings"],
   mapAwaitExpression: ["Bool", "map-await-expression"],
   componentPane: ["Bool", "component-pane"],
   xhrBreakpoints: ["Bool", "xhr-breakpoints"],
-  originalBlackbox: ["Bool", "origial-blackbox"],
+  originalBlackbox: ["Bool", "origial-blackbox"]
 });
 
 export const asyncStore = asyncStoreHelper("debugger", {
   pendingBreakpoints: ["pending-breakpoints", {}],
   tabs: ["tabs", []],
   xhrBreakpoints: ["xhr-breakpoints", []]
 });
 
--- a/devtools/client/debugger/new/src/utils/quick-open.js
+++ b/devtools/client/debugger/new/src/utils/quick-open.js
@@ -12,17 +12,20 @@ import {
   getSourceQueryString
 } from "./source";
 
 import type { Location as BabelLocation } from "@babel/types";
 import type { Symbols } from "../reducers/ast";
 import type { QuickOpenType } from "../reducers/quick-open";
 import type { TabList } from "../reducers/tabs";
 import type { Source } from "../types";
-import type { SymbolDeclaration } from "../workers/parser";
+import type {
+  SymbolDeclaration,
+  IdentifierDeclaration
+} from "../workers/parser";
 
 export const MODIFIERS = {
   "@": "functions",
   "#": "variables",
   ":": "goto",
   "?": "shortcuts"
 };
 
@@ -81,21 +84,22 @@ export type QuickOpenResult = {|
   title: string | React$Element<"div">,
   subtitle?: string,
   location?: BabelLocation,
   url?: string,
   icon?: string
 |};
 
 export type FormattedSymbolDeclarations = {|
-  variables: Array<QuickOpenResult>,
   functions: Array<QuickOpenResult>
 |};
 
-export function formatSymbol(symbol: SymbolDeclaration): QuickOpenResult {
+export function formatSymbol(
+  symbol: SymbolDeclaration | IdentifierDeclaration
+): QuickOpenResult {
   return {
     id: `${symbol.name}:${symbol.location.start.line}`,
     title: symbol.name,
     subtitle: `${symbol.location.start.line}`,
     value: symbol.name,
     location: symbol.location
   };
 }
--- a/devtools/client/debugger/new/src/workers/parser/index.js
+++ b/devtools/client/debugger/new/src/workers/parser/index.js
@@ -98,10 +98,11 @@ export type {
   PausePoint,
   PausePoints
 } from "./types";
 
 export type {
   ClassDeclaration,
   SymbolDeclaration,
   SymbolDeclarations,
+  IdentifierDeclaration,
   FunctionDeclaration
 } from "./getSymbols";
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-cond.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-cond.js
@@ -1,18 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-function findBreakpoint(dbg, url, line) {
+function findBreakpoint(dbg, url, line, column = 0) {
   const {
     selectors: { getBreakpoint },
     getState
   } = dbg;
   const source = findSource(dbg, url);
-  return getBreakpoint(getState(), { sourceId: source.id, line });
+  const location = { sourceId: source.id, line, column };
+  return getBreakpoint(getState(), location);
 }
 
 function getLineEl(dbg, line) {
   const lines = dbg.win.document.querySelectorAll(".CodeMirror-code > div");
   return lines[line - 1];
 }
 
 function assertEditorBreakpoint(dbg, line, shouldExist) {
@@ -58,16 +59,17 @@ async function setConditionalBreakpoint(
 
 add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html", "simple2");
   await selectSource(dbg, "simple2");
   await waitForSelectedSource(dbg, "simple2");
 
   await setConditionalBreakpoint(dbg, 5, "1");
   await waitForDispatch(dbg, "ADD_BREAKPOINT");
+  
   let bp = findBreakpoint(dbg, "simple2", 5);
   is(bp.condition, "1", "breakpoint is created with the condition");
   assertEditorBreakpoint(dbg, 5, true);
 
   await setConditionalBreakpoint(dbg, 5, "2");
   await waitForDispatch(dbg, "SET_BREAKPOINT_CONDITION");
   bp = findBreakpoint(dbg, "simple2", 5);
   is(bp.condition, "12", "breakpoint is created with the condition");
--- a/devtools/client/netmonitor/test/sjs_content-type-test-server.sjs
+++ b/devtools/client/netmonitor/test/sjs_content-type-test-server.sjs
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+Cu.importGlobalProperties(["TextEncoder"]);
+
 function gzipCompressString(string, obs) {
 
   let scs = Cc["@mozilla.org/streamConverters;1"]
            .getService(Ci.nsIStreamConverterService);
   let listener = Cc["@mozilla.org/network/stream-loader;1"]
                 .createInstance(Ci.nsIStreamLoader);
   listener.init(obs);
   let converter = scs.asyncConvertData("uncompressed", "gzip",
@@ -66,17 +68,29 @@ function handleRequest(request, response
   timer.initWithCallback(() => {
     // to avoid garbage collection
     timer = null;
     switch (format) {
       case "txt": {
         response.setStatusLine(request.httpVersion, status, "DA DA DA");
         response.setHeader("Content-Type", "text/plain", false);
         setCacheHeaders();
-        response.write("Братан, ты вообще качаешься?");
+
+        function convertToUtf8(str) {
+          return String.fromCharCode(...new TextEncoder().encode(str));
+        }
+
+        // This script must be evaluated as UTF-8 for this to write out the
+        // bytes of the string in UTF-8.  If it's evaluated as Latin-1, the
+        // written bytes will be the result of UTF-8-encoding this string
+        // *twice*.
+        let data = "Братан, ты вообще качаешься?";
+        let stringOfUtf8Bytes = convertToUtf8(data);
+        response.write(stringOfUtf8Bytes);
+
         response.finish();
         break;
       }
       case "xml": {
         response.setStatusLine(request.httpVersion, status, "OK");
         response.setHeader("Content-Type", "text/xml; charset=utf-8", false);
         setCacheHeaders();
         response.write("<label value='greeting'>Hello XML!</label>");
--- a/devtools/client/preferences/debugger.js
+++ b/devtools/client/preferences/debugger.js
@@ -62,9 +62,9 @@ pref("devtools.debugger.features.code-fo
 pref("devtools.debugger.features.outline", true);
 pref("devtools.debugger.features.pause-points", true);
 pref("devtools.debugger.features.component-pane", false);
 pref("devtools.debugger.features.async-stepping", true);
 pref("devtools.debugger.features.skip-pausing", true);
 pref("devtools.debugger.features.autocomplete-expressions", false);
 pref("devtools.debugger.features.map-expression-bindings", true);
 pref("devtools.debugger.features.xhr-breakpoints", true);
-pref("devtools.debugger.features.origial-blackbox", false);
\ No newline at end of file
+pref("devtools.debugger.features.origial-blackbox", false);
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -7128,20 +7128,20 @@ class ObjectInspectorItem extends Compon
     }
 
     const { item, depth, focused, expanded, onLabelClick } = this.props;
     return dom.span({
       className: "object-label",
       onClick: onLabelClick ? event => {
         event.stopPropagation();
 
-        // If the user selected text, bail out.
-        if (Utils.selection.documentHasSelection()) {
-          return;
-        }
+const objectProperty = /([\w\d\$]+)$/;
+const arrayProperty = /\[(.*?)\]$/;
+const functionProperty = /([\w\d]+)[\/\.<]*?$/;
+const annonymousProperty = /([\w\d]+)\(\^\)$/;
 
         onLabelClick(item, {
           depth,
           focused,
           expanded,
           setExpanded: this.props.setExpanded
         });
       } : undefined
@@ -7159,9 +7159,9 @@ class ObjectInspectorItem extends Compon
   }
 }
 
 module.exports = ObjectInspectorItem;
 
 /***/ })
 
 /******/ });
-});
\ No newline at end of file
+});
--- a/dom/security/test/csp/file_punycode_host_src.sjs
+++ b/dom/security/test/csp/file_punycode_host_src.sjs
@@ -4,17 +4,21 @@
 const HTML_PART1 =
   "<!DOCTYPE HTML>" +
   "<html><head><meta charset=\"utf-8\">" +
   "<title>Bug 1224225 - CSP source matching should work for punycoded domain names</title>" +
   "</head>" +
   "<body>" +
   "<script id='script' src='";
 
-const TESTCASE1 = "http://sub2.ält.example.org/";
+// U+00E4 LATIN SMALL LETTER A WITH DIAERESIS, encoded as UTF-8 code units.
+// response.write() writes out the provided string characters truncated to
+// bytes, so "ä" literally would write a literal \xE4 byte, not the desired
+// two-byte UTF-8 sequence.
+const TESTCASE1 = "http://sub2.\xC3\xA4lt.example.org/";
 const TESTCASE2 = "http://sub2.xn--lt-uia.example.org/"
 
 const HTML_PART2 = "tests/dom/security/test/csp/file_punycode_host_src.js'></script>" +
   "</body>" +
   "</html>";
 
 function handleRequest(request, response)
 {
--- a/dom/xhr/tests/file_XHR_header.sjs
+++ b/dom/xhr/tests/file_XHR_header.sjs
@@ -1,6 +1,9 @@
 // SJS file for getAllResponseRequests vs getResponseRequest
 function handleRequest(request, response)
 {
-  response.setHeader("X-Custom-Header-Bytes", "…", false);
+  // Header strings are interpreted by truncating the characters in them to
+  // bytes, so U+2026 HORIZONTAL ELLIPSIS here must be encoded manually: using
+  // "…" as the string would write a \x26 byte.
+  response.setHeader("X-Custom-Header-Bytes", "\xE2\x80\xA6", false);
   response.write("42");
 }
--- a/dom/xhr/tests/test_XHR_header.html
+++ b/dom/xhr/tests/test_XHR_header.html
@@ -15,17 +15,18 @@
 <script class="testbody" type="application/javascript">
 "use strict";
 SimpleTest.waitForExplicitFinish();
 
 let xhr = new XMLHttpRequest();
 xhr.open('GET', 'file_XHR_header.sjs', true);
 xhr.onreadystatechange = function() {
   if (xhr.readyState == 4) {
-    ok(xhr.getResponseHeader('X-Custom-Header-Bytes') == "\xE2\x80\xA6", 'getResponseHeader byte-inflates the output');
+    ok(xhr.getResponseHeader('X-Custom-Header-Bytes') == "\xE2\x80\xA6",
+       "getResponseHeader returns a string of the header's raw bytes");
     SimpleTest.finish();
   }
 }
 xhr.send(null);
 </script>
 </pre>
 </body>
 </html>
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5079,21 +5079,23 @@ void MStoreSlot::printOpcode(GenericPrin
   out.printf(" ");
   getOperand(0)->printName(out);
   out.printf(" %d ", slot());
   getOperand(1)->printName(out);
 }
 #endif
 
 MDefinition* MFunctionEnvironment::foldsTo(TempAllocator& alloc) {
-  if (!input()->isLambda()) {
-    return this;
-  }
-
-  return input()->toLambda()->environmentChain();
+  if (input()->isLambda()) {
+    return input()->toLambda()->environmentChain();
+  }
+  if (input()->isLambdaArrow()) {
+    return input()->toLambdaArrow()->environmentChain();
+  }
+  return this;
 }
 
 static bool AddIsANonZeroAdditionOf(MAdd* add, MDefinition* ins) {
   if (add->lhs() != ins && add->rhs() != ins) {
     return false;
   }
   MDefinition* other = (add->lhs() == ins) ? add->rhs() : add->lhs();
   if (!IsNumberType(other->type())) {
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -3235,16 +3235,23 @@ void HttpBaseChannel::ReleaseListeners()
   mCallbacks = nullptr;
   mProgressSink = nullptr;
   mCompressListener = nullptr;
 }
 
 void HttpBaseChannel::DoNotifyListener() {
   LOG(("HttpBaseChannel::DoNotifyListener this=%p", this));
 
+  // In case nsHttpChannel::OnStartRequest wasn't called (e.g. due to flag
+  // LOAD_ONLY_IF_MODIFIED) we want to set mAfterOnStartRequestBegun to true
+  // before notifying listener.
+  if (!mAfterOnStartRequestBegun) {
+    mAfterOnStartRequestBegun = true;
+  }
+
   if (mListener) {
     MOZ_ASSERT(!mOnStartRequestCalled,
                "We should not call OnStartRequest twice");
 
     nsCOMPtr<nsIStreamListener> listener = mListener;
     listener->OnStartRequest(this, mListenerContext);
 
     mOnStartRequestCalled = true;
--- a/netwerk/test/httpserver/httpd.js
+++ b/netwerk/test/httpserver/httpd.js
@@ -2524,17 +2524,17 @@ ServerHandler.prototype =
         try {
           // Alas, the line number in errors dumped to console when calling the
           // request handler is simply an offset from where we load the SJS file.
           // Work around this in a reasonably non-fragile way by dynamically
           // getting the line number where we evaluate the SJS file.  Don't
           // separate these two lines!
           var line = new Error().lineNumber;
           let uri = Services.io.newFileURI(file);
-          Services.scriptloader.loadSubScript(uri.spec, s);
+          Services.scriptloader.loadSubScript(uri.spec, s, "UTF-8");
         } catch (e) {
           dumpn("*** syntax error in SJS at " + file.path + ": " + e);
           throw HTTP_500;
         }
 
         try {
           s.handleRequest(metadata, response);
         } catch (e) {
--- a/servo/ports/geckolib/tests/specified_values.rs
+++ b/servo/ports/geckolib/tests/specified_values.rs
@@ -1,18 +1,18 @@
 /* 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 https://mozilla.org/MPL/2.0/. */
 
-use style;
-
 #[cfg(all(test, target_pointer_width = "64"))]
 #[test]
 fn size_of_specified_values() {
+    use style;
     use std::mem::size_of;
+
     let threshold = 24;
 
     let mut bad_properties = vec![];
 
     macro_rules! check_property {
         ( $( { $name: ident, $boxed: expr } )+ ) => {
             $(
                 let size = size_of::<style::properties::longhands::$name::SpecifiedValue>();
--- a/toolkit/components/search/tests/xpcshell/data/searchSuggestions.sjs
+++ b/toolkit/components/search/tests/xpcshell/data/searchSuggestions.sjs
@@ -1,26 +1,39 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 
+Cu.importGlobalProperties(["TextEncoder"]);
+
 /**
  * Provide search suggestions in the OpenSearch JSON format.
  */
 
 function handleRequest(request, response) {
   // Get the query parameters from the query string.
   let query = parseQueryString(request.queryString);
 
+  function convertToUtf8(str) {
+    return String.fromCharCode(...new TextEncoder().encode(str));
+  }
+
   function writeSuggestions(query, completions = []) {
     let result = [query, completions];
-    response.write(JSON.stringify(result));
-    return result;
+    let jsonString = JSON.stringify([query, completions]);
+
+    // This script must be evaluated as UTF-8 for this to write out the bytes of
+    // the string in UTF-8.  If it's evaluated as Latin-1, the written bytes
+    // will be the result of UTF-8-encoding the result-string *twice*, which
+    // will break the "I ❤️" case further down.
+    let stringOfUtf8Bytes = convertToUtf8(jsonString);
+
+    response.write(stringOfUtf8Bytes);
   }
 
   response.setStatusLine(request.httpVersion, 200, "OK");
 
   let q = request.method == "GET" ? query.q : undefined;
   if (q == "cookie") {
     response.setHeader("Set-Cookie", "cookie=1");
     writeSuggestions(q);
@@ -68,12 +81,12 @@ function handleRequest(request, response
     response.setStatusLine(request.httpVersion, 404, "Not Found");
   }
 }
 
 function parseQueryString(queryString) {
   let query = {};
   queryString.split('&').forEach(function (val) {
     let [name, value] = val.split('=');
-    query[name] = unescape(value).replace(/[+]/g, " ");
+    query[name] = decodeURIComponent(value).replace(/[+]/g, " ");
   });
   return query;
 }
--- a/toolkit/themes/osx/mozapps/extensions/extensions.css
+++ b/toolkit/themes/osx/mozapps/extensions/extensions.css
@@ -14,17 +14,17 @@
 
 #header-utils-btn {
   list-style-image: url("chrome://mozapps/skin/extensions/utilities.svg");
   -moz-context-properties: fill;
   fill: #424f5a;
 }
 
 #header-utils-btn > .toolbarbutton-menu-dropmarker {
-  list-style-image: url("chrome://mozapps/skin/extensions/toolbarbutton-dropmarker.png");
+  list-style-image: url("chrome://global/skin/icons/arrow-dropdown-12.svg");
   padding: 0;
   margin-inline-start: 2px;
 }
 
 .sorter[checkState="1"] {
   list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
 }
 
deleted file mode 100644
index fd0805619bf1eda9442a73b464f86904ebd38b33..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/toolkit/themes/osx/mozapps/jar.mn
+++ b/toolkit/themes/osx/mozapps/jar.mn
@@ -5,15 +5,14 @@
 toolkit.jar:
 #include ../../shared/mozapps.inc.mn
   skin/classic/mozapps/downloads/buttons.png                      (downloads/buttons.png)
   skin/classic/mozapps/downloads/unknownContentType.css           (downloads/unknownContentType.css)
   skin/classic/mozapps/extensions/discover-logo.png               (extensions/discover-logo.png)
   skin/classic/mozapps/extensions/rating-won.png                  (extensions/rating-won.png)
   skin/classic/mozapps/extensions/rating-not-won.png              (extensions/rating-not-won.png)
   skin/classic/mozapps/extensions/cancel.png                      (extensions/cancel.png)
-  skin/classic/mozapps/extensions/toolbarbutton-dropmarker.png    (extensions/toolbarbutton-dropmarker.png)
 * skin/classic/mozapps/extensions/extensions.css                  (extensions/extensions.css)
   skin/classic/mozapps/extensions/blocklist.css                   (extensions/blocklist.css)
   skin/classic/mozapps/profile/profileSelection.css               (profile/profileSelection.css)
   skin/classic/mozapps/update/buttons.png                         (update/buttons.png)
 * skin/classic/mozapps/update/updates.css                         (update/updates.css)
   skin/classic/mozapps/handling/handling.css                      (handling/handling.css)
--- a/xpcom/base/MemoryTelemetry.cpp
+++ b/xpcom/base/MemoryTelemetry.cpp
@@ -8,16 +8,17 @@
 #include "nsMemoryReporterManager.h"
 
 #include "GCTelemetry.h"
 #include "mozJSComponentLoader.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Result.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/Services.h"
+#include "mozilla/ScopeExit.h"
 #include "mozilla/SimpleEnumerator.h"
 #include "mozilla/SystemGroup.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsContentUtils.h"
 #include "nsIBrowserDOMWindow.h"
@@ -223,16 +224,22 @@ static inline void HandleMemoryReport(Te
     Telemetry::Accumulate(aId, val);
   } else {
     Telemetry::Accumulate(aId, aKey, val);
   }
 }
 
 nsresult MemoryTelemetry::GatherReports(
     const std::function<void()>& aCompletionCallback) {
+  auto cleanup = MakeScopeExit([&]() {
+    if (aCompletionCallback) {
+      aCompletionCallback();
+    }
+  });
+
   RefPtr<nsMemoryReporterManager> mgr = nsMemoryReporterManager::GetOrCreate();
   MOZ_DIAGNOSTIC_ASSERT(mgr);
   NS_ENSURE_TRUE(mgr, NS_ERROR_FAILURE);
 
   auto startTime = TimeStamp::Now();
 
 #define RECORD(id, metric, units)                                       \
   do {                                                                  \
@@ -310,17 +317,19 @@ nsresult MemoryTelemetry::GatherReports(
           NS_DispatchToMainThread(completionRunnable.forget(),
                                   NS_DISPATCH_NORMAL);
         }
       });
 
 #undef RECORD
 
   nsresult rv = mThreadPool->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
-  Unused << NS_WARN_IF(NS_FAILED(rv));
+  if (!NS_WARN_IF(NS_FAILED(rv))) {
+    cleanup.release();
+  }
 
   // If we're running in the parent process, collect data from all processes for
   // the MEMORY_TOTAL histogram.
   if (XRE_IsParentProcess() && !mTotalMemoryGatherer) {
     mTotalMemoryGatherer = new TotalMemoryGatherer();
     mTotalMemoryGatherer->Begin(mThreadPool);
   }