Bug 1507312 - Update Debugger Frontend v102 r=jlast
☠☠ backed out by dea39df5254a ☠ ☠
authorLogan Smyth <loganfsmyth@gmail.com>
Fri, 16 Nov 2018 19:27:14 +0000
changeset 503264 e368528459474eedea833311aea43cacc85c36ee
parent 503263 1e6a2dbfa104081bed1a4273aff4cbd2c29d3335
child 503265 3e9dee27eade4a155aec2748424e414a1900ece2
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjlast
bugs1507312
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 1507312 - Update Debugger Frontend v102 r=jlast Differential Revision: https://phabricator.services.mozilla.com/D11967
devtools/client/debugger/new/README.mozilla
devtools/client/debugger/new/dist/debugger.css
devtools/client/debugger/new/src/actions/breakpoints/index.js
devtools/client/debugger/new/src/actions/breakpoints/remapLocations.js
devtools/client/debugger/new/src/components/App.js
devtools/client/debugger/new/src/components/Editor/Breakpoints.js
devtools/client/debugger/new/src/components/Editor/CallSites.js
devtools/client/debugger/new/src/components/Editor/Tab.js
devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTreeItem.js
devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointHeading.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/Breakpoints/moz.build
devtools/client/debugger/new/src/components/SecondaryPanes/Frames/WhyPaused.js
devtools/client/debugger/new/src/components/SecondaryPanes/XHRBreakpoints.js
devtools/client/debugger/new/src/components/SecondaryPanes/index.js
devtools/client/debugger/new/src/components/shared/Badge.js
devtools/client/debugger/new/src/reducers/breakpoints.js
devtools/client/debugger/new/src/reducers/sources.js
devtools/client/debugger/new/src/selectors/breakpointAtLocation.js
devtools/client/debugger/new/src/selectors/breakpointSources.js
devtools/client/debugger/new/src/selectors/breakpoints.js
devtools/client/debugger/new/src/selectors/visibleBreakpoints.js
devtools/client/debugger/new/src/utils/prefs.js
devtools/client/debugger/new/src/utils/source.js
devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints.js
devtools/client/debugger/new/test/mochitest/browser_dbg-editor-gutter.js
devtools/client/debugger/new/test/mochitest/browser_dbg-reload.js
devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps-reload.js
devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps-reloading.js
devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps.js
devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js
devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps3.js
devtools/client/debugger/new/test/mochitest/browser_dbg-sources-querystring.js
devtools/client/debugger/new/test/mochitest/browser_dbg-xhr-breakpoints.js
devtools/client/debugger/new/test/mochitest/helpers.js
devtools/client/preferences/debugger.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 100
+Version 102
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-99...release-100
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-101...release-102
 
 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/dist/debugger.css
+++ b/devtools/client/debugger/new/dist/debugger.css
@@ -3076,16 +3076,21 @@ debug-expression-error {
 .breakpoints-list .breakpoint-heading {
   text-overflow: ellipsis;
   overflow: hidden;
   display: flex;
   width: 100%;
   align-items: center;
 }
 
+.breakpoints-list .breakpoint-heading .filename {
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
 .breakpoints-list .breakpoint-heading .filename span {
   opacity: 0.7;
   padding-left: 4px;
 }
 
 /* temporary until we refactor the sources tree and tab icon styles */
 .breakpoints-list .breakpoint-heading .source-icon.file {
   top: 0;
@@ -3476,34 +3481,38 @@ html[dir="rtl"] .breakpoints-list .break
   padding: 0 4px;
   font-size: 0.9em;
 }
 /* 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/>. */
 
 .why-paused {
+  display: flex;
   background-color: var(--theme-body-background);
   color: var(--theme-body-color);
-  padding: 10px 10px 10px 20px;
-  white-space: normal;
   opacity: 0.6;
   font-size: 12px;
-  flex: 0 1 auto;
   text-align: center;
   font-style: italic;
   font-weight: 300;
   cursor: default;
+  min-height: 24px;
+}
+
+.why-paused > div {
+  margin: auto;
 }
 
 .theme-dark .secondary-panes .why-paused {
   color: white;
 }
 
 .why-paused .message {
+  padding: 4px;
   font-size: 10px;
 }
 
 .why-paused .message.warning {
   font-size: 10px;
   color: var(--theme-graphs-red);
   font-weight: bold;
   font-style: normal;
@@ -3588,16 +3597,18 @@ html[dir="rtl"] .breakpoints-list .break
 
 :root.theme-light .frames ul li.selected .location,
 :root.theme-dark .frames ul li.selected .location {
   color: white;
 }
 
 .show-more-container {
   display: flex;
+  min-height: 24px;
+  padding: 4px 0;
 }
 
 .show-more {
   text-align: center;
   padding: 8px 0px;
   margin: 7px 10px 7px 7px;
   border: 1px solid var(--theme-splitter-color);
   background-color: var(--theme-tab-toolbar-background);
@@ -3985,17 +3996,17 @@ img.skipPausing {
 
 .xhr-checkbox {
     margin-left: 0px;
 }
 
 .xhr-label {
   max-width: calc(100% - var(--breakpoint-expression-right-clear-space));
   display: inline-block;
-  cursor: pointer;
+  cursor: text;
   flex-grow: 1;
   text-overflow: ellipsis;
   padding-inline-end: 8px;
   font-size: 11px;
 }
 
 .xhr-container .close-btn {
   offset-inline-end: 12px;
@@ -4269,32 +4280,57 @@ html .welcomebox .toggle-button-end.coll
   max-width: 100%;
   overflow: hidden;
   padding: 5px;
   cursor: default;
   height: 30px;
   font-size: 12px;
 }
 
+.source-tab::before {
+  content: "";
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 2px;
+  background: transparent;
+  transition: transform 250ms var(--animation-curve), opacity 250ms var(--animation-curve);
+  opacity: 0;
+  transform: scaleX(0);
+}
+
 .source-tab:first-child {
   margin-inline-start: 0;
 }
 
 .source-tab:not(.active):hover {
   background: linear-gradient(var(--theme-toolbar-hover) 28px, transparent 1px);
 }
 
+.source-tab:not(.active):hover::before {
+  background: var(--tab-line-hover-color);
+  opacity: 1;
+  transform: scaleX(1);
+}
+
 .source-tab.active {
   color: var(--theme-toolbar-selected-color);
   border-bottom-color: transparent;
   border-left: 1px solid var(--theme-splitter-color);
   border-right: 1px solid var(--theme-splitter-color);
   background-color: var(--theme-body-background);
 }
 
+.source-tab.active::before {
+  background: var(--tab-line-selected-color);
+  opacity: 1;
+  transform: scaleX(1);
+}
+
 .theme-dark .source-tab.active {
   color: var(--theme-toolbar-selected-color);
 }
 
 .source-tab.active path,
 .source-tab:hover path {
   fill: var(--theme-body-color);
 }
@@ -4367,40 +4403,16 @@ html[dir="rtl"] img.moreTabs {
 
 .source-tab.active .close-btn {
   visibility: visible;
 }
 
 .source-tab:hover .close-btn {
   visibility: visible;
 }
-
-.source-tab-line {
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 2px;
-  background: transparent;
-  transition: transform 250ms var(--animation-curve), opacity 250ms var(--animation-curve);
-  opacity: 0;
-  transform: scaleX(0);
-}
-
-.source-tab:not(.active):hover .source-tab-line {
-  background: var(--tab-line-hover-color);
-  opacity: 1;
-  transform: scaleX(1);
-}
-
-.source-tab.active .source-tab-line {
-  background: var(--tab-line-selected-color);
-  opacity: 1;
-  transform: scaleX(1);
-}
 /* 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/>. */
 
 .dropdown {
   --width: 150px;
   background: var(--theme-body-background);
   border: 1px solid var(--theme-splitter-color);
--- a/devtools/client/debugger/new/src/actions/breakpoints/index.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/index.js
@@ -8,17 +8,17 @@
  * Redux actions for breakpoints
  * @module actions/breakpoints
  */
 
 import { isOriginalId } from "devtools-source-map";
 import { PROMISE } from "../utils/middleware/promise";
 import {
   getBreakpoint,
-  getBreakpoints,
+  getBreakpointsList,
   getXHRBreakpoints,
   getSelectedSource,
   getBreakpointAtLocation,
   getBreakpointsAtLine
 } from "../../selectors";
 import { assertBreakpoint, createXHRBreakpoint } from "../../utils/breakpoint";
 import {
   addBreakpoint,
@@ -28,17 +28,16 @@ import {
 import remapLocations from "./remapLocations";
 import { syncBreakpoint } from "./syncBreakpoint";
 import { isEmptyLineInSource } from "../../reducers/ast";
 
 // this will need to be changed so that addCLientBreakpoint is removed
 
 import type { ThunkArgs, Action } from "../types";
 import type { Breakpoint, Location, XHRBreakpoint } from "../../types";
-import type { BreakpointsMap } from "../../reducers/types";
 
 import { recordEvent } from "../../utils/telemetry";
 
 type addBreakpointOptions = {
   condition?: string,
   hidden?: boolean
 };
 
@@ -108,21 +107,21 @@ export function disableBreakpoint(locati
 /**
  * Toggle All Breakpoints
  *
  * @memberof actions/breakpoints
  * @static
  */
 export function toggleAllBreakpoints(shouldDisableBreakpoints: boolean) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
-    const breakpoints = getBreakpoints(getState());
+    const breakpoints = getBreakpointsList(getState());
 
     const modifiedBreakpoints = [];
 
-    for (const [, breakpoint] of breakpoints) {
+    for (const breakpoint of breakpoints) {
       if (shouldDisableBreakpoints) {
         await client.removeBreakpoint(breakpoint.generatedLocation);
         const newBreakpoint: Breakpoint = { ...breakpoint, disabled: true };
         modifiedBreakpoints.push(newBreakpoint);
       } else {
         const newBreakpoint: Breakpoint = { ...breakpoint, disabled: false };
         modifiedBreakpoints.push(newBreakpoint);
       }
@@ -149,68 +148,62 @@ export function toggleAllBreakpoints(sho
 /**
  * Toggle Breakpoints
  *
  * @memberof actions/breakpoints
  * @static
  */
 export function toggleBreakpoints(
   shouldDisableBreakpoints: boolean,
-  breakpoints: BreakpointsMap
+  breakpoints: Breakpoint[]
 ) {
   return async ({ dispatch }: ThunkArgs) => {
-    const promises = breakpoints
-      .valueSeq()
-      .toJS()
-      .map(
-        ([, breakpoint]) =>
-          shouldDisableBreakpoints
-            ? dispatch(disableBreakpoint(breakpoint.location))
-            : dispatch(enableBreakpoint(breakpoint.location))
-      );
+    const promises = breakpoints.map(
+      breakpoint =>
+        shouldDisableBreakpoints
+          ? dispatch(disableBreakpoint(breakpoint.location))
+          : dispatch(enableBreakpoint(breakpoint.location))
+    );
 
     await Promise.all(promises);
   };
 }
 
 /**
  * Removes all breakpoints
  *
  * @memberof actions/breakpoints
  * @static
  */
 export function removeAllBreakpoints() {
   return async ({ dispatch, getState }: ThunkArgs) => {
-    const breakpointList = getBreakpoints(getState())
-      .valueSeq()
-      .toJS();
+    const breakpointList = getBreakpointsList(getState());
     return Promise.all(
       breakpointList.map(bp => dispatch(removeBreakpoint(bp.location)))
     );
   };
 }
 
 /**
  * Removes breakpoints
  *
  * @memberof actions/breakpoints
  * @static
  */
-export function removeBreakpoints(breakpoints: BreakpointsMap) {
+export function removeBreakpoints(breakpoints: Breakpoint[]) {
   return async ({ dispatch }: ThunkArgs) => {
-    const breakpointList = breakpoints.valueSeq().toJS();
     return Promise.all(
-      breakpointList.map(bp => dispatch(removeBreakpoint(bp.location)))
+      breakpoints.map(bp => dispatch(removeBreakpoint(bp.location)))
     );
   };
 }
 
 export function remapBreakpoints(sourceId: string) {
   return async ({ dispatch, getState, sourceMaps }: ThunkArgs) => {
-    const breakpoints = getBreakpoints(getState());
+    const breakpoints = getBreakpointsList(getState());
     const newBreakpoints = await remapLocations(
       breakpoints,
       sourceId,
       sourceMaps
     );
 
     return dispatch(
       ({
@@ -244,27 +237,26 @@ export function setBreakpointCondition(
     }
 
     if (bp.loading) {
       return;
     }
 
     if (bp.disabled) {
       await dispatch(enableBreakpoint(location));
-      bp.disabled = !bp.disabled;
     }
 
     await client.setBreakpointCondition(
       bp.id,
       location,
       condition,
       isOriginalId(bp.location.sourceId)
     );
 
-    const newBreakpoint = { ...bp, condition };
+    const newBreakpoint = { ...bp, disabled: false, condition };
 
     assertBreakpoint(newBreakpoint);
 
     return dispatch(
       ({
         type: "SET_BREAKPOINT_CONDITION",
         breakpoint: newBreakpoint
       }: Action)
@@ -317,17 +309,17 @@ export function toggleBreakpointsAtLine(
 
     if (!line || !selectedSource) {
       return;
     }
 
     const bps = getBreakpointsAtLine(state, line);
     const isEmptyLine = isEmptyLineInSource(state, line, selectedSource.id);
 
-    if (bps.size === 0 && !isEmptyLine) {
+    if (bps.length === 0 && !isEmptyLine) {
       return dispatch(
         addBreakpoint({
           sourceId: selectedSource.id,
           sourceUrl: selectedSource.url,
           line,
           column
         })
       );
--- a/devtools/client/debugger/new/src/actions/breakpoints/remapLocations.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/remapLocations.js
@@ -1,28 +1,27 @@
 /* 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 type { Breakpoint } from "../../types";
-import type { BreakpointsMap } from "../../selectors";
 
 export default function remapLocations(
   breakpoints: Breakpoint[],
   sourceId: string,
   sourceMaps: Object
 ) {
-  const sourceBreakpoints: BreakpointsMap = breakpoints.map(
+  const sourceBreakpoints: Promise<Breakpoint>[] = breakpoints.map(
     async breakpoint => {
       if (breakpoint.location.sourceId !== sourceId) {
         return breakpoint;
       }
       const location = await sourceMaps.getOriginalLocation(
         breakpoint.location
       );
       return { ...breakpoint, location };
     }
   );
 
-  return Promise.all(sourceBreakpoints.valueSeq());
+  return Promise.all(sourceBreakpoints);
 }
--- a/devtools/client/debugger/new/src/components/App.js
+++ b/devtools/client/debugger/new/src/components/App.js
@@ -2,17 +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 React, { Component } from "react";
 import { connect } from "react-redux";
 import PropTypes from "prop-types";
 
-import { features } from "../utils/prefs";
+import { prefs, features } from "../utils/prefs";
 import actions from "../actions";
 import A11yIntention from "./A11yIntention";
 import { ShortcutsModal } from "./ShortcutsModal";
 
 import {
   getSelectedSource,
   getPaneCollapse,
   getActiveSearch,
@@ -242,34 +242,38 @@ class App extends Component<Props, State
     }));
   }
 
   renderLayout = () => {
     const { startPanelCollapsed, endPanelCollapsed } = this.props;
     const horizontal = this.isHorizontal();
 
     const maxSize = horizontal ? "70%" : "95%";
-    const primaryInitialSize = horizontal ? "250px" : "150px";
 
     return (
       <SplitBox
         style={{ width: "100vw" }}
-        initialHeight={400}
-        initialWidth={300}
+        initialSize={prefs.endPanelSize}
         minSize={30}
         maxSize={maxSize}
         splitterSize={1}
         vert={horizontal}
+        onResizeEnd={num => {
+          prefs.endPanelSize = num;
+        }}
         startPanel={
           <SplitBox
             style={{ width: "100vw" }}
-            initialSize={primaryInitialSize}
+            initialSize={prefs.startPanelSize}
             minSize={30}
             maxSize="85%"
             splitterSize={1}
+            onResizeEnd={num => {
+              prefs.startPanelSize = num;
+            }}
             startPanelCollapsed={startPanelCollapsed}
             startPanel={<PrimaryPanes horizontal={horizontal} />}
             endPanel={this.renderEditorPane()}
           />
         }
         endPanelControl={true}
         endPanel={
           <SecondaryPanes
--- a/devtools/client/debugger/new/src/components/Editor/Breakpoints.js
+++ b/devtools/client/debugger/new/src/components/Editor/Breakpoints.js
@@ -7,22 +7,21 @@ import { connect } from "react-redux";
 import React, { Component } from "react";
 
 import Breakpoint from "./Breakpoint";
 
 import { getSelectedSource, getVisibleBreakpoints } from "../../selectors";
 import { makeLocationId } from "../../utils/breakpoint";
 import { isLoaded } from "../../utils/source";
 
-import type { BreakpointsMap } from "../../reducers/types";
-import type { Source } from "../../types";
+import type { Breakpoint as BreakpointType, Source } from "../../types";
 
 type Props = {
   selectedSource: Source,
-  breakpoints: BreakpointsMap,
+  breakpoints: BreakpointType[],
   editor: Object
 };
 
 class Breakpoints extends Component<Props> {
   shouldComponentUpdate(nextProps: Props) {
     if (nextProps.selectedSource && !isLoaded(nextProps.selectedSource)) {
       return false;
     }
@@ -34,17 +33,17 @@ class Breakpoints extends Component<Prop
     const { breakpoints, selectedSource, editor } = this.props;
 
     if (!selectedSource || !breakpoints || selectedSource.isBlackBoxed) {
       return null;
     }
 
     return (
       <div>
-        {breakpoints.valueSeq().map(bp => {
+        {breakpoints.map(bp => {
           return (
             <Breakpoint
               key={makeLocationId(bp.location)}
               breakpoint={bp}
               selectedSource={selectedSource}
               editor={editor}
             />
           );
--- a/devtools/client/debugger/new/src/components/Editor/CallSites.js
+++ b/devtools/client/debugger/new/src/components/Editor/CallSites.js
@@ -39,64 +39,37 @@ class CallSites extends Component {
     editor: Object,
     breakpoints: Map,
     addBreakpoint: Function,
     removeBreakpoint: Function,
     selectedSource: Object,
     selectedLocation: Object
   };
 
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      showCallSites: false
-    };
-  }
-
   componentDidMount() {
     const { editor } = this.props;
     const codeMirrorWrapper = editor.codeMirror.getWrapperElement();
 
     codeMirrorWrapper.addEventListener("click", e => this.onTokenClick(e));
-    document.body.addEventListener("keydown", this.onKeyDown);
-    document.body.addEventListener("keyup", this.onKeyUp);
   }
 
   componentWillUnmount() {
     const { editor } = this.props;
     const codeMirrorWrapper = editor.codeMirror.getWrapperElement();
 
     codeMirrorWrapper.removeEventListener("click", e => this.onTokenClick(e));
-    document.body.removeEventListener("keydown", this.onKeyDown);
-    document.body.removeEventListener("keyup", this.onKeyUp);
   }
 
-  onKeyUp = e => {
-    if (e.key === "Alt") {
-      e.preventDefault();
-      this.setState({ showCallSites: false });
-    }
-  };
-
-  onKeyDown = e => {
-    if (e.key === "Alt") {
-      e.preventDefault();
-      this.setState({ showCallSites: true });
-    }
-  };
-
   onTokenClick(e) {
     const { target } = e;
     const { editor, selectedLocation } = this.props;
 
     if (
-      (!e.altKey && !target.classList.contains("call-site-bp")) ||
-      (!target.classList.contains("call-site") &&
-        !target.classList.contains("call-site-bp"))
+      !target.classList.contains("call-site") &&
+      !target.classList.contains("call-site-bp")
     ) {
       return;
     }
 
     const { sourceId } = selectedLocation;
     const { line, column } = getTokenLocation(editor.codeMirror, target);
 
     this.toggleBreakpoint(line, isWasm(sourceId) ? undefined : column);
@@ -138,33 +111,47 @@ class CallSites extends Component {
         sourceId: sourceId,
         sourceUrl: selectedSource.url,
         line: line,
         column: column
       });
     }
   }
 
+  // Return the call sites that are on the same line as an
+  // existing line breakpoint
+  filterCallSitesByLineNumber() {
+    const { callSites, breakpoints } = this.props;
+
+    const breakpointLines = new Set(breakpoints.map(bp => bp.location.line));
+
+    return callSites.filter(({ location }) =>
+      breakpointLines.has(location.start.line)
+    );
+  }
+
   render() {
     const { editor, callSites, selectedSource } = this.props;
-    const { showCallSites } = this.state;
+
     let sites;
-    if (!callSites) {
+    if (!callSites || (selectedSource && selectedSource.isPrettyPrinted)) {
       return null;
     }
 
+    const callSitesFiltered = this.filterCallSitesByLineNumber();
+
     editor.codeMirror.operation(() => {
-      const childCallSites = callSites.map((callSite, index) => {
+      const childCallSites = callSitesFiltered.map((callSite, index) => {
         const props = {
           key: index,
           callSite,
           editor,
           source: selectedSource,
           breakpoint: callSite.breakpoint,
-          showCallSite: showCallSites
+          showCallSite: true
         };
         return <CallSite {...props} />;
       });
       sites = <div>{childCallSites}</div>;
     });
     return sites;
   }
 }
@@ -175,17 +162,17 @@ function getCallSites(symbols, breakpoin
   }
 
   const callSites = symbols.callExpressions;
 
   // NOTE: we create a breakpoint map keyed on location
   // to speed up the lookups. Hopefully we'll fix the
   // inconsistency with column offsets so that we can expect
   // a breakpoint to be added at the beginning of a call expression.
-  const bpLocationMap = keyBy(breakpoints.valueSeq().toJS(), ({ location }) =>
+  const bpLocationMap = keyBy(breakpoints, ({ location }) =>
     locationKey(location)
   );
 
   function locationKey({ line, column }) {
     return `${line}/${column}`;
   }
 
   function findBreakpoint(callSite) {
--- a/devtools/client/debugger/new/src/components/Editor/Tab.js
+++ b/devtools/client/debugger/new/src/components/Editor/Tab.js
@@ -17,41 +17,44 @@ import type { Source } from "../../types
 
 import actions from "../../actions";
 
 import {
   getFileURL,
   getRawSourceURL,
   getTruncatedFileName,
   getDisplayPath,
-  isPretty
+  isPretty,
+  getSourceQueryString
 } from "../../utils/source";
 import { copyToTheClipboard } from "../../utils/clipboard";
 import { getTabMenuItems } from "../../utils/tabs";
 
 import {
   getSelectedSource,
   getActiveSearch,
-  getSourcesForTabs
+  getSourcesForTabs,
+  getHasSiblingOfSameName
 } from "../../selectors";
 
 import classnames from "classnames";
 
 type SourcesList = List<Source>;
 
 type Props = {
   tabSources: SourcesList,
   selectedSource: Source,
   source: Source,
   activeSearch: string,
   selectSource: string => void,
   closeTab: Source => void,
   closeTabs: (List<string>) => void,
   togglePrettyPrint: string => void,
-  showSource: string => void
+  showSource: string => void,
+  hasSiblingOfSameName: boolean
 };
 
 class Tab extends PureComponent<Props> {
   onTabContextMenu = (event, tab: string) => {
     event.preventDefault();
     this.showContextMenu(event, tab);
   };
 
@@ -150,17 +153,18 @@ class Tab extends PureComponent<Props> {
   }
 
   render() {
     const {
       selectedSource,
       selectSource,
       closeTab,
       source,
-      tabSources
+      tabSources,
+      hasSiblingOfSameName
     } = this.props;
     const sourceId = source.id;
     const active =
       selectedSource &&
       sourceId == selectedSource.id &&
       (!this.isProjectSearchEnabled() && !this.isSourceSearchEnabled());
     const isPrettyCode = isPretty(source);
 
@@ -176,34 +180,34 @@ class Tab extends PureComponent<Props> {
     }
 
     const className = classnames("source-tab", {
       active,
       pretty: isPrettyCode
     });
 
     const path = getDisplayPath(source, tabSources);
+    const query = hasSiblingOfSameName ? getSourceQueryString(source) : "";
 
     return (
       <div
         className={className}
         key={sourceId}
         onClick={handleTabClick}
         // Accommodate middle click to close tab
         onMouseUp={e => e.button === 1 && closeTab(source)}
         onContextMenu={e => this.onTabContextMenu(e, sourceId)}
-        title={getFileURL(source)}
+        title={getFileURL(source, false)}
       >
-        <span className="source-tab-line" />
         <SourceIcon
           source={source}
           shouldHide={icon => ["file", "javascript"].includes(icon)}
         />
         <div className="filename">
-          {getTruncatedFileName(source)}
+          {getTruncatedFileName(source, query)}
           {path && <span>{`../${path}/..`}</span>}
         </div>
         <CloseButton
           handleClick={onClickClose}
           tooltip={L10N.getStr("sourceTabs.closeTabButtonTooltip")}
         />
       </div>
     );
@@ -211,17 +215,18 @@ class Tab extends PureComponent<Props> {
 }
 
 const mapStateToProps = (state, { source }) => {
   const selectedSource = getSelectedSource(state);
 
   return {
     tabSources: getSourcesForTabs(state),
     selectedSource: selectedSource,
-    activeSearch: getActiveSearch(state)
+    activeSearch: getActiveSearch(state),
+    hasSiblingOfSameName: getHasSiblingOfSameName(state, source)
   };
 };
 
 export default connect(
   mapStateToProps,
   {
     selectSource: actions.selectSource,
     closeTab: actions.closeTab,
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTreeItem.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTreeItem.js
@@ -9,25 +9,27 @@ import { connect } from "react-redux";
 import classnames from "classnames";
 import { showMenu } from "devtools-contextmenu";
 
 import SourceIcon from "../shared/SourceIcon";
 import Svg from "../shared/Svg";
 
 import {
   getGeneratedSourceByURL,
-  getSourcesUrlsInSources
+  getHasSiblingOfSameName
 } from "../../selectors";
 import actions from "../../actions";
 
-import { isOriginal as isOriginalSource } from "../../utils/source";
+import {
+  isOriginal as isOriginalSource,
+  getSourceQueryString
+} from "../../utils/source";
 import { isDirectory } from "../../utils/sources-tree";
 import { copyToTheClipboard } from "../../utils/clipboard";
 import { features } from "../../utils/prefs";
-import { parse } from "../../utils/url";
 
 import type { TreeNode } from "../../utils/sources-tree/types";
 import type { Source } from "../../types";
 
 type Props = {
   debuggeeUrl: string,
   projectRoot: string,
   source: ?Source,
@@ -176,17 +178,17 @@ class SourceTreeItem extends Component<P
       hasMatchingGeneratedSource,
       hasSiblingOfSameName
     } = this.props;
 
     const suffix = hasMatchingGeneratedSource ? (
       <span className="suffix">{L10N.getStr("sourceFooter.mappedSuffix")}</span>
     ) : null;
 
-    const querystring = source ? parse(source.url).search : null;
+    const querystring = getSourceQueryString(source);
     const query =
       hasSiblingOfSameName && querystring ? (
         <span className="query">{querystring}</span>
       ) : null;
 
     return (
       <div
         className={classnames("node", { focused })}
@@ -209,24 +211,16 @@ class SourceTreeItem extends Component<P
 function getHasMatchingGeneratedSource(state, source: ?Source) {
   if (!source || !isOriginalSource(source)) {
     return false;
   }
 
   return !!getGeneratedSourceByURL(state, source.url);
 }
 
-function getHasSiblingOfSameName(state, source: ?Source) {
-  if (!source) {
-    return false;
-  }
-
-  return getSourcesUrlsInSources(state, source.url).length > 1;
-}
-
 const mapStateToProps = (state, props) => {
   const { source } = props;
   return {
     hasMatchingGeneratedSource: getHasMatchingGeneratedSource(state, source),
     hasSiblingOfSameName: getHasSiblingOfSameName(state, source)
   };
 };
 
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
@@ -14,35 +14,39 @@ 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 type { BreakpointsMap } from "../../../reducers/types";
 import type { FormattedBreakpoint } from "../../../selectors/breakpointSources";
 
-import type { Frame, Source, Location } from "../../../types";
+import type {
+  Breakpoint as BreakpointType,
+  Frame,
+  Source,
+  Location
+} from "../../../types";
 
 type FormattedFrame = {
   ...Frame,
   selectedLocation: Location
 };
 
 import {
-  getBreakpoints,
+  getBreakpointsList,
   getSelectedFrame,
   getSelectedSource
 } from "../../../selectors";
 
 type Props = {
   breakpoint: FormattedBreakpoint,
-  breakpoints: BreakpointsMap,
+  breakpoints: BreakpointType[],
   source: Source,
   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,
@@ -178,17 +182,17 @@ const getFormattedFrame = createSelector
     return {
       ...frame,
       selectedLocation: getSelectedLocation(frame, selectedSource)
     };
   }
 );
 
 const mapStateToProps = state => ({
-  breakpoints: getBreakpoints(state),
+  breakpoints: getBreakpointsList(state),
   frame: getFormattedFrame(state)
 });
 
 export default connect(
   mapStateToProps,
   {
     enableBreakpoint: actions.enableBreakpoint,
     removeBreakpoint: actions.removeBreakpoint,
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointHeading.js
@@ -0,0 +1,61 @@
+/* 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 actions from "../../../actions";
+import {
+  getTruncatedFileName,
+  getDisplayPath,
+  getSourceQueryString,
+  getFileURL
+} from "../../../utils/source";
+import { getHasSiblingOfSameName } from "../../../selectors";
+
+import SourceIcon from "../../shared/SourceIcon";
+
+import type { Source } from "../../../types";
+
+type Props = {
+  sources: Source[],
+  source: Source,
+  hasSiblingOfSameName: boolean,
+  selectSource: string => void
+};
+
+class BreakpointHeading extends PureComponent<Props> {
+  render() {
+    const { sources, source, hasSiblingOfSameName, selectSource } = this.props;
+
+    const path = getDisplayPath(source, sources);
+    const query = hasSiblingOfSameName ? getSourceQueryString(source) : "";
+
+    return (
+      <div
+        className="breakpoint-heading"
+        title={getFileURL(source, false)}
+        onClick={() => selectSource(source.id)}
+      >
+        <SourceIcon
+          source={source}
+          shouldHide={icon => ["file", "javascript"].includes(icon)}
+        />
+        <div className="filename">
+          {getTruncatedFileName(source, query)}
+          {path && <span>{`../${path}/..`}</span>}
+        </div>
+      </div>
+    );
+  }
+}
+
+const mapStateToProps = (state, { source }) => ({
+  hasSiblingOfSameName: getHasSiblingOfSameName(state, source)
+});
+
+export default connect(
+  mapStateToProps,
+  { selectSource: actions.selectSource }
+)(BreakpointHeading);
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointsContextMenu.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointsContextMenu.js
@@ -179,34 +179,34 @@ export default function showContextMenu(
     accesskey: editConditionKey,
     click: () => {
       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;
+  const hideEnableAllItem = disabledBreakpoints.length === 0;
+  const hideEnableOthersItem = otherDisabledBreakpoints.length === 0;
+  const hideDisableAllItem = enabledBreakpoints.length === 0;
+  const hideDisableOthersItem = otherEnabledBreakpoints.length === 0;
   const hideDisableSelfItem = breakpoint.disabled;
 
   const items = [
     { item: enableSelfItem, hidden: () => hideEnableSelfItem },
     { item: enableAllItem, hidden: () => hideEnableAllItem },
     { item: enableOthersItem, hidden: () => hideEnableOthersItem },
     {
       item: { type: "separator" },
       hidden: () =>
         hideEnableSelfItem && hideEnableAllItem && hideEnableOthersItem
     },
     { item: deleteSelfItem },
     { item: deleteAllItem },
-    { item: deleteOthersItem, hidden: () => breakpoints.size === 1 },
+    { item: deleteOthersItem, hidden: () => breakpoints.length === 1 },
     {
       item: { type: "separator" },
       hidden: () =>
         hideDisableSelfItem && hideDisableAllItem && hideDisableOthersItem
     },
 
     { item: disableSelfItem, hidden: () => hideDisableSelfItem },
     { item: disableAllItem, hidden: () => hideDisableAllItem },
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
@@ -6,40 +6,35 @@
 
 import React, { Component } from "react";
 import classnames from "classnames";
 import { connect } from "react-redux";
 
 import ExceptionOption from "./ExceptionOption";
 
 import Breakpoint from "./Breakpoint";
-import SourceIcon from "../../shared/SourceIcon";
+import BreakpointHeading from "./BreakpointHeading";
 
 import actions from "../../../actions";
-import {
-  getTruncatedFileName,
-  getDisplayPath,
-  getRawSourceURL
-} from "../../../utils/source";
+import { getDisplayPath } from "../../../utils/source";
 import { makeLocationId } from "../../../utils/breakpoint";
 
 import { getSelectedSource, getBreakpointSources } from "../../../selectors";
 
 import type { Source } from "../../../types";
 import type { BreakpointSources } from "../../../selectors/breakpointSources";
 
 import "./Breakpoints.css";
 
 type Props = {
   breakpointSources: BreakpointSources,
   selectedSource: Source,
   shouldPauseOnExceptions: boolean,
   shouldPauseOnCaughtExceptions: boolean,
-  pauseOnExceptions: Function,
-  selectSource: string => void
+  pauseOnExceptions: Function
 };
 
 class Breakpoints extends Component<Props> {
   renderExceptionsOptions() {
     const {
       breakpointSources,
       shouldPauseOnExceptions,
       shouldPauseOnCaughtExceptions,
@@ -80,31 +75,22 @@ class Breakpoints extends Component<Prop
     const sources = [
       ...breakpointSources.map(({ source, breakpoints }) => source)
     ];
 
     return [
       ...breakpointSources.map(({ source, breakpoints, i }) => {
         const path = getDisplayPath(source, sources);
         return [
-          <div
-            className="breakpoint-heading"
-            title={getRawSourceURL(source.url)}
+          <BreakpointHeading
+            source={source}
+            sources={sources}
+            path={path}
             key={source.url}
-            onClick={() => this.props.selectSource(source.id)}
-          >
-            <SourceIcon
-              source={source}
-              shouldHide={icon => ["file", "javascript"].includes(icon)}
-            />
-            <div className="filename">
-              {getTruncatedFileName(source)}
-              {path && <span>{`../${path}/..`}</span>}
-            </div>
-          </div>,
+          />,
           ...breakpoints.map(breakpoint => (
             <Breakpoint
               breakpoint={breakpoint}
               source={source}
               key={makeLocationId(breakpoint.selectedLocation)}
             />
           ))
         ];
@@ -125,12 +111,11 @@ class Breakpoints extends Component<Prop
 const mapStateToProps = state => ({
   breakpointSources: getBreakpointSources(state),
   selectedSource: getSelectedSource(state)
 });
 
 export default connect(
   mapStateToProps,
   {
-    pauseOnExceptions: actions.pauseOnExceptions,
-    selectSource: actions.selectSource
+    pauseOnExceptions: actions.pauseOnExceptions
   }
 )(Breakpoints);
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/moz.build
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/moz.build
@@ -4,12 +4,13 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
 
 ]
 
 DebuggerModules(
     'Breakpoint.js',
+    'BreakpointHeading.js',
     'BreakpointsContextMenu.js',
     'ExceptionOption.js',
     'index.js',
 )
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/WhyPaused.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/WhyPaused.js
@@ -43,14 +43,16 @@ export default function renderWhyPaused(
   const reason = getPauseReason(why);
 
   if (!reason) {
     return null;
   }
 
   return (
     <div className={"pane why-paused"}>
-      <div>{L10N.getStr(reason)}</div>
-      {renderMessage(why)}
+      <div>
+        <div>{L10N.getStr(reason)}</div>
+        {renderMessage(why)}
+      </div>
     </div>
   );
 }
 renderWhyPaused.displayName = "whyPaused";
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/XHRBreakpoints.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/XHRBreakpoints.js
@@ -34,28 +34,45 @@ type State = {
   editing: boolean,
   inputValue: string,
   inputMethod: string,
   editIndex: number,
   focused: boolean
 };
 
 class XHRBreakpoints extends Component<Props, State> {
+  _input: ?HTMLInputElement;
+
   constructor(props: Props) {
     super(props);
 
     this.state = {
       editing: false,
       inputValue: "",
       inputMethod: "",
       focused: false,
       editIndex: -1
     };
   }
 
+  componentDidUpdate(prevProps, prevState) {
+    const input = this._input;
+
+    if (!input) {
+      return;
+    }
+
+    if (!prevState.editing && this.state.editing) {
+      input.setSelectionRange(0, input.value.length);
+      input.focus();
+    } else if (this.props.showInput && !this.state.focused) {
+      input.focus();
+    }
+  }
+
   handleNewSubmit = (e: SyntheticEvent<HTMLFormElement>) => {
     e.preventDefault();
     e.stopPropagation();
 
     this.props.setXHRBreakpoint(this.state.inputValue, "ANY");
 
     this.hideInput();
   };
@@ -121,16 +138,17 @@ class XHRBreakpoints extends Component<P
             className="xhr-input"
             type="text"
             placeholder={placeholder}
             onChange={this.handleChange}
             onBlur={this.hideInput}
             onFocus={this.onFocus}
             autoFocus={showInput}
             value={inputValue}
+            ref={c => (this._input = c)}
           />
           <input type="submit" style={{ display: "none" }} />
         </form>
       </li>
     );
   }
   handleCheckbox = index => {
     const {
@@ -141,24 +159,33 @@ class XHRBreakpoints extends Component<P
     const breakpoint = xhrBreakpoints.get(index);
     if (breakpoint.disabled) {
       enableXHRBreakpoint(index);
     } else {
       disableXHRBreakpoint(index);
     }
   };
 
-  renderBreakpoint = ({ path, text, disabled, method }, index) => {
+  renderBreakpoint = breakpoint => {
+    const { path, text, disabled, method } = breakpoint;
     const { editIndex } = this.state;
-    const { removeXHRBreakpoint } = this.props;
+    const { removeXHRBreakpoint, xhrBreakpoints } = this.props;
+
+    // The "pause on any" checkbox
+    if (!path) {
+      return;
+    }
+
+    // Finds the xhrbreakpoint so as to not make assumptions about position
+    const index = xhrBreakpoints.findIndex(
+      bp => bp.path === path && bp.method === method
+    );
 
     if (index === editIndex) {
       return this.renderXHRInput(this.handleExistingSubmit);
-    } else if (!path) {
-      return;
     }
 
     return (
       <li
         className="xhr-container"
         key={path}
         title={path}
         onDoubleClick={(items, options) => this.editExpression(index)}
@@ -175,48 +202,53 @@ 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 !== "");
+
     return (
       <ul className="pane expressions-list">
-        {xhrBreakpoints.map(this.renderBreakpoint)}
-        {(showInput || !xhrBreakpoints.size) &&
+        {explicitXhrBreakpoints.map(this.renderBreakpoint)}
+        {(showInput || explicitXhrBreakpoints.size === 0) &&
           this.renderXHRInput(this.handleNewSubmit)}
       </ul>
     );
   };
 
-  renderCheckpoint = () => {
+  renderCheckbox = () => {
     const { shouldPauseOnAny, togglePauseOnAny, xhrBreakpoints } = this.props;
-    const isEmpty = xhrBreakpoints.size === 0;
+
     return (
       <div
         className={classnames("breakpoints-exceptions-options", {
-          empty: isEmpty
+          empty: xhrBreakpoints.size === 0
         })}
       >
         <ExceptionOption
           className="breakpoints-exceptions"
           label={L10N.getStr("pauseOnAnyXHR")}
           isChecked={shouldPauseOnAny}
           onChange={() => togglePauseOnAny()}
         />
       </div>
     );
   };
 
   render() {
     return (
       <div>
-        {this.renderCheckpoint()}
+        {this.renderCheckbox()}
         {this.renderBreakpoints()}
       </div>
     );
   }
 }
 
 const mapStateToProps = state => {
   return {
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/index.js
@@ -6,17 +6,17 @@
 
 import React, { Component } from "react";
 import { connect } from "react-redux";
 import { List } from "immutable";
 
 import actions from "../../actions";
 import {
   getTopFrame,
-  getBreakpoints,
+  getBreakpointsList,
   getBreakpointsDisabled,
   getBreakpointsLoading,
   getExpressions,
   getIsWaitingOnBreak,
   getShouldPauseOnExceptions,
   getShouldPauseOnCaughtExceptions,
   getWorkers,
   getExtra
@@ -113,17 +113,17 @@ class SecondaryPanes extends Component<P
       toggleAllBreakpoints,
       breakpoints,
       breakpointsDisabled,
       breakpointsLoading
     } = this.props;
     const isIndeterminate =
       !breakpointsDisabled && breakpoints.some(x => x.disabled);
 
-    if (features.skipPausing || breakpoints.size == 0) {
+    if (features.skipPausing || breakpoints.length === 0) {
       return null;
     }
 
     const inputProps = {
       type: "checkbox",
       "aria-label": breakpointsDisabled
         ? L10N.getStr("breakpoints.enable")
         : L10N.getStr("breakpoints.disable"),
@@ -433,17 +433,17 @@ class SecondaryPanes extends Component<P
     );
   }
 }
 
 const mapStateToProps = state => ({
   expressions: getExpressions(state),
   extra: getExtra(state),
   hasFrames: !!getTopFrame(state),
-  breakpoints: getBreakpoints(state),
+  breakpoints: getBreakpointsList(state),
   breakpointsDisabled: getBreakpointsDisabled(state),
   breakpointsLoading: getBreakpointsLoading(state),
   isWaitingOnBreak: getIsWaitingOnBreak(state),
   shouldPauseOnExceptions: getShouldPauseOnExceptions(state),
   shouldPauseOnCaughtExceptions: getShouldPauseOnCaughtExceptions(state),
   workers: getWorkers(state)
 });
 
--- a/devtools/client/debugger/new/src/components/shared/Badge.js
+++ b/devtools/client/debugger/new/src/components/shared/Badge.js
@@ -6,12 +6,12 @@
 import React from "react";
 import "./Badge.css";
 
 type Props = {
   children: number
 };
 
 const Badge = ({ children }: Props) => (
-  <div className="badge text-white text-center">{children}</div>
+  <span className="badge text-white text-center">{children}</span>
 );
 
 export default Badge;
--- a/devtools/client/debugger/new/src/reducers/breakpoints.js
+++ b/devtools/client/debugger/new/src/reducers/breakpoints.js
@@ -5,49 +5,45 @@
 // @flow
 
 /**
  * Breakpoints reducer
  * @module reducers/breakpoints
  */
 
 import * as I from "immutable";
-import makeRecord from "../utils/makeRecord";
 
 import { isGeneratedId } from "devtools-source-map";
 import { makeLocationId } from "../utils/breakpoint";
 
 import type { XHRBreakpoint, Breakpoint, Location } from "../types";
 import type { Action, DonePromiseAction } from "../actions/types";
-import type { Record } from "../utils/makeRecord";
 
-export type BreakpointsMap = I.Map<string, Breakpoint>;
+export type BreakpointsMap = { [string]: Breakpoint };
 export type XHRBreakpointsList = I.List<XHRBreakpoint>;
 
 export type BreakpointsState = {
   breakpoints: BreakpointsMap,
   xhrBreakpoints: XHRBreakpointsList
 };
 
 export function initialBreakpointsState(
   xhrBreakpoints?: any[] = []
-): Record<BreakpointsState> {
-  return makeRecord(
-    ({
-      breakpoints: I.Map(),
-      xhrBreakpoints: I.List(xhrBreakpoints),
-      breakpointsDisabled: false
-    }: BreakpointsState)
-  )();
+): BreakpointsState {
+  return {
+    breakpoints: {},
+    xhrBreakpoints: I.List(xhrBreakpoints),
+    breakpointsDisabled: false
+  };
 }
 
 function update(
-  state: Record<BreakpointsState> = initialBreakpointsState(),
+  state: BreakpointsState = initialBreakpointsState(),
   action: Action
-) {
+): BreakpointsState {
   switch (action.type) {
     case "ADD_BREAKPOINT": {
       return addBreakpoint(state, action);
     }
 
     case "SYNC_BREAKPOINT": {
       return syncBreakpoint(state, action);
     }
@@ -113,183 +109,225 @@ function addXHRBreakpoint(state, action)
   const { breakpoint } = action;
   const { path, method } = breakpoint;
 
   const existingBreakpointIndex = state.xhrBreakpoints.findIndex(
     bp => bp.path === path && bp.method === method
   );
 
   if (existingBreakpointIndex === -1) {
-    return state.set("xhrBreakpoints", xhrBreakpoints.push(breakpoint));
+    return {
+      ...state,
+      xhrBreakpoints: xhrBreakpoints.push(breakpoint)
+    };
   } else if (xhrBreakpoints.get(existingBreakpointIndex) !== breakpoint) {
-    return state.set(
-      "xhrBreakpoints",
-      xhrBreakpoints.set(existingBreakpointIndex, breakpoint)
-    );
+    return {
+      ...state,
+      xhrBreakpoints: xhrBreakpoints.set(existingBreakpointIndex, breakpoint)
+    };
   }
 
   return state;
 }
 
 function removeXHRBreakpoint(state, action) {
   const {
     breakpoint: { path, method }
   } = action;
   const { xhrBreakpoints } = state;
 
+  if (action.status === "start") {
+    return state;
+  }
+
   const index = xhrBreakpoints.findIndex(
     bp => bp.path === path && bp.method === method
   );
 
-  return state.set("xhrBreakpoints", xhrBreakpoints.delete(index));
+  return {
+    ...state,
+    xhrBreakpoints: xhrBreakpoints.delete(index)
+  };
 }
 
 function updateXHRBreakpoint(state, action) {
   const { breakpoint, index } = action;
   const { xhrBreakpoints } = state;
-  return state.set("xhrBreakpoints", xhrBreakpoints.set(index, breakpoint));
+  return {
+    ...state,
+    xhrBreakpoints: xhrBreakpoints.set(index, breakpoint)
+  };
 }
 
-function addBreakpoint(state, action) {
+function setBreakpoint(state, locationId, breakpoint) {
+  return {
+    ...state,
+    breakpoints: { ...state.breakpoints, [locationId]: breakpoint }
+  };
+}
+
+function unsetBreakpoint(state, locationId) {
+  const breakpoints = { ...state.breakpoints };
+  delete breakpoints[locationId];
+  return {
+    ...state,
+    breakpoints: { ...breakpoints }
+  };
+}
+
+function addBreakpoint(state, action): BreakpointsState {
   if (action.status === "start" && action.breakpoint) {
     const { breakpoint } = action;
     const locationId = makeLocationId(breakpoint.location);
-    return state.setIn(["breakpoints", locationId], breakpoint);
+    return setBreakpoint(state, locationId, breakpoint);
   }
 
   // when the action completes, we can commit the breakpoint
   if (action.status === "done") {
     const { value } = ((action: any): DonePromiseAction);
     return syncBreakpoint(state, value);
   }
 
   // Remove the optimistic update
   if (action.status === "error" && action.breakpoint) {
     const locationId = makeLocationId(action.breakpoint.location);
-    return state.deleteIn(["breakpoints", locationId]);
+    return unsetBreakpoint(state, locationId);
   }
 
   return state;
 }
 
-function syncBreakpoint(state, data) {
+function syncBreakpoint(state, data): BreakpointsState {
   const { breakpoint, previousLocation } = data;
 
   if (previousLocation) {
-    state = state.deleteIn(["breakpoints", makeLocationId(previousLocation)]);
+    state = {
+      ...state,
+      breakpoints: { ...state.breakpoints }
+    };
+    delete state.breakpoints[makeLocationId(previousLocation)];
   }
 
   if (!breakpoint) {
     return state;
   }
 
   const locationId = makeLocationId(breakpoint.location);
-  return state.setIn(["breakpoints", locationId], breakpoint);
+  return setBreakpoint(state, locationId, breakpoint);
 }
 
-function updateBreakpoint(state, action) {
+function updateBreakpoint(state, action): BreakpointsState {
   const { breakpoint } = action;
   const locationId = makeLocationId(breakpoint.location);
-  return state.setIn(["breakpoints", locationId], breakpoint);
+  return setBreakpoint(state, locationId, breakpoint);
 }
 
-function updateAllBreakpoints(state, action) {
+function updateAllBreakpoints(state, action): BreakpointsState {
   const { breakpoints } = action;
+  state = {
+    ...state,
+    breakpoints: { ...state.breakpoints }
+  };
   breakpoints.forEach(breakpoint => {
     const locationId = makeLocationId(breakpoint.location);
-    state = state.setIn(["breakpoints", locationId], breakpoint);
+    state.breakpoints[locationId] = breakpoint;
   });
   return state;
 }
 
-function remapBreakpoints(state, action) {
+function remapBreakpoints(state, action): BreakpointsState {
   const breakpoints = action.breakpoints.reduce(
     (updatedBreakpoints, breakpoint) => {
       const locationId = makeLocationId(breakpoint.location);
       return { ...updatedBreakpoints, [locationId]: breakpoint };
     },
     {}
   );
 
-  return state.set("breakpoints", I.Map(breakpoints));
+  return { ...state, breakpoints };
 }
 
-function removeBreakpoint(state, action) {
+function removeBreakpoint(state, action): BreakpointsState {
   const { breakpoint } = action;
   const id = makeLocationId(breakpoint.location);
-  return state.deleteIn(["breakpoints", id]);
+  return unsetBreakpoint(state, id);
 }
 
 // Selectors
 // TODO: these functions should be moved out of the reducer
 
-type OuterState = { breakpoints: Record<BreakpointsState> };
+type OuterState = { breakpoints: BreakpointsState };
+
+export function getBreakpointsMap(state: OuterState): BreakpointsMap {
+  return state.breakpoints.breakpoints;
+}
 
-export function getBreakpoints(state: OuterState) {
-  return state.breakpoints.breakpoints;
+export function getBreakpointsList(state: OuterState): Breakpoint[] {
+  return (Object.values(getBreakpointsMap(state)): any);
+}
+
+export function getBreakpointCount(state: OuterState): number {
+  return getBreakpointsList(state).length;
 }
 
 export function getBreakpoint(
   state: OuterState,
   location: Location
-): Breakpoint {
-  const breakpoints = getBreakpoints(state);
-  return breakpoints.get(makeLocationId(location));
+): ?Breakpoint {
+  const breakpoints = getBreakpointsMap(state);
+  return breakpoints[makeLocationId(location)];
 }
 
 export function getBreakpointsDisabled(state: OuterState): boolean {
-  return state.breakpoints.breakpoints.every(x => x.disabled);
+  const breakpoints = getBreakpointsList(state);
+  return breakpoints.every(breakpoint => breakpoint.disabled);
 }
 
-export function getBreakpointsLoading(state: OuterState) {
-  const breakpoints = getBreakpoints(state);
-  const isLoading = !!breakpoints
-    .valueSeq()
-    .filter(bp => bp.loading)
-    .first();
-
-  return breakpoints.size > 0 && isLoading;
+export function getBreakpointsLoading(state: OuterState): boolean {
+  const breakpoints = getBreakpointsList(state);
+  const isLoading = breakpoints.some(breakpoint => breakpoint.loading);
+  return breakpoints.length > 0 && isLoading;
 }
 
-export function getBreakpointsForSource(state: OuterState, sourceId: string) {
+export function getBreakpointsForSource(
+  state: OuterState,
+  sourceId: string
+): Breakpoint[] {
   if (!sourceId) {
-    return I.Map();
+    return [];
   }
 
   const isGeneratedSource = isGeneratedId(sourceId);
-  const breakpoints = getBreakpoints(state);
-
+  const breakpoints = getBreakpointsList(state);
   return breakpoints.filter(bp => {
     const location = isGeneratedSource
       ? bp.generatedLocation || bp.location
       : bp.location;
     return location.sourceId === sourceId;
   });
 }
 
 export function getBreakpointForLine(
   state: OuterState,
   sourceId: string,
   line: number | null
 ): ?Breakpoint {
   if (!sourceId) {
-    return I.Map();
+    return undefined;
   }
-  const breakpoints = getBreakpointsForSource(state, sourceId);
+  const breakpoints = getBreakpointsList(state);
   return breakpoints.find(breakpoint => breakpoint.location.line === line);
 }
 
-export function getHiddenBreakpoint(state: OuterState) {
-  return getBreakpoints(state)
-    .valueSeq()
-    .filter(breakpoint => breakpoint.hidden)
-    .first();
+export function getHiddenBreakpoint(state: OuterState): ?Breakpoint {
+  const breakpoints = getBreakpointsList(state);
+  return breakpoints.find(bp => bp.hidden);
 }
 
-export function getHiddenBreakpointLocation(state: OuterState) {
+export function getHiddenBreakpointLocation(state: OuterState): ?Location {
   const hiddenBreakpoint = getHiddenBreakpoint(state);
   if (!hiddenBreakpoint) {
     return null;
   }
   return hiddenBreakpoint.location;
 }
 
 export default update;
--- a/devtools/client/debugger/new/src/reducers/sources.js
+++ b/devtools/client/debugger/new/src/reducers/sources.js
@@ -435,16 +435,24 @@ export function getSourcesUrlsInSources(
   const urls = getUrls(state);
   if (!url || !urls[url]) {
     return [];
   }
 
   return [...new Set(Object.keys(urls).filter(Boolean))];
 }
 
+export function getHasSiblingOfSameName(state: OuterState, source: ?Source) {
+  if (!source) {
+    return false;
+  }
+
+  return getSourcesUrlsInSources(state, source.url).length > 1;
+}
+
 export function getSourceInSources(sources: SourcesMap, id: string): ?Source {
   return sources[id];
 }
 
 export function getSources(state: OuterState) {
   return state.sources.sources;
 }
 
--- a/devtools/client/debugger/new/src/selectors/breakpointAtLocation.js
+++ b/devtools/client/debugger/new/src/selectors/breakpointAtLocation.js
@@ -1,32 +1,35 @@
 /* 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/>. */
 
 import { getSelectedSource } from "../reducers/sources";
-import { getBreakpoints } from "../reducers/breakpoints";
+import { getBreakpointsList } from "../reducers/breakpoints";
 import { isGenerated } from "../utils/source";
 
 function getColumn(column, selectedSource) {
   if (column) {
     return column;
   }
 
   return isGenerated(selectedSource) ? undefined : 0;
 }
 
 function getLocation(bp, selectedSource) {
   return isGenerated(selectedSource)
     ? bp.generatedLocation || bp.location
     : bp.location;
 }
 
-function getBreakpointsForSource(state: OuterState, selectedSource: Source) {
-  const breakpoints = getBreakpoints(state);
+function getBreakpointsForSource(
+  state: OuterState,
+  selectedSource: Source
+): Breakpoint[] {
+  const breakpoints = getBreakpointsList(state);
 
   return breakpoints.filter(bp => {
     const location = getLocation(bp, selectedSource);
     return location.sourceId === selectedSource.id;
   });
 }
 
 function findBreakpointAtLocation(
--- a/devtools/client/debugger/new/src/selectors/breakpointSources.js
+++ b/devtools/client/debugger/new/src/selectors/breakpointSources.js
@@ -1,22 +1,26 @@
 /* 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, getSelectedSource } from "../selectors";
+import {
+  getSources,
+  getBreakpointsList,
+  getSelectedSource
+} from "../selectors";
 import { isGenerated, getFilename } from "../utils/source";
 import { getSelectedLocation } from "../utils/source-maps";
 
 import type { Source, Breakpoint, Location } from "../types";
-import type { SourcesMap, BreakpointsMap } from "../reducers/types";
+import type { SourcesMap } from "../reducers/types";
 
 export type BreakpointSources = Array<{
   source: Source,
   breakpoints: FormattedBreakpoint[]
 }>;
 
 export type FormattedBreakpoint = {|
   condition: ?string,
@@ -40,56 +44,49 @@ function formatBreakpoint(
         : 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))
+  breakpoints: Breakpoint[]
+) {
+  return breakpoints
+    .sort((a, b) => a.location.line - b.location.line)
     .filter(
       bp =>
-        bp.selectedLocation.sourceId == source.id &&
         !bp.hidden &&
         !bp.loading &&
         (bp.text || bp.originalText || bp.condition || bp.disabled)
     )
-    .sortBy(bp => bp.selectedLocation.line)
-    .toJS();
+    .map(bp => formatBreakpoint(bp, selectedSource))
+    .filter(bp => bp.selectedLocation.sourceId == source.id);
 }
 
 function findBreakpointSources(
   sources: SourcesMap,
   selectedSource: Source,
-  breakpoints: BreakpointsMap
+  breakpoints: Breakpoint[]
 ): Source[] {
-  const sourceIds: string[] = uniq(
-    breakpoints
-      .valueSeq()
-      .map(bp => getSelectedLocation(bp, selectedSource).sourceId)
-      .toJS()
-  );
+  const sourceIds: string[] = uniq(breakpoints.map(bp => bp.location.sourceId));
 
   const breakpointSources = sourceIds
     .map(id => sources[id])
     .filter(source => source && !source.isBlackBoxed);
 
   return sortBy(breakpointSources, (source: Source) => getFilename(source));
 }
 
 export const getBreakpointSources = createSelector(
-  getBreakpoints,
+  getBreakpointsList,
   getSources,
   getSelectedSource,
-  (breakpoints: BreakpointsMap, sources: SourcesMap, selectedSource: Source) =>
+  (breakpoints: Breakpoint[], sources: SourcesMap, selectedSource: Source) =>
     findBreakpointSources(sources, selectedSource, breakpoints)
       .map(source => ({
         source,
         breakpoints: getBreakpointsForSource(
           source,
           selectedSource,
           breakpoints
         )
--- a/devtools/client/debugger/new/src/selectors/breakpoints.js
+++ b/devtools/client/debugger/new/src/selectors/breakpoints.js
@@ -2,19 +2,18 @@
  * 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 { createSelector } from "reselect";
 
 import type { BreakpointsState } from "../reducers/breakpoints";
-import type { Record } from "../utils/makeRecord";
 
-type OuterState = { breakpoints: Record<BreakpointsState> };
+type OuterState = { breakpoints: BreakpointsState };
 
 export function getXHRBreakpoints(state: OuterState) {
   return state.breakpoints.xhrBreakpoints;
 }
 
 export const shouldPauseOnAnyXHR = createSelector(
   getXHRBreakpoints,
   xhrBreakpoints => {
--- a/devtools/client/debugger/new/src/selectors/visibleBreakpoints.js
+++ b/devtools/client/debugger/new/src/selectors/visibleBreakpoints.js
@@ -1,22 +1,21 @@
 /* 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 { getBreakpoints } from "../reducers/breakpoints";
+import { getBreakpointsList } from "../reducers/breakpoints";
 import { getSelectedSource } from "../reducers/sources";
 import { isGeneratedId } from "devtools-source-map";
 import { createSelector } from "reselect";
 import memoize from "../utils/memoize";
 
-import type { BreakpointsMap } from "../reducers/types";
-import type { Source } from "../types";
+import type { Breakpoint, Source } from "../types";
 
 function getLocation(breakpoint, isGeneratedSource) {
   return isGeneratedSource
     ? breakpoint.generatedLocation || breakpoint.location
     : breakpoint.location;
 }
 
 const formatBreakpoint = memoize(function(breakpoint, selectedSource) {
@@ -41,18 +40,18 @@ function isVisible(breakpoint, selectedS
   return location.sourceId === sourceId;
 }
 
 /*
  * Finds the breakpoints, which appear in the selected source.
   */
 export const getVisibleBreakpoints = createSelector(
   getSelectedSource,
-  getBreakpoints,
-  (selectedSource: Source, breakpoints: BreakpointsMap) => {
+  getBreakpointsList,
+  (selectedSource: Source, breakpoints: Breakpoint[]) => {
     if (!selectedSource) {
       return null;
     }
 
     return breakpoints
       .filter(bp => isVisible(bp, selectedSource))
       .map(bp => formatBreakpoint(bp, selectedSource));
   }
--- a/devtools/client/debugger/new/src/utils/prefs.js
+++ b/devtools/client/debugger/new/src/utils/prefs.js
@@ -24,16 +24,18 @@ if (isDevelopment()) {
   pref("devtools.debugger.scopes-visible", true);
   pref("devtools.debugger.component-visible", true);
   pref("devtools.debugger.workers-visible", true);
   pref("devtools.debugger.expressions-visible", true);
   pref("devtools.debugger.xhr-breakpoints-visible", true);
   pref("devtools.debugger.breakpoints-visible", true);
   pref("devtools.debugger.start-panel-collapsed", false);
   pref("devtools.debugger.end-panel-collapsed", false);
+  pref("devtools.debugger.start-panel-size", 300);
+  pref("devtools.debugger.end-panel-size", 300);
   pref("devtools.debugger.tabs", "[]");
   pref("devtools.debugger.tabsBlackBoxed", "[]");
   pref("devtools.debugger.ui.framework-grouping-on", true);
   pref("devtools.debugger.pending-selected-location", "{}");
   pref("devtools.debugger.pending-breakpoints", "{}");
   pref("devtools.debugger.expressions", "[]");
   pref("devtools.debugger.file-search-case-sensitive", false);
   pref("devtools.debugger.file-search-whole-word", false);
@@ -47,17 +49,17 @@ if (isDevelopment()) {
   pref("devtools.debugger.features.shortcuts", true);
   pref("devtools.debugger.features.root", true);
   pref("devtools.debugger.features.map-scopes", true);
   pref("devtools.debugger.features.remove-command-bar-options", true);
   pref("devtools.debugger.features.code-coverage", false);
   pref("devtools.debugger.features.event-listeners", false);
   pref("devtools.debugger.features.code-folding", false);
   pref("devtools.debugger.features.outline", true);
-  pref("devtools.debugger.features.column-breakpoints", false);
+  pref("devtools.debugger.features.column-breakpoints", true);
   pref("devtools.debugger.features.pause-points", true);
   pref("devtools.debugger.features.skip-pausing", true);
   pref("devtools.debugger.features.component-pane", false);
   pref("devtools.debugger.features.autocomplete-expressions", false);
   pref("devtools.debugger.features.map-expression-bindings", true);
   pref("devtools.debugger.features.map-await-expression", true);
   pref("devtools.debugger.features.xhr-breakpoints", true);
 }
@@ -73,16 +75,18 @@ export const prefs = new PrefsHelper("de
   scopesVisible: ["Bool", "debugger.scopes-visible"],
   componentVisible: ["Bool", "debugger.component-visible"],
   workersVisible: ["Bool", "debugger.workers-visible"],
   breakpointsVisible: ["Bool", "debugger.breakpoints-visible"],
   expressionsVisible: ["Bool", "debugger.expressions-visible"],
   xhrBreakpointsVisible: ["Bool", "debugger.xhr-breakpoints-visible"],
   startPanelCollapsed: ["Bool", "debugger.start-panel-collapsed"],
   endPanelCollapsed: ["Bool", "debugger.end-panel-collapsed"],
+  startPanelSize: ["Int", "debugger.start-panel-size"],
+  endPanelSize: ["Int", "debugger.end-panel-size"],
   frameworkGroupingOn: ["Bool", "debugger.ui.framework-grouping-on"],
   tabs: ["Json", "debugger.tabs", []],
   tabsBlackBoxed: ["Json", "debugger.tabsBlackBoxed", []],
   pendingSelectedLocation: ["Json", "debugger.pending-selected-location", {}],
   pendingBreakpoints: ["Json", "debugger.pending-breakpoints", {}],
   expressions: ["Json", "debugger.expressions", []],
   fileSearchCaseSensitive: ["Bool", "debugger.file-search-case-sensitive"],
   fileSearchWholeWord: ["Bool", "debugger.file-search-whole-word"],
--- a/devtools/client/debugger/new/src/utils/source.js
+++ b/devtools/client/debugger/new/src/utils/source.js
@@ -166,18 +166,22 @@ export function getFilename(source: Sour
 }
 
 /**
  * Provides a middle-trunated filename
  *
  * @memberof utils/source
  * @static
  */
-export function getTruncatedFileName(source: Source, length: number = 30) {
-  return truncateMiddleText(getFilename(source), length);
+export function getTruncatedFileName(
+  source: Source,
+  querystring: string = "",
+  length: number = 30
+) {
+  return truncateMiddleText(`${getFilename(source)}${querystring}`, length);
 }
 
 /* Gets path for files with same filename for editor tabs, breakpoints, etc.
  * Pass the source, and list of other sources
  *
  * @memberof utils/source
  * @static
  */
@@ -451,8 +455,12 @@ export function isOriginal(source: Sourc
   // Pretty-printed sources are given original IDs, so no need
   // for any additional check
   return isOriginalId(source.id);
 }
 
 export function isGenerated(source: Source) {
   return isGeneratedId(source.id);
 }
+
+export function getSourceQueryString(source: ?Source) {
+  return source ? parseURL(source.url).search : "";
+}
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
@@ -11,11 +11,11 @@ async function removeBreakpoint(dbg) {
 add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html");
   await selectSource(dbg, "simple2");
   await waitForSelectedSource(dbg, "simple2");
 
   await addBreakpoint(dbg, "simple2", 3);
   await removeBreakpoint(dbg);
 
-  await waitForState(dbg, state => dbg.selectors.getBreakpoints(state).size == 0);
+  await waitForState(dbg, state => dbg.selectors.getBreakpointCount(state) === 0);
   ok("successfully removed the breakpoint")
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints.js
@@ -39,24 +39,16 @@ function findBreakpoint(dbg, url, line) 
   const {
     selectors: { getBreakpoint },
     getState
   } = dbg;
   const source = findSource(dbg, url);
   return getBreakpoint(getState(), { sourceId: source.id, line });
 }
 
-function findBreakpoints(dbg) {
-  const {
-    selectors: { getBreakpoints },
-    getState
-  } = dbg;
-  return getBreakpoints(getState());
-}
-
 add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html");
 
   // Create two breakpoints
   await selectSource(dbg, "simple2");
   await addBreakpoint(dbg, "simple2", 3);
   await addBreakpoint(dbg, "simple2", 5);
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-gutter.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-gutter.js
@@ -23,35 +23,35 @@ function assertEditorBreakpoint(dbg, lin
       " on line " +
       line
   );
 }
 
 add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html");
   const {
-    selectors: { getBreakpoints, getBreakpoint },
+    selectors: { getBreakpoint, getBreakpointCount },
     getState
   } = dbg;
   const source = findSource(dbg, "simple1.js");
 
   await selectSource(dbg, source.url);
 
   // Make sure that clicking the gutter creates a breakpoint icon.
   clickGutter(dbg, 4);
   await waitForDispatch(dbg, "ADD_BREAKPOINT");
-  is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
+  is(getBreakpointCount(getState()), 1, "One breakpoint exists");
   assertEditorBreakpoint(dbg, 4, true);
 
   // Make sure clicking at the same place removes the icon.
   clickGutter(dbg, 4);
   await waitForDispatch(dbg, "REMOVE_BREAKPOINT");
-  is(getBreakpoints(getState()).size, 0, "No breakpoints exist");
+  is(getBreakpointCount(getState()), 0, "No breakpoints exist");
   assertEditorBreakpoint(dbg, 4, false);
 
   // Ensure that clicking the gutter removes all breakpoints on a given line
   await addBreakpoint(dbg, source, 4, 0);
   await addBreakpoint(dbg, source, 4, 1);
   await addBreakpoint(dbg, source, 4, 2);
   clickGutter(dbg, 4);
-  await waitForState(dbg, state => dbg.selectors.getBreakpoints(state).size == 0);
+  await waitForState(dbg, state => dbg.selectors.getBreakpointCount(state) === 0);
   assertEditorBreakpoint(dbg, 4, false);
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-reload.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-reload.js
@@ -27,15 +27,14 @@ add_task(async function() {
   await reload(dbg, "sjs_code_reload.sjs");
   await waitForSelectedSource(dbg, "sjs_code_reload.sjs");
 
   const source = findSource(dbg, "sjs_code_reload");
   const location = { sourceId: source.id, line: 6 };
 
   await waitForBreakpoint(dbg, location);
 
-  const breakpoints = dbg.selectors.getBreakpoints(dbg.getState());
-  const breakpointList = breakpoints.valueSeq().toJS();
+  const breakpointList = dbg.selectors.getBreakpointsList(dbg.getState());
   const breakpoint = breakpointList[0];
 
   is(breakpointList.length, 1);
   is(breakpoint.location.line, 6);
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps-reload.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps-reload.js
@@ -13,18 +13,17 @@ async function waitForBreakpoint(dbg, lo
     state => {
       return dbg.selectors.getBreakpoint(dbg.getState(), location);
     },
     "Waiting for breakpoint"
   );
 }
 
 function getBreakpoints(dbg) {
-  const breakpoints = dbg.selectors.getBreakpoints(dbg.getState());
-  return breakpoints.valueSeq().toJS();
+  return dbg.selectors.getBreakpointsList(dbg.getState());
 }
 
 add_task(async function() {
   const dbg = await initDebugger("doc-minified.html");
 
   await navigate(dbg, "sourcemaps-reload/doc-sourcemaps-reload.html", "v1.js");
 
   await selectSource(dbg, "v1.js");
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps-reloading.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps-reloading.js
@@ -1,24 +1,24 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 requestLongerTimeout(2);
 
 async function waitForBreakpointCount(dbg, count) {
   return waitForState(
     dbg,
-    state => dbg.selectors.getBreakpoints(state).size === count
+    state => dbg.selectors.getBreakpointCount(state) === count
   );
 }
 
 add_task(async function() {
   // NOTE: the CORS call makes the test run times inconsistent
   const dbg = await initDebugger("doc-sourcemaps.html");
   const {
-    selectors: { getBreakpoint, getBreakpoints },
+    selectors: { getBreakpoint, getBreakpointCount },
     getState
   } = dbg;
 
   await waitForSources(dbg, "entry.js", "output.js", "times2.js", "opts.js");
   ok(true, "Original sources exist");
   const entrySrc = findSource(dbg, "entry.js");
 
   await selectSource(dbg, entrySrc);
@@ -27,17 +27,17 @@ add_task(async function() {
       .getValue()
       .includes("window.keepMeAlive"),
     "Original source text loaded correctly"
   );
 
   // Test that breakpoint sliding is not attempted. The breakpoint
   // should not move anywhere.
   await addBreakpoint(dbg, entrySrc, 13);
-  is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
+  is(getBreakpointCount(getState()), 1, "One breakpoint exists");
 
   ok(
     getBreakpoint(getState(), { sourceId: entrySrc.id, line: 13 }),
     "Breakpoint has correct line"
   );
 
   await addBreakpoint(dbg, entrySrc, 5);
 
@@ -47,17 +47,17 @@ add_task(async function() {
   // Test reloading the debugger
   await reload(dbg, "opts.js");
   await waitForDispatch(dbg, "LOAD_SOURCE_TEXT");
 
   await waitForPaused(dbg);
   assertPausedLocation(dbg);
 
   await waitForBreakpointCount(dbg, 3);
-  is(getBreakpoints(getState()).size, 3, "Three breakpoints exist");
+  is(getBreakpointCount(getState()), 3, "Three breakpoints exist");
 
   ok(
     getBreakpoint(getState(), { sourceId: entrySrc.id, line: 13 }),
     "Breakpoint has correct line"
   );
 
   ok(
     getBreakpoint(getState(), {
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps.js
@@ -36,47 +36,47 @@ function getLineEl(dbg, line) {
 function clickGutter(dbg, line) {
   clickElement(dbg, "gutter", line);
 }
 
 add_task(async function() {
   // NOTE: the CORS call makes the test run times inconsistent
   const dbg = await initDebugger("doc-sourcemaps.html");
   const {
-    selectors: { getBreakpoint, getBreakpoints },
+    selectors: { getBreakpoint, getBreakpointCount },
     getState
   } = dbg;
 
   await waitForSources(dbg, "entry.js", "output.js", "times2.js", "opts.js");
   ok(true, "Original sources exist");
   const bundleSrc = findSource(dbg, "bundle.js");
 
   await selectSource(dbg, bundleSrc);
 
   await clickGutter(dbg, 13);
   await waitForDispatch(dbg, "ADD_BREAKPOINT");
   assertEditorBreakpoint(dbg, 13, true);
 
   await clickGutter(dbg, 13);
   await waitForDispatch(dbg, "REMOVE_BREAKPOINT");
-  is(getBreakpoints(getState()).size, 0, "No breakpoints exists");
+  is(getBreakpointCount(getState()), 0, "No breakpoints exists");
 
   const entrySrc = findSource(dbg, "entry.js");
 
   await selectSource(dbg, entrySrc);
   ok(
     getCM(dbg)
       .getValue()
       .includes("window.keepMeAlive"),
     "Original source text loaded correctly"
   );
 
   // Test breaking on a breakpoint
   await addBreakpoint(dbg, "entry.js", 15);
-  is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
+  is(getBreakpointCount(getState()), 1, "One breakpoint exists");
   assertBreakpointExists(dbg, entrySrc, 15);
 
   invokeInTab("keepMeAlive");
   await waitForPaused(dbg);
   assertPausedLocation(dbg);
 
   await stepIn(dbg);
   await stepIn(dbg);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js
@@ -15,30 +15,30 @@ function assertBpInGutter(dbg, lineNumbe
 // Tests loading sourcemapped sources, setting breakpoints, and
 // stepping in them.
 
 // This source map does not have source contents, so it's fetched separately
 add_task(async function() {
   // NOTE: the CORS call makes the test run times inconsistent
   const dbg = await initDebugger("doc-sourcemaps2.html");
   const {
-    selectors: { getBreakpoint, getBreakpoints },
+    selectors: { getBreakpoint, getBreakpointCount },
     getState
   } = dbg;
 
   await waitForSources(dbg, "main.js", "main.min.js");
 
   ok(true, "Original sources exist");
   const mainSrc = findSource(dbg, "main.js");
 
   await selectSource(dbg, mainSrc);
 
   // Test that breakpoint is not off by a line.
   await addBreakpoint(dbg, mainSrc, 4);
-  is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
+  is(getBreakpointCount(getState()), 1, "One breakpoint exists");
   ok(
     getBreakpoint(getState(), { sourceId: mainSrc.id, line: 4, column: 2 }),
     "Breakpoint has correct line"
   );
 
   assertBpInGutter(dbg, 4);
   invokeInTab("logMessage");
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps3.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps3.js
@@ -7,30 +7,30 @@ requestLongerTimeout(2);
 
 // This source map does not have source contents, so it's fetched separately
 add_task(async function() {
   // NOTE: the CORS call makes the test run times inconsistent
   await pushPref("devtools.debugger.features.map-scopes", true);
 
   const dbg = await initDebugger("doc-sourcemaps3.html");
   const {
-    selectors: { getBreakpoint, getBreakpoints },
+    selectors: { getBreakpoint, getBreakpointCount },
     getState
   } = dbg;
 
   await waitForSources(dbg, "bundle.js", "sorted.js", "test.js");
 
   ok(true, "Original sources exist");
   const sortedSrc = findSource(dbg, "sorted.js");
 
   await selectSource(dbg, sortedSrc);
 
   // Test that breakpoint is not off by a line.
   await addBreakpoint(dbg, sortedSrc, 9);
-  is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
+  is(getBreakpointCount(getState()), 1, "One breakpoint exists");
   ok(
     getBreakpoint(getState(), { sourceId: sortedSrc.id, line: 9, column: 4 }),
     "Breakpoint has correct line"
   );
 
   invokeInTab("test");
 
   await waitForPaused(dbg);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sources-querystring.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sources-querystring.js
@@ -22,10 +22,22 @@ add_task(async function() {
   await assertSourceCount(dbg, 2);
   await clickElement(dbg, "sourceDirectoryLabel", 2);
 
   const labels = [getLabel(dbg, 4), getLabel(dbg, 3)];
   is(
     labels.includes("simple1.js?x=1") && labels.includes("simple1.js?x=2"),
     true,
     "simple1.js?x=1 and simple2.jsx=2 exist"
-  )
+  );
+
+  const source = findSource(dbg, "simple1.js?x=1");
+  await selectSource(dbg, source);
+  const tab = findElement(dbg, "activeTab");
+  is(tab.innerText, "simple1.js?x=1", "Tab label is simple1.js?x=1");
+  await addBreakpoint(dbg, "simple1.js?x=1", 6);
+  const breakpointHeading = findElement(dbg, "breakpointItem", 2).innerText;
+  is(
+    breakpointHeading,
+    "simple1.js?x=1",
+    "Breakpoint heading is simple1.js?x=1"
+  );
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-xhr-breakpoints.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-xhr-breakpoints.js
@@ -1,47 +1,125 @@
 /* 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/>. */
 
+ async function addXHRBreakpoint(dbg, text) {
+  info("Adding a XHR breakpoint");
+
+  const plusIcon = findElementWithSelector(dbg, ".xhr-breakpoints-pane .plus");
+  if (plusIcon) {
+    plusIcon.click();
+  }
+  findElementWithSelector(dbg, ".xhr-input").focus();
+  type(dbg, text);
+  pressKey(dbg, "Enter");
+
+  await waitForDispatch(dbg, "SET_XHR_BREAKPOINT");
+}
+
+async function removeXHRBreakpoint(dbg, index) {
+  info("Removing a XHR breakpoint");
+
+  const closeButtons = dbg.win.document.querySelectorAll(".xhr-breakpoints-pane .close-btn");
+  if (closeButtons[index]) {
+    closeButtons[index].click();
+  }
+
+  await waitForDispatch(dbg, "REMOVE_XHR_BREAKPOINT");
+}
+
+function getXHRBreakpointsElements(dbg) {
+  return [...dbg.win.document.querySelectorAll(".xhr-breakpoints-pane .xhr-container")];
+}
+
+function getXHRBreakpointLabels(elements) {
+  return elements.map(element => element.title);
+}
+
+function getXHRBreakpointCheckbox(dbg) {
+  return findElementWithSelector(dbg, ".xhr-breakpoints-pane .breakpoints-exceptions input");
+}
+
+async function clickPauseOnAny(dbg, expectedEvent) {
+  getXHRBreakpointCheckbox(dbg).click();
+  await waitForDispatch(dbg, expectedEvent);
+}
+
 // Tests that a basic XHR breakpoint works for get and POST is ignored
 add_task(async function() {
-    const dbg = await initDebugger("doc-xhr.html");
-    await waitForSources(dbg, "fetch.js");
-    await dbg.actions.setXHRBreakpoint("doc", "GET");
-    invokeInTab("main", "doc-xhr.html");
-    await waitForPaused(dbg);
-    assertPausedLocation(dbg);
-    resume(dbg);
+  const dbg = await initDebugger("doc-xhr.html");
+  await waitForSources(dbg, "fetch.js");
+  await dbg.actions.setXHRBreakpoint("doc", "GET");
+  invokeInTab("main", "doc-xhr.html");
+  await waitForPaused(dbg);
+  assertPausedLocation(dbg);
+  resume(dbg);
 
-    await dbg.actions.removeXHRBreakpoint(0);
-    invokeInTab("main", "doc-xhr.html");
-    assertNotPaused(dbg);
+  await dbg.actions.removeXHRBreakpoint(0);
+  invokeInTab("main", "doc-xhr.html");
+  assertNotPaused(dbg);
 
-    await dbg.actions.setXHRBreakpoint("doc-xhr.html", "POST");
-    invokeInTab("main", "doc");
-    assertNotPaused(dbg);
+  await dbg.actions.setXHRBreakpoint("doc-xhr.html", "POST");
+  invokeInTab("main", "doc");
+  assertNotPaused(dbg);
 });
 
 // Tests the "pause on any URL" checkbox works properly
 add_task(async function() {
-    const dbg = await initDebugger("doc-xhr.html");
-    await waitForSources(dbg, "fetch.js");
+  const dbg = await initDebugger("doc-xhr.html");
+  await waitForSources(dbg, "fetch.js");
+
+  info("HERE 1");
+
+  // Enable pause on any URL
+  await clickPauseOnAny(dbg, "SET_XHR_BREAKPOINT");
+
+  invokeInTab("main", "doc-xhr.html");
+  await waitForPaused(dbg);
+  await resume(dbg);
 
-    // Enable pause on any URL
-    await dbg.actions.togglePauseOnAny();
-    invokeInTab("main", "doc-xhr.html");
-    await waitForPaused(dbg);
-    await resume(dbg);
+  invokeInTab("main", "fetch.js");
+  await waitForPaused(dbg);
+  await resume(dbg);
+
+  invokeInTab("main", "README.md");
+  await waitForPaused(dbg);
+  await resume(dbg);
+
+  // Disable pause on any URL
+  await clickPauseOnAny(dbg, "DISABLE_XHR_BREAKPOINT");
+  info("HERE 4");
+  invokeInTab("main", "README.md");
+  assertNotPaused(dbg);
 
-    invokeInTab("main", "fetch.js");
-    await waitForPaused(dbg);
-    await resume(dbg);
+  // Turn off the checkbox
+  await dbg.actions.removeXHRBreakpoint(0);
+});
+
+// Tests removal works properly
+add_task(async function() {
+  const dbg = await initDebugger("doc-xhr.html");
+
+  const pauseOnAnyCheckbox = getXHRBreakpointCheckbox(dbg);
+
+  await clickPauseOnAny(dbg, "SET_XHR_BREAKPOINT");
+  await addXHRBreakpoint(dbg, "1");
+  await addXHRBreakpoint(dbg, "2");
+  await addXHRBreakpoint(dbg, "3");
+  await addXHRBreakpoint(dbg, "4");
 
-    invokeInTab("main", "README.md");
-    await waitForPaused(dbg);
-    await resume(dbg);
+  // Remove "2"
+  await removeXHRBreakpoint(dbg, 1);
 
-    // Disable pause on any URL
-    await dbg.actions.togglePauseOnAny();
-    invokeInTab("main", "README.md");
-    assertNotPaused(dbg);
-});
+  const listItems = getXHRBreakpointsElements(dbg);
+  is(listItems.length, 3, "3 XHR breakpoints display in list");
+  is(
+    pauseOnAnyCheckbox.checked, true, 
+    "The pause on any is still checked"
+  );
+  is(
+    getXHRBreakpointLabels(listItems).join(""),
+    "134",
+    "Only the desired breakpoint was removed"
+  );
+
+});
\ No newline at end of file
--- a/devtools/client/debugger/new/test/mochitest/helpers.js
+++ b/devtools/client/debugger/new/test/mochitest/helpers.js
@@ -335,19 +335,22 @@ function assertDebugLine(dbg, line) {
     1,
     "There is only one line"
   );
 
   ok(isVisibleInEditor(dbg, debugLine), "debug line is visible");
 
   const markedSpans = lineInfo.handle.markedSpans;
   if (markedSpans && markedSpans.length > 0) {
-    const marker = markedSpans[0].marker;
+    const classMatch = markedSpans.filter(
+      span => span.marker.className.includes("debug-expression")
+    ).length > 0;
+
     ok(
-      marker.className.includes("debug-expression"),
+      classMatch,
       "expression is highlighted as paused"
     );
   }
 }
 
 /**
  * Assert that the debugger is highlighting the correct location.
  *
@@ -716,33 +719,33 @@ function disableBreakpoint(dbg, source, 
   source = findSource(dbg, source);
   const sourceId = source.id;
   dbg.actions.disableBreakpoint({ sourceId, line, column });
   return waitForDispatch(dbg, "DISABLE_BREAKPOINT");
 }
 
 async function loadAndAddBreakpoint(dbg, filename, line, column) {
   const {
-    selectors: { getBreakpoint, getBreakpoints },
+    selectors: { getBreakpoint, getBreakpointCount, getBreakpointsMap },
     getState
   } = dbg;
 
   await waitForSources(dbg, filename);
 
   ok(true, "Original sources exist");
   const source = findSource(dbg, filename);
 
   await selectSource(dbg, source);
 
   // Test that breakpoint is not off by a line.
   await addBreakpoint(dbg, source, line);
 
-  is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
+  is(getBreakpointCount(getState()), 1, "One breakpoint exists");
   if (!getBreakpoint(getState(), { sourceId: source.id, line, column })) {
-    const breakpoints = getBreakpoints(getState()).toJS();
+    const breakpoints = getBreakpointsMap(getState());
     const id = Object.keys(breakpoints).pop();
     const loc = breakpoints[id].location;
     ok(
       false,
       `Breakpoint has correct line ${line}, column ${column}, but was line ${loc.line} column ${loc.column}`
     );
   }
 
@@ -752,17 +755,17 @@ async function loadAndAddBreakpoint(dbg,
 async function invokeWithBreakpoint(
   dbg,
   fnName,
   filename,
   { line, column },
   handler
 ) {
   const {
-    selectors: { getBreakpoints },
+    selectors: { getBreakpointCount },
     getState
   } = dbg;
 
   const source = await loadAndAddBreakpoint(dbg, filename, line, column);
 
   const invokeResult = invokeInTab(fnName);
 
   let invokeFailed = await Promise.race([
@@ -773,17 +776,17 @@ async function invokeWithBreakpoint(
   if (invokeFailed) {
     return invokeResult;
   }
 
   assertPausedLocation(dbg);
 
   await removeBreakpoint(dbg, source.id, line, column);
 
-  is(getBreakpoints(getState()).size, 0, "Breakpoint reverted");
+  is(getBreakpointCount(getState()), 0, "Breakpoint reverted");
 
   await handler(source);
 
   await resume(dbg);
 
   // If the invoke errored later somehow, capture here so the error is reported nicely.
   await invokeResult;
 }
--- a/devtools/client/preferences/debugger.js
+++ b/devtools/client/preferences/debugger.js
@@ -30,31 +30,33 @@ pref("devtools.debugger.call-stack-visib
 pref("devtools.debugger.scopes-visible", true);
 pref("devtools.debugger.component-visible", true);
 pref("devtools.debugger.workers-visible", true);
 pref("devtools.debugger.breakpoints-visible", true);
 pref("devtools.debugger.expressions-visible", true);
 pref("devtools.debugger.xhr-breakpoints-visible", true);
 pref("devtools.debugger.start-panel-collapsed", false);
 pref("devtools.debugger.end-panel-collapsed", false);
+pref("devtools.debugger.start-panel-size", 300);
+pref("devtools.debugger.end-panel-size", 300);
 pref("devtools.debugger.tabs", "[]");
 pref("devtools.debugger.tabsBlackBoxed", "[]");
 pref("devtools.debugger.pending-selected-location", "{}");
 pref("devtools.debugger.pending-breakpoints", "{}");
 pref("devtools.debugger.expressions", "[]");
 pref("devtools.debugger.file-search-case-sensitive", false);
 pref("devtools.debugger.file-search-whole-word", false);
 pref("devtools.debugger.file-search-regex-match", false);
 pref("devtools.debugger.project-directory-root", "");
 pref("devtools.debugger.skip-pausing", false);
 
 pref("devtools.debugger.features.wasm", true);
 pref("devtools.debugger.features.shortcuts", true);
 pref("devtools.debugger.features.root", true);
-pref("devtools.debugger.features.column-breakpoints", false);
+pref("devtools.debugger.features.column-breakpoints", true);
 pref("devtools.debugger.features.chrome-scopes", false);
 pref("devtools.debugger.features.map-scopes", true);
 pref("devtools.debugger.features.remove-command-bar-options", false);
 pref("devtools.debugger.features.workers", true);
 pref("devtools.debugger.features.code-coverage", false);
 pref("devtools.debugger.features.event-listeners", false);
 pref("devtools.debugger.features.code-folding", false);
 pref("devtools.debugger.features.outline", true);