Merge inbound to mozilla-central. a=merge
authorOana Pop Rus <opoprus@mozilla.com>
Mon, 01 Apr 2019 12:48:18 +0300
changeset 467024 a5a5ddfb5178f8a2b30d186d3cf478da38f324ba
parent 467020 a4ba89d3c83e000e402a6f275240b8b293d446a1 (current diff)
parent 467023 1f44db83758cde791dc4c0f54c7636a0328355a3 (diff)
child 467029 c8aafc542d448f266b79566305513e178f083461
child 467097 200699647a66d09556a3d8784bf41d4b18463718
push id35794
push useropoprus@mozilla.com
push dateMon, 01 Apr 2019 09:49:05 +0000
treeherdermozilla-central@a5a5ddfb5178 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
a5a5ddfb5178 / 68.0a1 / 20190401094905 / files
nightly linux64
a5a5ddfb5178 / 68.0a1 / 20190401094905 / files
nightly mac
a5a5ddfb5178 / 68.0a1 / 20190401094905 / files
nightly win32
a5a5ddfb5178 / 68.0a1 / 20190401094905 / files
nightly win64
a5a5ddfb5178 / 68.0a1 / 20190401094905 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
devtools/client/debugger/new/src/actions/breakpoints/addBreakpoint.js
deleted file mode 100644
--- a/devtools/client/debugger/new/src/actions/breakpoints/addBreakpoint.js
+++ /dev/null
@@ -1,112 +0,0 @@
-/* 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 { setBreakpointPositions } from "./breakpointPositions";
-import {
-  assertBreakpoint,
-  createBreakpoint,
-  getASTLocation,
-  makeBreakpointId,
-  makeBreakpointLocation
-} from "../../utils/breakpoint";
-import { PROMISE } from "../utils/middleware/promise";
-import {
-  getSymbols,
-  getFirstBreakpointPosition,
-  getBreakpointPositionsForLocation,
-  getSourceFromId
-} from "../../selectors";
-
-import { getTextAtPosition } from "../../utils/source";
-import { recordEvent } from "../../utils/telemetry";
-
-import type {
-  BreakpointOptions,
-  Breakpoint,
-  SourceLocation
-} from "../../types";
-import type { ThunkArgs } from "../types";
-
-async function addBreakpointPromise(getState, client, sourceMaps, breakpoint) {
-  const state = getState();
-  const { location, generatedLocation } = breakpoint;
-  const source = getSourceFromId(state, location.sourceId);
-  const generatedSource = getSourceFromId(state, generatedLocation.sourceId);
-
-  const breakpointLocation = makeBreakpointLocation(
-    getState(),
-    generatedLocation
-  );
-  await client.setBreakpoint(breakpointLocation, breakpoint.options);
-
-  const symbols = getSymbols(getState(), source);
-  const astLocation = await getASTLocation(source, symbols, location);
-
-  const originalText = getTextAtPosition(source, location);
-  const text = getTextAtPosition(generatedSource, generatedLocation);
-
-  const newBreakpoint = {
-    id: makeBreakpointId(generatedLocation),
-    disabled: false,
-    options: breakpoint.options,
-    location,
-    astLocation,
-    generatedLocation,
-    text,
-    originalText
-  };
-
-  assertBreakpoint(newBreakpoint);
-
-  return newBreakpoint;
-}
-
-export function addHiddenBreakpoint(location: SourceLocation) {
-  return ({ dispatch }: ThunkArgs) => {
-    return dispatch(addBreakpoint(location, { hidden: true }));
-  };
-}
-
-export function enableBreakpoint(breakpoint: Breakpoint) {
-  return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
-    // To instantly reflect in the UI, we optimistically enable the breakpoint
-    const enabledBreakpoint = { ...breakpoint, disabled: false };
-
-    return dispatch({
-      type: "ENABLE_BREAKPOINT",
-      breakpoint: enabledBreakpoint,
-      [PROMISE]: addBreakpointPromise(getState, client, sourceMaps, breakpoint)
-    });
-  };
-}
-
-export function addBreakpoint(
-  location: SourceLocation,
-  options: BreakpointOptions = {}
-) {
-  return async ({ dispatch, getState, sourceMaps, client }: ThunkArgs) => {
-    recordEvent("add_breakpoint");
-
-    const { sourceId, column } = location;
-
-    await dispatch(setBreakpointPositions(sourceId));
-
-    const position = column
-      ? getBreakpointPositionsForLocation(getState(), location)
-      : getFirstBreakpointPosition(getState(), location);
-
-    if (!position) {
-      return;
-    }
-
-    const breakpoint = createBreakpoint(position, { options });
-
-    return dispatch({
-      type: "ADD_BREAKPOINT",
-      breakpoint,
-      [PROMISE]: addBreakpointPromise(getState, client, sourceMaps, breakpoint)
-    });
-  };
-}
--- a/devtools/client/debugger/new/src/actions/breakpoints/index.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/index.js
@@ -6,106 +6,52 @@
 
 /**
  * Redux actions for breakpoints
  * @module actions/breakpoints
  */
 
 import { PROMISE } from "../utils/middleware/promise";
 import {
-  getBreakpoint,
   getBreakpointsList,
   getXHRBreakpoints,
   getSelectedSource,
   getBreakpointAtLocation,
   getConditionalPanelLocation,
   getBreakpointsForSource,
   isEmptyLineInSource,
   getBreakpointsAtLine
 } from "../../selectors";
-import {
-  assertBreakpoint,
-  createXHRBreakpoint,
-  makeBreakpointLocation
-} from "../../utils/breakpoint";
+import { createXHRBreakpoint } from "../../utils/breakpoint";
 import {
   addBreakpoint,
-  addHiddenBreakpoint,
-  enableBreakpoint
-} from "./addBreakpoint";
+  removeBreakpoint,
+  enableBreakpoint,
+  disableBreakpoint
+} from "./modify";
 import remapLocations from "./remapLocations";
-import { syncBreakpoint } from "./syncBreakpoint";
 import { closeConditionalPanel } from "../ui";
 
 // this will need to be changed so that addCLientBreakpoint is removed
 
-import type { ThunkArgs, Action } from "../types";
+import type { ThunkArgs } from "../types";
 import type {
   Breakpoint,
-  BreakpointOptions,
   Source,
   SourceLocation,
   XHRBreakpoint
 } from "../../types";
 
-import { recordEvent } from "../../utils/telemetry";
-
 export * from "./breakpointPositions";
-
-async function removeBreakpointsPromise(client, state, breakpoint) {
-  const breakpointLocation = makeBreakpointLocation(
-    state,
-    breakpoint.generatedLocation
-  );
-  await client.removeBreakpoint(breakpointLocation);
-}
-
-/**
- * Remove a single breakpoint
- *
- * @memberof actions/breakpoints
- * @static
- */
-export function removeBreakpoint(breakpoint: Breakpoint) {
-  return ({ dispatch, getState, client }: ThunkArgs) => {
-    recordEvent("remove_breakpoint");
+export * from "./modify";
+export * from "./syncBreakpoint";
 
-    // If the breakpoint is already disabled, we don't need to communicate
-    // with the server. We just need to dispatch an action
-    // simulating a successful server request
-    if (breakpoint.disabled) {
-      return dispatch(
-        ({ type: "REMOVE_BREAKPOINT", breakpoint, status: "done" }: Action)
-      );
-    }
-
-    return dispatch({
-      type: "REMOVE_BREAKPOINT",
-      breakpoint,
-      disabled: false,
-      [PROMISE]: removeBreakpointsPromise(client, getState(), breakpoint)
-    });
-  };
-}
-
-/**
- * Disable a single breakpoint
- *
- * @memberof actions/breakpoints
- * @static
- */
-export function disableBreakpoint(breakpoint: Breakpoint) {
-  return async ({ dispatch, getState, client }: ThunkArgs) => {
-    await removeBreakpointsPromise(client, getState(), breakpoint);
-
-    const newBreakpoint: Breakpoint = { ...breakpoint, disabled: true };
-
-    return dispatch(
-      ({ type: "DISABLE_BREAKPOINT", breakpoint: newBreakpoint }: Action)
-    );
+export function addHiddenBreakpoint(location: SourceLocation) {
+  return ({ dispatch }: ThunkArgs) => {
+    return dispatch(addBreakpoint(location, { hidden: true }));
   };
 }
 
 /**
  * Disable all breakpoints in a source
  *
  * @memberof actions/breakpoints
  * @static
@@ -143,44 +89,23 @@ export function enableBreakpointsInSourc
  *
  * @memberof actions/breakpoints
  * @static
  */
 export function toggleAllBreakpoints(shouldDisableBreakpoints: boolean) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
     const breakpoints = getBreakpointsList(getState());
 
-    const modifiedBreakpoints = [];
-
     for (const breakpoint of breakpoints) {
       if (shouldDisableBreakpoints) {
-        await removeBreakpointsPromise(client, getState(), breakpoint);
-        const newBreakpoint: Breakpoint = { ...breakpoint, disabled: true };
-        modifiedBreakpoints.push(newBreakpoint);
+        dispatch(disableBreakpoint(breakpoint));
       } else {
-        const newBreakpoint: Breakpoint = { ...breakpoint, disabled: false };
-        modifiedBreakpoints.push(newBreakpoint);
+        dispatch(enableBreakpoint(breakpoint));
       }
     }
-
-    if (shouldDisableBreakpoints) {
-      return dispatch(
-        ({
-          type: "DISABLE_ALL_BREAKPOINTS",
-          breakpoints: modifiedBreakpoints
-        }: Action)
-      );
-    }
-
-    return dispatch(
-      ({
-        type: "ENABLE_ALL_BREAKPOINTS",
-        breakpoints: modifiedBreakpoints
-      }: Action)
-    );
   };
 }
 
 /**
  * Toggle Breakpoints
  *
  * @memberof actions/breakpoints
  * @static
@@ -257,66 +182,19 @@ export function remapBreakpoints(sourceI
   return async ({ dispatch, getState, sourceMaps }: ThunkArgs) => {
     const breakpoints = getBreakpointsList(getState());
     const newBreakpoints = await remapLocations(
       breakpoints,
       sourceId,
       sourceMaps
     );
 
-    return dispatch(
-      ({
-        type: "REMAP_BREAKPOINTS",
-        breakpoints: newBreakpoints
-      }: Action)
-    );
-  };
-}
-
-/**
- * Update the options of a breakpoint.
- *
- * @throws {Error} "not implemented"
- * @memberof actions/breakpoints
- * @static
- * @param {SourceLocation} location
- *        @see DebuggerController.Breakpoints.addBreakpoint
- * @param {Object} options
- *        Any options to set on the breakpoint
- */
-export function setBreakpointOptions(
-  location: SourceLocation,
-  options: BreakpointOptions = {}
-) {
-  return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
-    const bp = getBreakpoint(getState(), location);
-    if (!bp) {
-      return dispatch(addBreakpoint(location, options));
+    for (const bp of newBreakpoints) {
+      await dispatch(addBreakpoint(bp.location, bp.options, bp.disabled));
     }
-
-    if (bp.disabled) {
-      await dispatch(enableBreakpoint(bp));
-    }
-
-    const breakpointLocation = makeBreakpointLocation(
-      getState(),
-      bp.generatedLocation
-    );
-    await client.setBreakpoint(breakpointLocation, options);
-
-    const newBreakpoint = { ...bp, disabled: false, options };
-
-    assertBreakpoint(newBreakpoint);
-
-    return dispatch(
-      ({
-        type: "SET_BREAKPOINT_OPTIONS",
-        breakpoint: newBreakpoint
-      }: Action)
-    );
   };
 }
 
 export function toggleBreakpointAtLine(line: number) {
   return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const state = getState();
     const selectedSource = getSelectedSource(state);
 
@@ -509,10 +387,8 @@ export function removeXHRBreakpoint(inde
     return dispatch({
       type: "REMOVE_XHR_BREAKPOINT",
       breakpoint,
       index,
       [PROMISE]: client.removeXHRBreakpoint(breakpoint.path, breakpoint.method)
     });
   };
 }
-
-export { addBreakpoint, addHiddenBreakpoint, enableBreakpoint, syncBreakpoint };
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/actions/breakpoints/modify.js
@@ -0,0 +1,262 @@
+/* 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 { ThunkArgs } from "../types";
+import type {
+  Breakpoint,
+  BreakpointOptions,
+  SourceLocation
+} from "../../types";
+
+import {
+  makeBreakpointLocation,
+  makeBreakpointId,
+  getASTLocation
+} from "../../utils/breakpoint";
+
+import { getTextAtPosition } from "../../utils/source";
+
+import {
+  getBreakpoint,
+  getBreakpointPositionsForLocation,
+  getFirstBreakpointPosition,
+  getSourceFromId,
+  getSymbols
+} from "../../selectors";
+
+import { loadSourceText } from "../sources/loadSourceText";
+import { setBreakpointPositions } from "./breakpointPositions";
+
+import { recordEvent } from "../../utils/telemetry";
+
+// This file has the primitive operations used to modify individual breakpoints
+// and keep them in sync with the breakpoints installed on server threads. These
+// are collected here to make it easier to preserve the following invariant:
+//
+// Breakpoints are included in reducer state iff they are disabled or requests
+// have been dispatched to set them in all server threads.
+//
+// To maintain this property, updates to the reducer and installed breakpoints
+// must happen with no intervening await. Using await allows other operations to
+// modify the breakpoint state in the interim and potentially cause breakpoint
+// state to go out of sync.
+//
+// The reducer is optimistically updated when users set or remove a breakpoint,
+// but it might take a little while before the breakpoints have been set or
+// removed in each thread. Once all outstanding requests sent to a thread have
+// been processed, the reducer and server threads will be in sync.
+//
+// There is another exception to the above invariant when first connecting to
+// the server: breakpoints have been installed on all generated locations in the
+// pending breakpoints, but no breakpoints have been added to the reducer. When
+// a matching source appears, either the server breakpoint will be removed or a
+// breakpoint will be added to the reducer, to restore the above invariant.
+// See syncBreakpoint.js for more.
+
+function clientSetBreakpoint(breakpoint: Breakpoint) {
+  return ({ getState, client }: ThunkArgs) => {
+    const breakpointLocation = makeBreakpointLocation(
+      getState(),
+      breakpoint.generatedLocation
+    );
+    return client.setBreakpoint(breakpointLocation, breakpoint.options);
+  };
+}
+
+function clientRemoveBreakpoint(breakpoint: Breakpoint) {
+  return ({ getState, client }: ThunkArgs) => {
+    const breakpointLocation = makeBreakpointLocation(
+      getState(),
+      breakpoint.generatedLocation
+    );
+    return client.removeBreakpoint(breakpointLocation);
+  };
+}
+
+export function enableBreakpoint(initialBreakpoint: Breakpoint) {
+  return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
+    const breakpoint = getBreakpoint(getState(), initialBreakpoint.location);
+    if (!breakpoint || !breakpoint.disabled) {
+      return;
+    }
+
+    dispatch({
+      type: "SET_BREAKPOINT",
+      breakpoint: { ...breakpoint, disabled: false }
+    });
+
+    return dispatch(clientSetBreakpoint(breakpoint));
+  };
+}
+
+export function addBreakpoint(
+  initialLocation: SourceLocation,
+  options: BreakpointOptions = {},
+  disabled: boolean = false
+) {
+  return async ({ dispatch, getState, sourceMaps, client }: ThunkArgs) => {
+    recordEvent("add_breakpoint");
+
+    const { sourceId, column } = initialLocation;
+
+    await dispatch(setBreakpointPositions(sourceId));
+
+    const position = column
+      ? getBreakpointPositionsForLocation(getState(), initialLocation)
+      : getFirstBreakpointPosition(getState(), initialLocation);
+
+    if (!position) {
+      return;
+    }
+
+    const { location, generatedLocation } = position;
+
+    // Both the original and generated sources must be loaded to get the
+    // breakpoint's text.
+    await dispatch(
+      loadSourceText(getSourceFromId(getState(), location.sourceId))
+    );
+    await dispatch(
+      loadSourceText(getSourceFromId(getState(), generatedLocation.sourceId))
+    );
+
+    const source = getSourceFromId(getState(), location.sourceId);
+    const generatedSource = getSourceFromId(
+      getState(),
+      generatedLocation.sourceId
+    );
+
+    const symbols = getSymbols(getState(), source);
+    const astLocation = await getASTLocation(source, symbols, location);
+
+    const originalText = getTextAtPosition(source, location);
+    const text = getTextAtPosition(generatedSource, generatedLocation);
+
+    const id = makeBreakpointId(location);
+    const breakpoint = {
+      id,
+      disabled,
+      options,
+      location,
+      astLocation,
+      generatedLocation,
+      text,
+      originalText
+    };
+
+    // There cannot be multiple breakpoints with the same generated location.
+    // Because a generated location cannot map to multiple original locations,
+    // the only breakpoints that can map to this generated location have the
+    // new breakpoint's |location| or |generatedLocation| as their own
+    // |location|. We will overwrite any breakpoint at |location| with the
+    // SET_BREAKPOINT action below, but need to manually remove any breakpoint
+    // at |generatedLocation|.
+    const generatedId = makeBreakpointId(breakpoint.generatedLocation);
+    if (id != generatedId && getBreakpoint(getState(), generatedLocation)) {
+      dispatch({
+        type: "REMOVE_BREAKPOINT",
+        location: generatedLocation
+      });
+    }
+
+    dispatch({
+      type: "SET_BREAKPOINT",
+      breakpoint
+    });
+
+    if (disabled) {
+      // If we just clobbered an enabled breakpoint with a disabled one, we need
+      // to remove any installed breakpoint in the server.
+      return dispatch(clientRemoveBreakpoint(breakpoint));
+    }
+
+    return dispatch(clientSetBreakpoint(breakpoint));
+  };
+}
+
+/**
+ * Remove a single breakpoint
+ *
+ * @memberof actions/breakpoints
+ * @static
+ */
+export function removeBreakpoint(initialBreakpoint: Breakpoint) {
+  return ({ dispatch, getState, client }: ThunkArgs) => {
+    recordEvent("remove_breakpoint");
+
+    const breakpoint = getBreakpoint(getState(), initialBreakpoint.location);
+    if (!breakpoint) {
+      return;
+    }
+
+    dispatch({
+      type: "REMOVE_BREAKPOINT",
+      location: breakpoint.location
+    });
+
+    // If the breakpoint is disabled then it is not installed in the server.
+    if (breakpoint.disabled) {
+      return;
+    }
+
+    return dispatch(clientRemoveBreakpoint(breakpoint));
+  };
+}
+
+/**
+ * Disable a single breakpoint
+ *
+ * @memberof actions/breakpoints
+ * @static
+ */
+export function disableBreakpoint(initialBreakpoint: Breakpoint) {
+  return ({ dispatch, getState, client }: ThunkArgs) => {
+    const breakpoint = getBreakpoint(getState(), initialBreakpoint.location);
+    if (!breakpoint || breakpoint.disabled) {
+      return;
+    }
+
+    dispatch({
+      type: "SET_BREAKPOINT",
+      breakpoint: { ...breakpoint, disabled: true }
+    });
+
+    return dispatch(clientRemoveBreakpoint(breakpoint));
+  };
+}
+
+/**
+ * Update the options of a breakpoint.
+ *
+ * @throws {Error} "not implemented"
+ * @memberof actions/breakpoints
+ * @static
+ * @param {SourceLocation} location
+ *        @see DebuggerController.Breakpoints.addBreakpoint
+ * @param {Object} options
+ *        Any options to set on the breakpoint
+ */
+export function setBreakpointOptions(
+  location: SourceLocation,
+  options: BreakpointOptions = {}
+) {
+  return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
+    let breakpoint = getBreakpoint(getState(), location);
+    if (!breakpoint) {
+      return dispatch(addBreakpoint(location, options));
+    }
+
+    // Note: setting a breakpoint's options implicitly enables it.
+    breakpoint = { ...breakpoint, disabled: false, options };
+
+    dispatch({
+      type: "SET_BREAKPOINT",
+      breakpoint
+    });
+
+    return dispatch(clientSetBreakpoint(breakpoint));
+  };
+}
--- a/devtools/client/debugger/new/src/actions/breakpoints/moz.build
+++ b/devtools/client/debugger/new/src/actions/breakpoints/moz.build
@@ -3,14 +3,14 @@
 # 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/.
 
 DIRS += [
 
 ]
 
 CompiledModules(
-    'addBreakpoint.js',
     'breakpointPositions.js',
     'index.js',
+    'modify.js',
     'remapLocations.js',
     'syncBreakpoint.js',
 )
--- a/devtools/client/debugger/new/src/actions/breakpoints/syncBreakpoint.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/syncBreakpoint.js
@@ -1,46 +1,37 @@
 /* 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 { setBreakpointPositions } from "./breakpointPositions";
 import {
-  createBreakpoint,
-  assertBreakpoint,
   assertPendingBreakpoint,
   findFunctionByName,
   findPosition,
   makeBreakpointLocation
 } from "../../utils/breakpoint";
 
-import { getTextAtPosition, isInlineScript } from "../../utils/source";
-import { comparePosition } from "../../utils/location";
+import { comparePosition, createLocation } from "../../utils/location";
 
 import { originalToGeneratedId, isOriginalId } from "devtools-source-map";
-import { getSource, getBreakpointsList } from "../../selectors";
-import { removeBreakpoint } from ".";
+import { getSource, getBreakpoint } from "../../selectors";
+import { removeBreakpoint, addBreakpoint } from ".";
 
-import type { ThunkArgs, Action } from "../types";
+import type { ThunkArgs } from "../types";
 
 import type {
   SourceLocation,
   ASTLocation,
   PendingBreakpoint,
-  SourceId,
-  Breakpoint
+  SourceId
 } from "../../types";
 
-type BreakpointSyncData = {
-  previousLocation: SourceLocation,
-  breakpoint: ?Breakpoint
-};
-
 async function findBreakpointPosition(
   { getState, dispatch },
   location: SourceLocation
 ) {
   const positions = await dispatch(setBreakpointPositions(location.sourceId));
   const position = findPosition(positions, location);
   return position && position.generatedLocation;
 }
@@ -61,177 +52,128 @@ async function findNewLocation(
   return {
     line,
     column: location.column,
     sourceUrl: source.url,
     sourceId: source.id
   };
 }
 
-function createSyncData(
-  pendingBreakpoint: PendingBreakpoint,
-  location: SourceLocation,
-  generatedLocation: SourceLocation,
-  previousLocation: SourceLocation,
-  text: string,
-  originalText: string
-): BreakpointSyncData {
-  const overrides = {
-    ...pendingBreakpoint,
-    text,
-    originalText
-  };
-  const breakpoint = createBreakpoint(
-    { generatedLocation, location },
-    overrides
-  );
-
-  assertBreakpoint(breakpoint);
-  return { breakpoint, previousLocation };
-}
-
-// Look for an existing breakpoint at the specified generated location.
-function findExistingBreakpoint(state, generatedLocation) {
-  const breakpoints = getBreakpointsList(state);
-
-  return breakpoints.find(bp => {
-    return (
-      bp.generatedLocation.sourceUrl == generatedLocation.sourceUrl &&
-      bp.generatedLocation.line == generatedLocation.line &&
-      bp.generatedLocation.column == generatedLocation.column
-    );
-  });
-}
-
-// we have three forms of syncing: disabled syncing, existing server syncing
-// and adding a new breakpoint
-export async function syncBreakpointPromise(
-  thunkArgs: ThunkArgs,
-  sourceId: SourceId,
-  pendingBreakpoint: PendingBreakpoint
-): Promise<?BreakpointSyncData> {
-  const { getState, client, dispatch } = thunkArgs;
-  assertPendingBreakpoint(pendingBreakpoint);
-
-  const source = getSource(getState(), sourceId);
-
-  const generatedSourceId = isOriginalId(sourceId)
-    ? originalToGeneratedId(sourceId)
-    : sourceId;
-
-  const generatedSource = getSource(getState(), generatedSourceId);
-
-  if (!source || !generatedSource) {
-    return;
-  }
-
-  const { location, generatedLocation, astLocation } = pendingBreakpoint;
-  const previousLocation = { ...location, sourceId };
-
-  const newLocation = await findNewLocation(
-    astLocation,
-    previousLocation,
-    source
-  );
-
-  const newGeneratedLocation = await findBreakpointPosition(
-    thunkArgs,
-    newLocation
-  );
-
-  const isSameLocation = comparePosition(
-    generatedLocation,
-    newGeneratedLocation
-  );
-
-  /** ******* CASE 1: No server change ***********/
-  // early return if breakpoint is disabled or we are in the sameLocation
-  if (newGeneratedLocation && (pendingBreakpoint.disabled || isSameLocation)) {
-    // Make sure the breakpoint is installed on all source actors.
-    if (!pendingBreakpoint.disabled) {
-      await client.setBreakpoint(
-        makeBreakpointLocation(getState(), newGeneratedLocation),
-        pendingBreakpoint.options
-      );
-    }
-
-    const originalText = getTextAtPosition(source, previousLocation);
-    const text = getTextAtPosition(generatedSource, newGeneratedLocation);
-
-    return createSyncData(
-      pendingBreakpoint,
-      newLocation,
-      newGeneratedLocation,
-      previousLocation,
-      text,
-      originalText
-    );
-  }
-
-  // Clear any breakpoint for the generated location.
-  const bp = findExistingBreakpoint(getState(), generatedLocation);
-  if (bp) {
-    await dispatch(removeBreakpoint(bp));
-  }
-
-  if (!newGeneratedLocation) {
-    return {
-      previousLocation,
-      // We return the original bp here for HTML scripts because there may
-      // be multiple <script> elements in a source and not all of them may
-      // have loaded yet to allow synching. This means we need to leave
-      // breakpoints on these pages pending.
-      breakpoint: isInlineScript(generatedSource) ? bp : null
-    };
-  }
-
-  /** ******* Case 2: Add New Breakpoint ***********/
-  // If we are not disabled, set the breakpoint on the server and get
-  // that info so we can set it on our breakpoints.
-  await client.setBreakpoint(
-    makeBreakpointLocation(getState(), newGeneratedLocation),
-    pendingBreakpoint.options
-  );
-
-  const originalText = getTextAtPosition(source, newLocation);
-  const text = getTextAtPosition(generatedSource, newGeneratedLocation);
-
-  return createSyncData(
-    pendingBreakpoint,
-    newLocation,
-    newGeneratedLocation,
-    previousLocation,
-    text,
-    originalText
-  );
-}
-
-/**
- * Syncing a breakpoint add breakpoint information that is stored, and
- * contact the server for more data.
- */
+// Breakpoint syncing occurs when a source is found that matches either the
+// original or generated URL of a pending breakpoint. A new breakpoint is
+// constructed that might have a different original and/or generated location,
+// if the original source has changed since the pending breakpoint was created.
+// There are a couple subtle aspects to syncing:
+//
+// - We handle both the original and generated source because there is no
+//   guarantee that seeing the generated source means we will also see the
+//   original source. When connecting, a breakpoint will be installed in the
+//   client for the generated location in the pending breakpoint, and we need
+//   to make sure that either a breakpoint is added to the reducer or that this
+//   client breakpoint is deleted.
+//
+// - If we see both the original and generated sources and the source mapping
+//   has changed, we need to make sure that only a single breakpoint is added
+//   to the reducer for the new location corresponding to the original location
+//   in the pending breakpoint.
 export function syncBreakpoint(
   sourceId: SourceId,
   pendingBreakpoint: PendingBreakpoint
 ) {
   return async (thunkArgs: ThunkArgs) => {
-    const { dispatch } = thunkArgs;
+    const { getState, client, dispatch } = thunkArgs;
+    assertPendingBreakpoint(pendingBreakpoint);
+
+    const source = getSource(getState(), sourceId);
 
-    const response = await syncBreakpointPromise(
-      thunkArgs,
-      sourceId,
-      pendingBreakpoint
-    );
+    const generatedSourceId = isOriginalId(sourceId)
+      ? originalToGeneratedId(sourceId)
+      : sourceId;
 
-    if (!response) {
+    const generatedSource = getSource(getState(), generatedSourceId);
+
+    if (!source || !generatedSource) {
       return;
     }
 
-    const { breakpoint, previousLocation } = response;
+    const { location, generatedLocation, astLocation } = pendingBreakpoint;
+    const sourceGeneratedLocation = createLocation({
+      ...generatedLocation,
+      sourceId: generatedSourceId
+    });
+
+    if (
+      source == generatedSource &&
+      location.sourceUrl != generatedLocation.sourceUrl
+    ) {
+      // We are handling the generated source and the pending breakpoint has a
+      // source mapping. Watch out for the case when the original source has
+      // already been processed, in which case either a breakpoint has already
+      // been added at this generated location or the client breakpoint has been
+      // removed.
+      const breakpointLocation = makeBreakpointLocation(
+        getState(),
+        sourceGeneratedLocation
+      );
+      if (
+        getBreakpoint(getState(), sourceGeneratedLocation) ||
+        !client.hasBreakpoint(breakpointLocation)
+      ) {
+        return;
+      }
+      return dispatch(
+        addBreakpoint(
+          sourceGeneratedLocation,
+          pendingBreakpoint.options,
+          pendingBreakpoint.disabled
+        )
+      );
+    }
+
+    const previousLocation = { ...location, sourceId };
+
+    const newLocation = await findNewLocation(
+      astLocation,
+      previousLocation,
+      source
+    );
+
+    const newGeneratedLocation = await findBreakpointPosition(
+      thunkArgs,
+      newLocation
+    );
+
+    if (!newGeneratedLocation) {
+      return;
+    }
+
+    const isSameLocation = comparePosition(
+      generatedLocation,
+      newGeneratedLocation
+    );
+
+    // If the new generated location has changed from that in the pending
+    // breakpoint, remove any breakpoint associated with the old generated
+    // location. This could either be in the reducer or only in the client,
+    // depending on whether the pending breakpoint has been processed for the
+    // generated source yet.
+    if (!isSameLocation) {
+      const bp = getBreakpoint(getState(), sourceGeneratedLocation);
+      if (bp) {
+        dispatch(removeBreakpoint(bp));
+      } else {
+        const breakpointLocation = makeBreakpointLocation(
+          getState(),
+          sourceGeneratedLocation
+        );
+        client.removeBreakpoint(breakpointLocation);
+      }
+    }
+
     return dispatch(
-      ({
-        type: "SYNC_BREAKPOINT",
-        breakpoint,
-        previousLocation
-      }: Action)
+      addBreakpoint(
+        newLocation,
+        pendingBreakpoint.options,
+        pendingBreakpoint.disabled
+      )
     );
   };
 }
--- a/devtools/client/debugger/new/src/actions/breakpoints/tests/__snapshots__/breakpoints.spec.js.snap
+++ b/devtools/client/debugger/new/src/actions/breakpoints/tests/__snapshots__/breakpoints.spec.js.snap
@@ -24,21 +24,17 @@ Array [
         },
         "id": "a:2:1",
         "location": Object {
           "column": 1,
           "line": 2,
           "sourceId": "a",
           "sourceUrl": "http://localhost:8000/examples/a",
         },
-        "options": Object {
-          "condition": null,
-          "hidden": false,
-          "logValue": null,
-        },
+        "options": Object {},
         "originalText": "return a",
         "text": "return a",
       },
     ],
     "source": Object {
       "actors": Array [
         Object {
           "actor": "a-actor",
@@ -59,43 +55,37 @@ Array [
 `;
 
 exports[`breakpoints should not show a breakpoint that does not have text 1`] = `Array []`;
 
 exports[`breakpoints should remap breakpoints on pretty print 1`] = `
 Object {
   "astLocation": Object {
     "index": 0,
-    "name": undefined,
+    "name": "a",
     "offset": Object {
-      "column": 0,
-      "line": 1,
-      "sourceId": "a.js",
-      "sourceUrl": "http://localhost:8000/examples/a.js",
+      "column": undefined,
+      "line": 0,
     },
   },
   "disabled": false,
   "generatedLocation": Object {
     "column": 0,
     "line": 1,
     "sourceId": "a.js",
     "sourceUrl": "http://localhost:8000/examples/a.js",
   },
-  "id": "a.js:1:",
+  "id": "a.js/originalSource-d6d70368d5c252598541e693a7ad6c27:1:",
   "location": Object {
     "column": 0,
     "line": 1,
     "sourceId": "a.js/originalSource-d6d70368d5c252598541e693a7ad6c27",
     "sourceUrl": "http://localhost:8000/examples/a.js:formatted",
   },
-  "options": Object {
-    "condition": null,
-    "hidden": false,
-    "logValue": null,
-  },
+  "options": Object {},
   "originalText": "function a() {",
   "text": "function a() {",
 }
 `;
 
 exports[`breakpoints should show a disabled breakpoint that does not have text 1`] = `
 Array [
   Object {
@@ -120,21 +110,17 @@ Array [
         },
         "id": "a:5:1",
         "location": Object {
           "column": 1,
           "line": 5,
           "sourceId": "a",
           "sourceUrl": "http://localhost:8000/examples/a",
         },
-        "options": Object {
-          "condition": null,
-          "hidden": false,
-          "logValue": null,
-        },
+        "options": Object {},
         "originalText": "",
         "text": "",
       },
     ],
     "source": Object {
       "actors": Array [
         Object {
           "actor": "a-actor",
--- a/devtools/client/debugger/new/src/actions/breakpoints/tests/breakpoints.spec.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/tests/breakpoints.spec.js
@@ -94,17 +94,22 @@ describe("breakpoints", () => {
     await dispatch(
       actions.setSelectedLocation(source, {
         line: 1,
         column: 1,
         sourceId: source.id
       })
     );
 
-    const breakpoint = await dispatch(actions.addBreakpoint(loc1));
+    await dispatch(actions.addBreakpoint(loc1));
+    const breakpoint = selectors.getBreakpoint(getState(), loc1);
+    if (!breakpoint) {
+      throw new Error("no breakpoint");
+    }
+
     await dispatch(actions.disableBreakpoint(breakpoint));
 
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
     const bp = selectors.getBreakpoint(getState(), loc1);
     expect(bp && bp.location).toEqual(loc1);
     expect(selectors.getBreakpointSources(getState())).toMatchSnapshot();
   });
 
@@ -206,19 +211,24 @@ describe("breakpoints", () => {
     const aSource = makeSource("a");
     await dispatch(actions.newSource(aSource));
     await dispatch(actions.loadSourceText(aSource));
 
     const bSource = makeSource("b");
     await dispatch(actions.newSource(bSource));
     await dispatch(actions.loadSourceText(bSource));
 
-    const breakpoint = await dispatch(actions.addBreakpoint(loc1));
+    await dispatch(actions.addBreakpoint(loc1));
     await dispatch(actions.addBreakpoint(loc2));
 
+    const breakpoint = selectors.getBreakpoint(getState(), loc1);
+    if (!breakpoint) {
+      throw new Error("no breakpoint");
+    }
+
     await dispatch(actions.disableBreakpoint(breakpoint));
 
     const bp = selectors.getBreakpoint(getState(), loc1);
     expect(bp && bp.disabled).toBe(true);
   });
 
   it("should enable breakpoint", async () => {
     const { dispatch, getState } = createStore(
@@ -230,23 +240,32 @@ describe("breakpoints", () => {
       column: 1,
       sourceUrl: "http://localhost:8000/examples/a"
     };
 
     const aSource = makeSource("a");
     await dispatch(actions.newSource(aSource));
     await dispatch(actions.loadSourceText(aSource));
 
-    const breakpoint = await dispatch(actions.addBreakpoint(loc));
-    await dispatch(actions.disableBreakpoint(breakpoint));
+    await dispatch(actions.addBreakpoint(loc));
+    let bp = selectors.getBreakpoint(getState(), loc);
+    if (!bp) {
+      throw new Error("no breakpoint");
+    }
 
-    let bp = selectors.getBreakpoint(getState(), loc);
+    await dispatch(actions.disableBreakpoint(bp));
+
+    bp = selectors.getBreakpoint(getState(), loc);
+    if (!bp) {
+      throw new Error("no breakpoint");
+    }
+
     expect(bp && bp.disabled).toBe(true);
 
-    await dispatch(actions.enableBreakpoint(breakpoint));
+    await dispatch(actions.enableBreakpoint(bp));
 
     bp = selectors.getBreakpoint(getState(), loc);
     expect(bp && !bp.disabled).toBe(true);
   });
 
   it("should toggle all the breakpoints", async () => {
     const { dispatch, getState } = createStore(
       mockClient({ "5": [1], "6": [2] })
@@ -349,17 +368,17 @@ describe("breakpoints", () => {
 
     const source = makeSource("a");
     await dispatch(actions.newSource(source));
     await dispatch(actions.loadSourceText(source));
 
     await dispatch(actions.addBreakpoint(loc));
 
     let bp = selectors.getBreakpoint(getState(), loc);
-    expect(bp && bp.options.condition).toBe(null);
+    expect(bp && bp.options.condition).toBe(undefined);
 
     await dispatch(
       actions.setBreakpointOptions(loc, {
         condition: "const foo = 0",
         getTextForLine: () => {}
       })
     );
 
@@ -376,21 +395,26 @@ describe("breakpoints", () => {
       column: 1,
       sourceUrl: "http://localhost:8000/examples/a"
     };
 
     const source = makeSource("a");
     await dispatch(actions.newSource(source));
     await dispatch(actions.loadSourceText(source));
 
-    const breakpoint = await dispatch(actions.addBreakpoint(loc));
-    await dispatch(actions.disableBreakpoint(breakpoint));
+    await dispatch(actions.addBreakpoint(loc));
+    let bp = selectors.getBreakpoint(getState(), loc);
+    if (!bp) {
+      throw new Error("no breakpoint");
+    }
 
-    const bp = selectors.getBreakpoint(getState(), loc);
-    expect(bp && bp.options.condition).toBe(null);
+    await dispatch(actions.disableBreakpoint(bp));
+
+    bp = selectors.getBreakpoint(getState(), loc);
+    expect(bp && bp.options.condition).toBe(undefined);
 
     await dispatch(
       actions.setBreakpointOptions(loc, {
         condition: "const foo = 0",
         getTextForLine: () => {}
       })
     );
     const newBreakpoint = selectors.getBreakpoint(getState(), loc);
--- a/devtools/client/debugger/new/src/actions/sources/loadSourceText.js
+++ b/devtools/client/debugger/new/src/actions/sources/loadSourceText.js
@@ -83,21 +83,16 @@ async function loadSourceTextPromise(
     [PROMISE]: loadSource(getState(), source, { sourceMaps, client })
   });
 
   const newSource = getSource(getState(), source.id);
   if (!newSource) {
     return;
   }
 
-  dispatch({
-    type: "UPDATE_BREAKPOINT_TEXT",
-    source: newSource
-  });
-
   if (!newSource.isWasm && isLoaded(newSource)) {
     parser.setSource(newSource);
     dispatch(setBreakpointPositions(newSource.id));
   }
 
   return newSource;
 }
 
--- a/devtools/client/debugger/new/src/actions/sources/newSources.js
+++ b/devtools/client/debugger/new/src/actions/sources/newSources.js
@@ -8,21 +8,17 @@
  * Redux actions for the sources state
  * @module actions/sources
  */
 
 import { generatedToOriginalId } from "devtools-source-map";
 import { flatten } from "lodash";
 
 import { toggleBlackBox } from "./blackbox";
-import {
-  syncBreakpoint,
-  addBreakpoint,
-  setBreakpointPositions
-} from "../breakpoints";
+import { syncBreakpoint, setBreakpointPositions } from "../breakpoints";
 import { loadSourceText } from "./loadSourceText";
 import { togglePrettyPrint } from "./prettyPrint";
 import { selectLocation } from "../sources";
 import {
   getRawSourceURL,
   isPrettyURL,
   isOriginal,
   isInlineScript
@@ -193,28 +189,19 @@ function checkPendingBreakpoints(sourceI
 
     if (pendingBreakpoints.length === 0) {
       return;
     }
 
     // load the source text if there is a pending breakpoint for it
     await dispatch(loadSourceText(source));
 
-    // Matching pending breakpoints could have either the same generated or the
-    // same original source. We expect the generated source to appear first and
-    // will add a breakpoint at that location initially. If the original source
-    // appears later then we use syncBreakpoint to see if the generated location
-    // changed and we need to remove the breakpoint we added earlier.
     await Promise.all(
       pendingBreakpoints.map(bp => {
-        if (source.url == bp.location.sourceUrl) {
-          return dispatch(syncBreakpoint(sourceId, bp));
-        }
-        const { line, column } = bp.generatedLocation;
-        return dispatch(addBreakpoint({ sourceId, line, column }, bp.options));
+        return dispatch(syncBreakpoint(sourceId, bp));
       })
     );
   };
 }
 
 function restoreBlackBoxedSources(sources: Source[]) {
   return async ({ dispatch }: ThunkArgs) => {
     const tabs = getBlackBoxList();
--- a/devtools/client/debugger/new/src/actions/sources/tests/loadSource.spec.js
+++ b/devtools/client/debugger/new/src/actions/sources/tests/loadSource.spec.js
@@ -11,17 +11,16 @@ import {
   createStore,
   makeOriginalSource,
   makeSource
 } from "../../../utils/test-head";
 import {
   createSource,
   sourceThreadClient
 } from "../../tests/helpers/threadClient.js";
-import { addBreakpoint } from "../../breakpoints/addBreakpoint";
 import { getBreakpointsList } from "../../../selectors";
 
 describe("loadSourceText", () => {
   it("should load source text", async () => {
     const store = createStore(sourceThreadClient);
     const { dispatch, getState } = store;
 
     const foo1Source = makeSource("foo1");
@@ -74,31 +73,34 @@ describe("loadSourceText", () => {
         })
       }
     );
     const { dispatch, getState } = store;
 
     await dispatch(actions.newSource(fooOrigSource));
     await dispatch(actions.newSource(fooGenSource));
 
-    const breakpoint = await dispatch(
-      addBreakpoint({
-        sourceId: fooOrigSource.id,
-        line: 1,
-        column: 0
-      })
-    );
+    const location = {
+      sourceId: fooOrigSource.id,
+      line: 1,
+      column: 0
+    };
+    await dispatch(actions.addBreakpoint(location, {}));
+    const breakpoint = selectors.getBreakpoint(getState(), location);
+    if (!breakpoint) {
+      throw new Error("no breakpoint");
+    }
 
-    expect(breakpoint.text).toBe("");
-    expect(breakpoint.originalText).toBe("");
+    expect(breakpoint.text).toBe("var fooGen = 42;");
+    expect(breakpoint.originalText).toBe("var fooOrig = 42;");
 
     await dispatch(actions.loadSourceText(fooOrigSource));
 
     const breakpoint1 = getBreakpointsList(getState())[0];
-    expect(breakpoint1.text).toBe("");
+    expect(breakpoint1.text).toBe("var fooGen = 42;");
     expect(breakpoint1.originalText).toBe("var fooOrig = 42;");
 
     await dispatch(actions.loadSourceText(fooGenSource));
 
     const breakpoint2 = getBreakpointsList(getState())[0];
     expect(breakpoint2.text).toBe("var fooGen = 42;");
     expect(breakpoint2.originalText).toBe("var fooOrig = 42;");
   });
--- a/devtools/client/debugger/new/src/actions/tests/__snapshots__/pending-breakpoints.spec.js.snap
+++ b/devtools/client/debugger/new/src/actions/tests/__snapshots__/pending-breakpoints.spec.js.snap
@@ -48,21 +48,17 @@ Object {
     "line": 5,
     "sourceUrl": "http://localhost:8000/examples/foo.js",
   },
   "location": Object {
     "column": 1,
     "line": 5,
     "sourceUrl": "http://localhost:8000/examples/foo.js",
   },
-  "options": Object {
-    "condition": null,
-    "hidden": false,
-    "logValue": null,
-  },
+  "options": Object {},
 }
 `;
 
 exports[`when adding breakpoints adding and deleting breakpoints add a corresponding pendingBreakpoint for each addition 1`] = `
 Object {
   "astLocation": Object {
     "index": 0,
     "name": undefined,
@@ -79,21 +75,17 @@ Object {
     "line": 5,
     "sourceUrl": "http://localhost:8000/examples/foo",
   },
   "location": Object {
     "column": 0,
     "line": 5,
     "sourceUrl": "http://localhost:8000/examples/foo",
   },
-  "options": Object {
-    "condition": null,
-    "hidden": false,
-    "logValue": null,
-  },
+  "options": Object {},
 }
 `;
 
 exports[`when adding breakpoints adding and deleting breakpoints add a corresponding pendingBreakpoint for each addition 2`] = `
 Object {
   "astLocation": Object {
     "index": 0,
     "name": undefined,
@@ -110,15 +102,11 @@ Object {
     "line": 5,
     "sourceUrl": "http://localhost:8000/examples/foo2",
   },
   "location": Object {
     "column": 0,
     "line": 5,
     "sourceUrl": "http://localhost:8000/examples/foo2",
   },
-  "options": Object {
-    "condition": null,
-    "hidden": false,
-    "logValue": null,
-  },
+  "options": Object {},
 }
 `;
--- a/devtools/client/debugger/new/src/actions/types/BreakpointAction.js
+++ b/devtools/client/debugger/new/src/actions/types/BreakpointAction.js
@@ -10,29 +10,16 @@ import type {
   XHRBreakpoint,
   Source,
   BreakpointPositions
 } from "../../types";
 
 import type { PromiseAction } from "../utils/middleware/promise";
 
 export type BreakpointAction =
-  | PromiseAction<
-      {|
-        +type: "ADD_BREAKPOINT",
-        +breakpoint: Breakpoint,
-        +condition?: string
-      |},
-      Breakpoint
-    >
-  | PromiseAction<{|
-      +type: "REMOVE_BREAKPOINT",
-      +breakpoint: Breakpoint,
-      +disabled: boolean
-    |}>
   | PromiseAction<{|
       +type: "SET_XHR_BREAKPOINT",
       +breakpoint: XHRBreakpoint
     |}>
   | PromiseAction<{|
       +type: "ENABLE_XHR_BREAKPOINT",
       +breakpoint: XHRBreakpoint,
       +index: number
@@ -48,57 +35,20 @@ export type BreakpointAction =
       +index: number
     |}>
   | PromiseAction<{|
       +type: "REMOVE_XHR_BREAKPOINT",
       +index: number,
       +breakpoint: XHRBreakpoint
     |}>
   | {|
-      +type: "REMOVE_BREAKPOINT",
-      +breakpoint: Breakpoint,
-      +status: "done"
-    |}
-  | {|
-      +type: "SET_BREAKPOINT_OPTIONS",
-      +breakpoint: Breakpoint
-    |}
-  | PromiseAction<{|
-      +type: "TOGGLE_BREAKPOINTS",
-      +shouldDisableBreakpoints: boolean
-    |}>
-  | {|
-      +type: "SYNC_BREAKPOINT",
-      +breakpoint: ?Breakpoint,
-      +previousLocation: SourceLocation
-    |}
-  | PromiseAction<
-      {|
-        +type: "ENABLE_BREAKPOINT",
-        +breakpoint: Breakpoint
-      |},
-      Breakpoint
-    >
-  | {|
-      +type: "DISABLE_BREAKPOINT",
+      +type: "SET_BREAKPOINT",
       +breakpoint: Breakpoint
     |}
   | {|
-      +type: "DISABLE_ALL_BREAKPOINTS",
-      +breakpoints: Breakpoint[]
-    |}
-  | {|
-      +type: "ENABLE_ALL_BREAKPOINTS",
-      +breakpoints: Breakpoint[]
-    |}
-  | {|
-      +type: "REMAP_BREAKPOINTS",
-      +breakpoints: Breakpoint[]
+      +type: "REMOVE_BREAKPOINT",
+      +location: SourceLocation
     |}
   | {|
       type: "ADD_BREAKPOINT_POSITIONS",
       positions: BreakpointPositions,
       source: Source
-    |}
-  | {|
-      +type: "UPDATE_BREAKPOINT_TEXT",
-      +source: Source
     |};
--- a/devtools/client/debugger/new/src/client/firefox/commands.js
+++ b/devtools/client/debugger/new/src/client/firefox/commands.js
@@ -95,16 +95,19 @@ function lookupConsoleClient(thread: str
 
 function listWorkerThreadClients() {
   return (Object.values(workerClients): any).map(({ thread }) => thread);
 }
 
 function forEachWorkerThread(iteratee) {
   const promises = listWorkerThreadClients().map(thread => iteratee(thread));
 
+  // Do not return promises for the caller to wait on unless a flag is set.
+  // Currently, worker threads are not guaranteed to respond to all requests,
+  // if we send a request while they are shutting down. See bug 1529163.
   if (shouldWaitForWorkers) {
     return Promise.all(promises);
   }
 }
 
 function resume(thread: string): Promise<*> {
   return new Promise(resolve => {
     lookupThreadClient(thread).resume(resolve);
@@ -205,42 +208,49 @@ function maybeClearLogpoint(location: Br
   if (bp && bp.options.logGroupId && tabTarget.activeConsole) {
     tabTarget.activeConsole.emit(
       "clearLogpointMessages",
       bp.options.logGroupId
     );
   }
 }
 
+function hasBreakpoint(location: BreakpointLocation) {
+  return !!breakpoints[locationKey(location)];
+}
+
 async function setBreakpoint(
   location: BreakpointLocation,
   options: BreakpointOptions
 ) {
   maybeClearLogpoint(location);
   options = maybeGenerateLogGroupId(options);
   breakpoints[locationKey(location)] = { location, options };
-  await threadClient.setBreakpoint(location, options);
 
-  // Set breakpoints in other threads as well, but do not wait for the requests
-  // to complete, so that we don't get hung up if one of the threads stops
-  // responding. We don't strictly need to wait for the main thread to finish
-  // setting its breakpoint, but this leads to more consistent behavior if the
-  // user sets a breakpoint and immediately starts interacting with the page.
-  // If the main thread stops responding then we're toast regardless.
+  // We have to be careful here to atomically initiate the setBreakpoint() call
+  // on every thread, with no intervening await. Otherwise, other code could run
+  // and change or remove the breakpoint before we finish calling setBreakpoint
+  // on all threads. Requests on server threads will resolve in FIFO order, and
+  // this could result in the breakpoint state here being out of sync with the
+  // breakpoints that are installed in the server.
+  const mainThreadPromise = threadClient.setBreakpoint(location, options);
+
   await forEachWorkerThread(thread => thread.setBreakpoint(location, options));
+  await mainThreadPromise;
 }
 
 async function removeBreakpoint(location: PendingLocation) {
   maybeClearLogpoint((location: any));
   delete breakpoints[locationKey((location: any))];
-  await threadClient.removeBreakpoint(location);
 
-  // Remove breakpoints without waiting for the thread to respond, for the same
-  // reason as in setBreakpoint.
+  // Delay waiting on this promise, for the same reason as in setBreakpoint.
+  const mainThreadPromise = threadClient.removeBreakpoint(location);
+
   await forEachWorkerThread(thread => thread.removeBreakpoint(location));
+  await mainThreadPromise;
 }
 
 async function evaluateInFrame(script: Script, options: EvaluateParam) {
   return evaluate(script, options);
 }
 
 async function evaluateExpressions(scripts: Script[], options: EvaluateParam) {
   return Promise.all(scripts.map(script => evaluate(script, options)));
@@ -479,16 +489,17 @@ const clientCommands = {
   rewind,
   reverseStepIn,
   reverseStepOut,
   reverseStepOver,
   breakOnNext,
   sourceContents,
   getSourceForActor,
   getBreakpointPositions,
+  hasBreakpoint,
   setBreakpoint,
   setXHRBreakpoint,
   removeXHRBreakpoint,
   removeBreakpoint,
   evaluate,
   evaluateInFrame,
   evaluateExpressions,
   navigate,
--- a/devtools/client/debugger/new/src/client/firefox/types.js
+++ b/devtools/client/debugger/new/src/client/firefox/types.js
@@ -354,22 +354,15 @@ export type ThreadClient = {
   _parent: TabClient,
   actor: ActorId,
   request: (payload: Object) => Promise<*>,
   url: string,
   setEventListenerBreakpoints: (string[]) => void,
   skipBreakpoints: boolean => Promise<{| skip: boolean |}>
 };
 
-export type FirefoxClientConnection = {
-  getTabTarget: () => TabTarget,
-  getThreadClient: () => ThreadClient,
-  setTabTarget: (target: TabTarget) => void,
-  setThreadClient: (client: ThreadClient) => void
-};
-
 export type Panel = {|
   emit: (eventName: string) => void,
   openLink: (url: string) => void,
   openWorkerToolbox: (worker: Worker) => void,
   openElementInInspector: (grip: Object) => void,
   openConsoleAndEvaluate: (input: string) => void
 |};
--- a/devtools/client/debugger/new/src/client/index.js
+++ b/devtools/client/debugger/new/src/client/index.js
@@ -79,17 +79,17 @@ export async function onConnect(
     sourceMaps,
     panel,
     initialState
   );
 
   const workers = bootstrapWorkers();
   await client.onConnect(connection, actions);
 
-  syncBreakpoints();
+  await syncBreakpoints();
   syncXHRBreakpoints();
   setupHelper({
     store,
     actions,
     selectors,
     workers: { ...workers, sourceMaps },
     connection,
     client: client.clientCommands
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/tests/BreakpointsContextMenu.spec.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/tests/BreakpointsContextMenu.spec.js
@@ -3,79 +3,60 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React from "react";
 import { shallow } from "enzyme";
 
 import BreakpointsContextMenu from "../BreakpointsContextMenu";
-import { createBreakpoint } from "../../../../utils/breakpoint";
 import { buildMenu } from "devtools-contextmenu";
 
 import {
-  makeMockSource,
-  makeMappedLocation
+  makeMockBreakpoint,
+  makeMockSource
 } from "../../../../utils/test-mockup";
 
 jest.mock("devtools-contextmenu");
 
 function render(disabled = false) {
   const props = generateDefaults(disabled);
   const component = shallow(<BreakpointsContextMenu {...props} />);
   return { component, props };
 }
 
 function generateDefaults(disabled) {
+  const source = makeMockSource(
+    "https://example.com/main.js",
+    "source-https://example.com/main.js"
+  );
   const breakpoints = [
-    createBreakpoint(
-      makeMappedLocation({
-        line: 1,
-        column: undefined,
-        sourceId: "source-https://example.com/main.js",
-        sourceUrl: "https://example.com/main.js"
-      }),
-      {
-        id: "https://example.com/main.js:1:",
-        disabled: disabled,
-        options: {
-          condition: "",
-          logValue: "",
-          hidden: false
-        }
+    {
+      ...makeMockBreakpoint(source, 1),
+      id: "https://example.com/main.js:1:",
+      disabled: disabled,
+      options: {
+        condition: "",
+        logValue: "",
+        hidden: false
       }
-    ),
-    createBreakpoint(
-      makeMappedLocation({
-        line: 2,
-        column: undefined,
-        sourceId: "source-https://example.com/main.js",
-        sourceUrl: "https://example.com/main.js"
-      }),
-      {
-        id: "https://example.com/main.js:2:",
-        disabled: disabled,
-        options: {
-          hidden: false
-        }
+    },
+    {
+      ...makeMockBreakpoint(source, 2),
+      id: "https://example.com/main.js:2:",
+      disabled: disabled,
+      options: {
+        hidden: false
       }
-    ),
-    createBreakpoint(
-      makeMappedLocation({
-        line: 3,
-        column: undefined,
-        sourceId: "source-https://example.com/main.js",
-        sourceUrl: "https://example.com/main.js"
-      }),
-      {
-        id: "https://example.com/main.js:3:",
-        disabled: disabled,
-        options: {}
-      }
-    )
+    },
+    {
+      ...makeMockBreakpoint(source, 3),
+      id: "https://example.com/main.js:3:",
+      disabled: disabled
+    }
   ];
 
   const props = {
     breakpoints,
     breakpoint: breakpoints[0],
     removeBreakpoint: jest.fn(),
     removeBreakpoints: jest.fn(),
     removeAllBreakpoints: jest.fn(),
--- a/devtools/client/debugger/new/src/reducers/breakpoints.js
+++ b/devtools/client/debugger/new/src/reducers/breakpoints.js
@@ -9,31 +9,30 @@
  * @module reducers/breakpoints
  */
 
 import { isGeneratedId } from "devtools-source-map";
 import { isEqual } from "lodash";
 
 import { makeBreakpointId, findPosition } from "../utils/breakpoint";
 import { findEmptyLines } from "../utils/empty-lines";
-import { getTextAtPosition, isInlineScript } from "../utils/source";
+import { isInlineScript } from "../utils/source";
 
 // eslint-disable-next-line max-len
 import { getBreakpointsList as getBreakpointsListSelector } from "../selectors/breakpoints";
 
 import type {
   XHRBreakpoint,
   Breakpoint,
   BreakpointId,
   MappedLocation,
-  Source,
   SourceLocation,
   BreakpointPositions
 } from "../types";
-import type { Action, DonePromiseAction } from "../actions/types";
+import type { Action } from "../actions/types";
 
 export type BreakpointsMap = { [BreakpointId]: Breakpoint };
 export type XHRBreakpointsList = $ReadOnlyArray<XHRBreakpoint>;
 export type BreakpointPositionsMap = { [string]: BreakpointPositions };
 
 export type BreakpointsState = {
   breakpoints: BreakpointsMap,
   breakpointPositions: BreakpointPositionsMap,
@@ -54,76 +53,24 @@ export function initialBreakpointsState(
   };
 }
 
 function update(
   state: BreakpointsState = initialBreakpointsState(),
   action: Action
 ): BreakpointsState {
   switch (action.type) {
-    case "UPDATE_BREAKPOINT_TEXT": {
-      return updateBreakpointText(state, action.source);
-    }
-
-    case "ADD_SOURCES": {
-      const { sources } = action;
-
-      const scriptSources = sources.filter(source => isInlineScript(source));
-
-      if (scriptSources.length > 0) {
-        const { ...breakpointPositions } = state.breakpointPositions;
-
-        // If new HTML sources are being added, we need to clear the breakpoint
-        // positions since the new source is a <script> with new breakpoints.
-        for (const source of scriptSources) {
-          delete breakpointPositions[source.id];
-        }
-
-        state = { ...state, breakpointPositions };
-      }
-
-      return state;
-    }
-
-    case "ADD_BREAKPOINT": {
-      return addBreakpoint(state, action);
-    }
-
-    case "SYNC_BREAKPOINT": {
-      return syncBreakpoint(state, action);
-    }
-
-    case "ENABLE_BREAKPOINT": {
-      return addBreakpoint(state, action);
-    }
-
-    case "DISABLE_BREAKPOINT": {
-      return updateBreakpoint(state, action);
-    }
-
-    case "DISABLE_ALL_BREAKPOINTS": {
-      return updateAllBreakpoints(state, action);
-    }
-
-    case "ENABLE_ALL_BREAKPOINTS": {
-      return updateAllBreakpoints(state, action);
-    }
-
-    case "SET_BREAKPOINT_OPTIONS": {
-      return updateBreakpoint(state, action);
+    case "SET_BREAKPOINT": {
+      return setBreakpoint(state, action);
     }
 
     case "REMOVE_BREAKPOINT": {
       return removeBreakpoint(state, action);
     }
 
-    case "REMAP_BREAKPOINTS": {
-      return remapBreakpoints(state, action);
-    }
-
     case "NAVIGATE": {
       return initialBreakpointsState(state.xhrBreakpoints);
     }
 
     case "SET_XHR_BREAKPOINT": {
       return addXHRBreakpoint(state, action);
     }
 
@@ -154,63 +101,36 @@ function update(
           [source.id]: positions
         },
         emptyLines: {
           ...state.emptyLines,
           [source.id]: emptyLines
         }
       };
     }
-  }
+
+    case "ADD_SOURCES": {
+      const { sources } = action;
 
-  return state;
-}
+      const scriptSources = sources.filter(source => isInlineScript(source));
+
+      if (scriptSources.length > 0) {
+        const { ...breakpointPositions } = state.breakpointPositions;
 
-function updateBreakpointText(
-  state: BreakpointsState,
-  source: Source
-): BreakpointsState {
-  const updates = [];
-  for (const id of Object.keys(state.breakpoints)) {
-    const breakpoint = state.breakpoints[id];
-    const { location, generatedLocation } = breakpoint;
-    let { text, originalText } = breakpoint;
-    let needsUpdate = false;
+        // If new HTML sources are being added, we need to clear the breakpoint
+        // positions since the new source is a <script> with new breakpoints.
+        for (const source of scriptSources) {
+          delete breakpointPositions[source.id];
+        }
 
-    if (location.sourceId === source.id) {
-      const result = getTextAtPosition(source, location);
-      if (result !== originalText) {
-        originalText = result;
-        needsUpdate = true;
+        state = { ...state, breakpointPositions };
       }
+
+      return state;
     }
-    if (generatedLocation.sourceId === source.id) {
-      const result = getTextAtPosition(source, generatedLocation);
-      if (result !== text) {
-        text = result;
-        needsUpdate = true;
-      }
-    }
-
-    if (needsUpdate) {
-      updates.push({ id, text, originalText });
-    }
-  }
-
-  if (updates.length > 0) {
-    const { ...breakpoints } = state.breakpoints;
-
-    for (const { id, text, originalText } of updates) {
-      breakpoints[id] = { ...breakpoints[id], text, originalText };
-    }
-
-    state = {
-      ...state,
-      breakpoints
-    };
   }
 
   return state;
 }
 
 function addXHRBreakpoint(state, action) {
   const { xhrBreakpoints } = state;
   const { breakpoint } = action;
@@ -257,108 +177,27 @@ function updateXHRBreakpoint(state, acti
   const newXhrBreakpoints = [...xhrBreakpoints];
   newXhrBreakpoints[index] = breakpoint;
   return {
     ...state,
     xhrBreakpoints: newXhrBreakpoints
   };
 }
 
-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 = makeBreakpointId(breakpoint.location);
-    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, { breakpoint: value, previousLocation: null });
-  }
-
-  // Remove the optimistic update
-  if (action.status === "error" && action.breakpoint) {
-    const locationId = makeBreakpointId(action.breakpoint.location);
-    return unsetBreakpoint(state, locationId);
-  }
-
-  return state;
-}
-
-function syncBreakpoint(state, data): BreakpointsState {
-  const { breakpoint, previousLocation } = data;
-
-  if (previousLocation) {
-    state = {
-      ...state,
-      breakpoints: { ...state.breakpoints }
-    };
-    delete state.breakpoints[makeBreakpointId(previousLocation)];
-  }
-
-  if (!breakpoint) {
-    return state;
-  }
-
-  const locationId = makeBreakpointId(breakpoint.location);
-  return setBreakpoint(state, locationId, breakpoint);
-}
-
-function updateBreakpoint(state, action): BreakpointsState {
-  const { breakpoint } = action;
-  const locationId = makeBreakpointId(breakpoint.location);
-  return setBreakpoint(state, locationId, breakpoint);
-}
-
-function updateAllBreakpoints(state, action): BreakpointsState {
-  const { breakpoints } = action;
-  state = {
-    ...state,
-    breakpoints: { ...state.breakpoints }
-  };
-  breakpoints.forEach(breakpoint => {
-    const locationId = makeBreakpointId(breakpoint.location);
-    state.breakpoints[locationId] = breakpoint;
-  });
-  return state;
-}
-
-function remapBreakpoints(state, action): BreakpointsState {
-  const breakpoints = action.breakpoints.reduce(
-    (updatedBreakpoints, breakpoint) => {
-      const locationId = makeBreakpointId(breakpoint.location);
-      return { ...updatedBreakpoints, [locationId]: breakpoint };
-    },
-    {}
-  );
-
+function setBreakpoint(state, { breakpoint }): BreakpointsState {
+  const id = makeBreakpointId(breakpoint.location);
+  const breakpoints = { ...state.breakpoints, [id]: breakpoint };
   return { ...state, breakpoints };
 }
 
-function removeBreakpoint(state, action): BreakpointsState {
-  const { breakpoint } = action;
-  const id = makeBreakpointId(breakpoint.location);
-  return unsetBreakpoint(state, id);
+function removeBreakpoint(state, { location }): BreakpointsState {
+  const id = makeBreakpointId(location);
+  const breakpoints = { ...state.breakpoints };
+  delete breakpoints[id];
+  return { ...state, breakpoints };
 }
 
 function isMatchingLocation(location1, location2) {
   return isEqual(location1, location2);
 }
 
 // Selectors
 // TODO: these functions should be moved out of the reducer
--- a/devtools/client/debugger/new/src/reducers/pending-breakpoints.js
+++ b/devtools/client/debugger/new/src/reducers/pending-breakpoints.js
@@ -14,125 +14,46 @@ import { getSourcesByURL } from "./sourc
 import {
   createPendingBreakpoint,
   makePendingLocationId
 } from "../utils/breakpoint";
 import { isGenerated } from "../utils/source";
 
 import type { SourcesState } from "./sources";
 import type { PendingBreakpoint, Source } from "../types";
-import type { Action, DonePromiseAction } from "../actions/types";
+import type { Action } from "../actions/types";
 
 export type PendingBreakpointsState = { [string]: PendingBreakpoint };
 
 function update(state: PendingBreakpointsState = {}, action: Action) {
   switch (action.type) {
-    case "ADD_BREAKPOINT": {
-      return addBreakpoint(state, action);
-    }
-
-    case "SYNC_BREAKPOINT": {
-      return syncBreakpoint(state, action);
-    }
-
-    case "ENABLE_BREAKPOINT": {
-      return addBreakpoint(state, action);
-    }
-
-    case "DISABLE_BREAKPOINT": {
-      return updateBreakpoint(state, action);
-    }
+    case "SET_BREAKPOINT":
+      return setBreakpoint(state, action);
 
-    case "DISABLE_ALL_BREAKPOINTS": {
-      return updateAllBreakpoints(state, action);
-    }
-
-    case "ENABLE_ALL_BREAKPOINTS": {
-      return updateAllBreakpoints(state, action);
-    }
-
-    case "SET_BREAKPOINT_OPTIONS": {
-      return updateBreakpoint(state, action);
-    }
-
-    case "REMOVE_BREAKPOINT": {
-      if (action.breakpoint.options.hidden) {
-        return state;
-      }
+    case "REMOVE_BREAKPOINT":
       return removeBreakpoint(state, action);
-    }
   }
 
   return state;
 }
 
-function addBreakpoint(state, action) {
-  if (action.breakpoint.options.hidden || action.status !== "done") {
-    return state;
-  }
-  // when the action completes, we can commit the breakpoint
-  const breakpoint = ((action: any): DonePromiseAction).value;
-
-  const locationId = makePendingLocationId(breakpoint.location);
-  const pendingBreakpoint = createPendingBreakpoint(breakpoint);
-
-  return { ...state, [locationId]: pendingBreakpoint };
-}
-
-function syncBreakpoint(state, action) {
-  const { breakpoint, previousLocation } = action;
-
-  if (previousLocation) {
-    const previousLocationId = makePendingLocationId(previousLocation);
-    state = deleteBreakpoint(state, previousLocationId);
-  }
-
-  if (!breakpoint) {
+function setBreakpoint(state, { breakpoint }) {
+  if (breakpoint.options.hidden) {
     return state;
   }
 
   const locationId = makePendingLocationId(breakpoint.location);
   const pendingBreakpoint = createPendingBreakpoint(breakpoint);
 
   return { ...state, [locationId]: pendingBreakpoint };
 }
 
-function updateBreakpoint(state, action) {
-  const { breakpoint } = action;
-  const locationId = makePendingLocationId(breakpoint.location);
-  const pendingBreakpoint = createPendingBreakpoint(breakpoint);
-
-  return { ...state, [locationId]: pendingBreakpoint };
-}
-
-function updateAllBreakpoints(state, action) {
-  const { breakpoints } = action;
-  breakpoints.forEach(breakpoint => {
-    const locationId = makePendingLocationId(breakpoint.location);
-    const pendingBreakpoint = createPendingBreakpoint(breakpoint);
+function removeBreakpoint(state, { location }) {
+  const locationId = makePendingLocationId(location);
 
-    state = { ...state, [locationId]: pendingBreakpoint };
-  });
-  return state;
-}
-
-function removeBreakpoint(state, action) {
-  const { breakpoint } = action;
-
-  const locationId = makePendingLocationId(breakpoint.location);
-  const pendingBp = state[locationId];
-
-  if (!pendingBp && action.status == "start") {
-    return {};
-  }
-
-  return deleteBreakpoint(state, locationId);
-}
-
-function deleteBreakpoint(state, locationId) {
   state = { ...state };
   delete state[locationId];
   return state;
 }
 
 // Selectors
 // TODO: these functions should be moved out of the reducer
 
--- a/devtools/client/debugger/new/src/reducers/tests/breakpoints.spec.js
+++ b/devtools/client/debugger/new/src/reducers/tests/breakpoints.spec.js
@@ -7,40 +7,33 @@ declare var describe: (name: string, fun
 declare var it: (desc: string, func: () => void) => void;
 declare var expect: (value: any) => any;
 
 import {
   getBreakpointsForSource,
   initialBreakpointsState
 } from "../breakpoints";
 
-import { createBreakpoint } from "../../utils/breakpoint";
-import { makeMappedLocation } from "../../utils/test-mockup";
+import { makeMockBreakpoint, makeMockSource } from "../../utils/test-mockup";
 
 function initializeStateWith(data) {
   const state = initialBreakpointsState();
   state.breakpoints = data;
   return state;
 }
 
 describe("Breakpoints Selectors", () => {
   it("it gets a breakpoint for an original source", () => {
     const sourceId = "server1.conn1.child1/source1/originalSource";
     const matchingBreakpoints = {
-      id1: createBreakpoint(
-        makeMappedLocation({ line: 1, sourceId: sourceId }),
-        { options: {} }
-      )
+      id1: makeMockBreakpoint(makeMockSource(undefined, sourceId), 1)
     };
 
     const otherBreakpoints = {
-      id2: createBreakpoint(
-        makeMappedLocation({ line: 1, sourceId: "not-this-source" }),
-        { options: {} }
-      )
+      id2: makeMockBreakpoint(makeMockSource(undefined, "not-this-source"), 1)
     };
 
     const data = {
       ...matchingBreakpoints,
       ...otherBreakpoints
     };
 
     const breakpoints = initializeStateWith(data);
@@ -52,37 +45,27 @@ describe("Breakpoints Selectors", () => 
 
     expect(sourceBreakpoints).toEqual(allBreakpoints);
     expect(sourceBreakpoints[0] === allBreakpoints[0]).toBe(true);
   });
 
   it("it gets a breakpoint for a generated source", () => {
     const generatedSourceId = "random-source";
     const matchingBreakpoints = {
-      id1: createBreakpoint(
-        makeMappedLocation(
-          { line: 1, sourceId: "original-source-id-1" },
-          { line: 1, sourceId: generatedSourceId }
-        ),
-        {
-          options: {}
-        }
-      )
+      id1: {
+        ...makeMockBreakpoint(makeMockSource(undefined, generatedSourceId), 1),
+        location: { line: 1, sourceId: "original-source-id-1" }
+      }
     };
 
     const otherBreakpoints = {
-      id2: createBreakpoint(
-        makeMappedLocation(
-          { line: 1, sourceId: "original-source-id-2" },
-          { line: 1, sourceId: "not-this-source" }
-        ),
-        {
-          options: {}
-        }
-      )
+      id2: {
+        ...makeMockBreakpoint(makeMockSource(undefined, "not-this-source"), 1),
+        location: { line: 1, sourceId: "original-source-id-2" }
+      }
     };
 
     const data = {
       ...matchingBreakpoints,
       ...otherBreakpoints
     };
 
     const breakpoints = initializeStateWith(data);
--- a/devtools/client/debugger/new/src/utils/breakpoint/index.js
+++ b/devtools/client/debugger/new/src/utils/breakpoint/index.js
@@ -16,18 +16,17 @@ export * from "./breakpointPositions";
 import type {
   Source,
   SourceActor,
   SourceLocation,
   SourceActorLocation,
   PendingLocation,
   Breakpoint,
   BreakpointLocation,
-  PendingBreakpoint,
-  MappedLocation
+  PendingBreakpoint
 } from "../../types";
 
 import type { State } from "../../reducers/types";
 
 // Return the first argument that is a string, or null if nothing is a
 // string.
 export function firstString(...args: string[]) {
   for (const arg of args) {
@@ -148,44 +147,16 @@ export function breakpointAtLocation(
   });
 }
 
 export function breakpointExists(state: State, location: SourceLocation) {
   const currentBp = getBreakpoint(state, location);
   return currentBp && !currentBp.disabled;
 }
 
-export function createBreakpoint(
-  mappedLocation: MappedLocation,
-  overrides: Object = {}
-): Breakpoint {
-  const { disabled, astLocation, text, originalText, options } = overrides;
-
-  const defaultASTLocation = {
-    name: undefined,
-    offset: mappedLocation.location,
-    index: 0
-  };
-  const properties = {
-    id: makeBreakpointId(mappedLocation.location),
-    ...mappedLocation,
-    options: {
-      condition: options.condition || null,
-      logValue: options.logValue || null,
-      hidden: options.hidden || false
-    },
-    disabled: disabled || false,
-    astLocation: astLocation || defaultASTLocation,
-    text,
-    originalText
-  };
-
-  return properties;
-}
-
 export function createXHRBreakpoint(
   path: string,
   method: string,
   overrides?: Object = {}
 ) {
   const properties = {
     path,
     method,
--- a/devtools/client/debugger/new/src/utils/test-mockup.js
+++ b/devtools/client/debugger/new/src/utils/test-mockup.js
@@ -17,17 +17,16 @@ import type {
   Expression,
   Frame,
   FrameId,
   Scope,
   JsSource,
   WasmSource,
   Source,
   SourceId,
-  SourceLocation,
   Why
 } from "../types";
 
 function makeMockSource(
   url: string = "url",
   id: SourceId = "source",
   contentType: string = "text/javascript",
   text: string = ""
@@ -150,24 +149,16 @@ function makeMockExpression(value: Objec
   return {
     input: "input",
     value,
     from: "from",
     updating: false
   };
 }
 
-export function makeMappedLocation(
-  location: SourceLocation,
-  generatedLocation: ?SourceLocation
-) {
-  generatedLocation = generatedLocation || location;
-  return { location, generatedLocation };
-}
-
 export {
   makeMockSource,
   makeMockWasmSource,
   makeMockScope,
   mockScopeAddVariable,
   makeMockBreakpoint,
   makeMockFrame,
   makeMockFrameWithURL,
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
@@ -33,43 +33,43 @@ add_task(async function() {
 
   await addBreakpoint(dbg, "simple1", 4);
   await addBreakpoint(dbg, "simple1", 5);
   await addBreakpoint(dbg, "simple1", 6);
 
   openFirstBreakpointContextMenu(dbg);
   // select "Disable Others"
   // FIXME bug 1524374 this waitForDispatch call only sees one dispatch for
-  // DISABLE_BREAKPOINT even though three are triggered, due to the order in
+  // SET_BREAKPOINT even though three are triggered, due to the order in
   // which promises get resolved. The problem seems to indicate a coverage gap
   // in waitUntilService(). Workaround this by only waiting for one dispatch,
   // though this is fragile and could break again in the future.
-  let dispatched = waitForDispatch(dbg, "DISABLE_BREAKPOINT", /* 2*/ 1);
+  let dispatched = waitForDispatch(dbg, "SET_BREAKPOINT", /* 2*/ 1);
   selectContextMenuItem(dbg, selectors.breakpointContextMenu.disableOthers);
   await waitForState(dbg, state =>
     dbg.selectors
       .getBreakpointsList(state)
       .every(bp => (bp.location.line !== 4) === bp.disabled)
   );
   await dispatched;
   ok("breakpoint at 4 is the only enabled breakpoint");
 
   openFirstBreakpointContextMenu(dbg);
   // select "Disable All"
-  dispatched = waitForDispatch(dbg, "DISABLE_ALL_BREAKPOINTS");
+  dispatched = waitForDispatch(dbg, "SET_BREAKPOINT");
   selectContextMenuItem(dbg, selectors.breakpointContextMenu.disableAll);
   await waitForState(dbg, state =>
     dbg.selectors.getBreakpointsList(state).every(bp => bp.disabled)
   );
   await dispatched;
   ok("all breakpoints are disabled");
 
   openFirstBreakpointContextMenu(dbg);
   // select "Enable Others"
-  dispatched = waitForDispatch(dbg, "ENABLE_BREAKPOINT", 2);
+  dispatched = waitForDispatch(dbg, "SET_BREAKPOINT", 2);
   selectContextMenuItem(dbg, selectors.breakpointContextMenu.enableOthers);
   await waitForState(dbg, state =>
     dbg.selectors
       .getBreakpointsList(state)
       .every(bp => (bp.location.line === 4) === bp.disabled)
   );
   await dispatched;
   ok("all breakpoints except line 1 are enabled");
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-cond.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-cond.js
@@ -108,17 +108,17 @@ add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html", "simple2");
   await pushPref("devtools.debugger.features.column-breakpoints", true);
   await pushPref("devtools.debugger.features.log-points", true);
 
   await selectSource(dbg, "simple2");
   await waitForSelectedSource(dbg, "simple2");
 
   await setConditionalBreakpoint(dbg, 5, "1");
-  await waitForDispatch(dbg, "ADD_BREAKPOINT");
+  await waitForDispatch(dbg, "SET_BREAKPOINT");
   await waitForBreakpointWithCondition(dbg, "simple2", 5);
 
   let bp = findBreakpoint(dbg, "simple2", 5);
   is(bp.options.condition, "1", "breakpoint is created with the condition");
   await assertEditorBreakpoint(dbg, 5, { hasCondition: true });
 
   info("Edit the conditional breakpoint set above");
   await setConditionalBreakpoint(dbg, 5, "2");
@@ -131,17 +131,17 @@ add_task(async function() {
   clickElement(dbg, "gutter", 5);
   await waitForDispatch(dbg, "REMOVE_BREAKPOINT");
   bp = findBreakpoint(dbg, "simple2", 5);
   is(bp, null, "breakpoint was removed");
   await assertEditorBreakpoint(dbg, 5);
 
   info("Adding a condition to a breakpoint");
   clickElement(dbg, "gutter", 5);
-  await waitForDispatch(dbg, "ADD_BREAKPOINT");
+  await waitForDispatch(dbg, "SET_BREAKPOINT");
   await setConditionalBreakpoint(dbg, 5, "1");
   await waitForBreakpointWithCondition(dbg, "simple2", 5);
 
   bp = findBreakpoint(dbg, "simple2", 5);
   is(bp.options.condition, "1", "breakpoint is created with the condition");
   await assertEditorBreakpoint(dbg, 5, { hasCondition: true });
 
   rightClickElement(dbg, "breakpointItem", 3);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-reloading.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-reloading.js
@@ -12,33 +12,33 @@ function clickGutter(dbg, line) {
 
 function getLineEl(dbg, line) {
   const lines = dbg.win.document.querySelectorAll(".CodeMirror-code > div");
   return lines[line - 1];
 }
 
 function addBreakpoint(dbg, line) {
   clickGutter(dbg, line);
-  return waitForDispatch(dbg, "ADD_BREAKPOINT");
+  return waitForDispatch(dbg, "SET_BREAKPOINT");
 }
 
 function assertEditorBreakpoint(dbg, line) {
   const lineEl = getLineEl(dbg, line);
   const exists = lineEl.classList.contains("new-breakpoint");
   ok(exists, `Breakpoint exists on line ${line}`);
 }
 
 add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html", "simple1.js");
   const source = findSource(dbg, "simple1.js");
 
   await selectSource(dbg, source.url);
   await addBreakpoint(dbg, 5);
   await addBreakpoint(dbg, 4);
 
-  const syncedBps = waitForDispatch(dbg, "SYNC_BREAKPOINT", 2);
+  const syncedBps = waitForDispatch(dbg, "SET_BREAKPOINT", 2);
   await reload(dbg, "simple1");
   await waitForSelectedSource(dbg, "simple1");
   await syncedBps;
 
   assertEditorBreakpoint(dbg, 4);
   assertEditorBreakpoint(dbg, 5);
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints.js
@@ -4,23 +4,23 @@
 
 function toggleBreakpoint(dbg, index) {
   const bp = findAllElements(dbg, "breakpointItems")[index];
   const input = bp.querySelector("input");
   input.click();
 }
 
 async function disableBreakpoint(dbg, index) {
-  const disabled = waitForDispatch(dbg, "DISABLE_BREAKPOINT");
+  const disabled = waitForDispatch(dbg, "SET_BREAKPOINT");
   toggleBreakpoint(dbg, index);
   await disabled;
 }
 
 async function enableBreakpoint(dbg, index) {
-  const enabled = waitForDispatch(dbg, "ENABLE_BREAKPOINT");
+  const enabled = waitForDispatch(dbg, "SET_BREAKPOINT");
   toggleBreakpoint(dbg, index);
   await enabled;
 }
 
 // Test enabling and disabling a breakpoint using the check boxes
 add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html", "simple2");
 
@@ -49,27 +49,27 @@ add_task(async function() {
   await selectSource(dbg, "simple2");
   await addBreakpoint(dbg, "simple2", 3);
   await addBreakpoint(dbg, "simple2", 5);
 
   assertEmptyLines(dbg, [1, 2]);
   assertBreakpointSnippet(dbg, 3, "return x + y;");
 
   rightClickElement(dbg, "breakpointItem", 3);
-  const disableBreakpointDispatch = waitForDispatch(dbg, "DISABLE_BREAKPOINT");
+  const disableBreakpointDispatch = waitForDispatch(dbg, "SET_BREAKPOINT");
   selectContextMenuItem(dbg, selectors.breakpointContextMenu.disableSelf);
   await disableBreakpointDispatch;
 
   let bp1 = findBreakpoint(dbg, "simple2", 3);
   let bp2 = findBreakpoint(dbg, "simple2", 5);
   is(bp1.disabled, true, "first breakpoint is disabled");
   is(bp2.disabled, false, "second breakpoint is enabled");
 
   rightClickElement(dbg, "breakpointItem", 3);
-  const enableBreakpointDispatch = waitForDispatch(dbg, "ENABLE_BREAKPOINT");
+  const enableBreakpointDispatch = waitForDispatch(dbg, "SET_BREAKPOINT");
   selectContextMenuItem(dbg, selectors.breakpointContextMenu.enableSelf);
   await enableBreakpointDispatch;
 
   bp1 = findBreakpoint(dbg, "simple2", 3);
   bp2 = findBreakpoint(dbg, "simple2", 5);
   is(bp1.disabled, false, "first breakpoint is enabled");
   is(bp2.disabled, false, "second breakpoint is enabled");
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-browser-content-toolbox.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-browser-content-toolbox.js
@@ -15,23 +15,23 @@ const {
 
 function toggleBreakpoint(dbg, index) {
   const bp = findAllElements(dbg, "breakpointItems")[index];
   const input = bp.querySelector("input");
   input.click();
 }
 
 async function disableBreakpoint(dbg, index) {
-  const disabled = waitForDispatch(dbg, "DISABLE_BREAKPOINT");
+  const disabled = waitForDispatch(dbg, "SET_BREAKPOINT");
   toggleBreakpoint(dbg, index);
   await disabled;
 }
 
 async function enableBreakpoint(dbg, index) {
-  const enabled = waitForDispatch(dbg, "ENABLE_BREAKPOINT");
+  const enabled = waitForDispatch(dbg, "SET_BREAKPOINT");
   toggleBreakpoint(dbg, index);
   await enabled;
 }
 
 add_task(async function() {
   clearDebuggerPreferences();
 
   info("Open a tab pointing to doc-scripts.html");
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-gutter.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-gutter.js
@@ -21,17 +21,17 @@ add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html", "simple1.js");
   const { 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");
+  await waitForDispatch(dbg, "SET_BREAKPOINT");
   is(dbg.selectors.getBreakpointCount(getState()), 1, "One breakpoint exists");
   await assertEditorBreakpoint(dbg, 4, true);
 
   // Make sure clicking at the same place removes the icon.
   clickGutter(dbg, 4);
   await waitForDispatch(dbg, "REMOVE_BREAKPOINT");
   is(dbg.selectors.getBreakpointCount(getState()), 0, "No breakpoints exist");
   await assertEditorBreakpoint(dbg, 4, false);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps-reload.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps-reload.js
@@ -34,31 +34,33 @@ add_task(async function() {
   info("Add initial breakpoint");
   await selectSource(dbg, "v1.js");
   await addBreakpoint(dbg, "v1.js", 6);
 
   let breakpoint = getBreakpoints(dbg)[0];
   is(breakpoint.location.line, 6);
 
   info("Reload with a new version of the file");
-  let syncBp = waitForDispatch(dbg, "SYNC_BREAKPOINT");
+  let syncBp = waitForDispatch(dbg, "SET_BREAKPOINT");
   await navigate(dbg, "doc-sourcemaps-reload2.html", "v1.js");
 
   await syncBp;
   breakpoint = getBreakpoints(dbg)[0];
 
   is(breakpoint.location.line, 9);
   is(breakpoint.generatedLocation.line, 79);
 
   info("Add a second breakpoint");
   await addBreakpoint(dbg, "v1.js", 13);
   is(getBreakpointCount(dbg), 2, "No breakpoints");
 
   // NOTE: When we reload, the `foo` function and the
   // module is no longer 13 lines long
   info("Reload and observe no breakpoints");
-  syncBp = waitForDispatch(dbg, "SYNC_BREAKPOINT", 2);
   await navigate(dbg, "doc-sourcemaps-reload3.html", "v1.js");
   await waitForSource(dbg, "v1");
-  await syncBp;
+
+  // There will initially be zero breakpoints, but wait to make sure none are
+  // installed while syncing.
+  await waitForTime(1000);
 
   is(getBreakpointCount(dbg), 0, "No breakpoints");
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps.js
@@ -35,16 +35,24 @@ async function getLineEl(dbg, line) {
   return el;
 }
 
 async function clickGutter(dbg, line) {
   const el = await codeMirrorGutterElement(dbg, line);
   clickDOMElement(dbg, el);
 }
 
+async function waitForBreakpointCount(dbg, count) {
+  const {
+    selectors: { getBreakpointCount },
+    getState
+  } = dbg;
+  await waitForState(dbg, state => getBreakpointCount(getState()) == count);
+}
+
 add_task(async function() {
   // NOTE: the CORS call makes the test run times inconsistent
   const dbg = await initDebugger(
     "doc-sourcemaps.html",
     "entry.js",
     "output.js",
     "times2.js",
     "opts.js"
@@ -56,21 +64,21 @@ add_task(async function() {
 
   // Check that the original sources appear in the source tree
   await clickElement(dbg, "sourceDirectoryLabel", 3);
   await assertSourceCount(dbg, 8);
 
   await selectSource(dbg, bundleSrc);
 
   await clickGutter(dbg, 70);
-  await waitForDispatch(dbg, "ADD_BREAKPOINT");
+  await waitForBreakpointCount(dbg, 1);
   await assertEditorBreakpoint(dbg, 70, true);
 
   await clickGutter(dbg, 70);
-  await waitForDispatch(dbg, "REMOVE_BREAKPOINT");
+  await waitForBreakpointCount(dbg, 0);
   is(dbg.selectors.getBreakpointCount(getState()), 0, "No breakpoints exists");
 
   const entrySrc = findSource(dbg, "entry.js");
 
   await selectSource(dbg, entrySrc);
   ok(
     getCM(dbg)
       .getValue()
--- a/devtools/client/debugger/new/test/mochitest/helpers.js
+++ b/devtools/client/debugger/new/test/mochitest/helpers.js
@@ -772,40 +772,37 @@ function getFirstBreakpointColumn(dbg, {
  * @param {Number} col
  * @return {Promise}
  * @static
  */
 async function addBreakpoint(dbg, source, line, column, options) {
   source = findSource(dbg, source);
   const sourceId = source.id;
   const bpCount = dbg.selectors.getBreakpointCount(dbg.getState());
-  dbg.actions.addBreakpoint({ sourceId, line, column }, options);
-  await waitForDispatch(dbg, "ADD_BREAKPOINT");
+  await dbg.actions.addBreakpoint({ sourceId, line, column }, options);
   is(
     dbg.selectors.getBreakpointCount(dbg.getState()),
     bpCount + 1,
     "a new breakpoint was created"
   );
 }
 
 function disableBreakpoint(dbg, source, line, column) {
   column =
     column || getFirstBreakpointColumn(dbg, { line, sourceId: source.id });
   const location = { sourceId: source.id, sourceUrl: source.url, line, column };
   const bp = dbg.selectors.getBreakpointForLocation(dbg.getState(), location);
-  dbg.actions.disableBreakpoint(bp);
-  return waitForDispatch(dbg, "DISABLE_BREAKPOINT");
+  return dbg.actions.disableBreakpoint(bp);
 }
 
 function setBreakpointOptions(dbg, source, line, column, options) {
   source = findSource(dbg, source);
   const sourceId = source.id;
   column = column || getFirstBreakpointColumn(dbg, {line, sourceId});
-  dbg.actions.setBreakpointOptions({ sourceId, line, column }, options);
-  return waitForDispatch(dbg, "SET_BREAKPOINT_OPTIONS");
+  return dbg.actions.setBreakpointOptions({ sourceId, line, column }, options);
 }
 
 function findBreakpoint(dbg, url, line) {
   const {
     selectors: { getBreakpoint, getBreakpointsList },
     getState
   } = dbg;
   const source = findSource(dbg, url);
@@ -933,18 +930,17 @@ async function assertScopes(dbg, items) 
  * @return {Promise}
  * @static
  */
 function removeBreakpoint(dbg, sourceId, line, column) {
   const source = dbg.selectors.getSource(dbg.getState(), sourceId);
   column = column || getFirstBreakpointColumn(dbg, {line, sourceId});
   const location = { sourceId, sourceUrl: source.url, line, column };
   const bp = dbg.selectors.getBreakpointForLocation(dbg.getState(), location);
-  dbg.actions.removeBreakpoint(bp);
-  return waitForDispatch(dbg, "REMOVE_BREAKPOINT");
+  return dbg.actions.removeBreakpoint(bp);
 }
 
 /**
  * Toggles the Pause on exceptions feature in the debugger.
  *
  * @memberof mochitest/actions
  * @param {Object} dbg
  * @param {Boolean} pauseOnExceptions
--- a/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
+++ b/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
@@ -57,47 +57,26 @@ struct DevTools : public ::testing::Test
   static void reportError(JSContext* cx, const char* message,
                           JSErrorReport* report) {
     fprintf(stderr, "%s:%u:%s\n",
             report->filename ? report->filename : "<no filename>",
             (unsigned int)report->lineno, message);
   }
 
   static const JSClass* getGlobalClass() {
-    static const JSClassOps globalClassOps = {nullptr,
-                                              nullptr,
-                                              nullptr,
-                                              nullptr,
-                                              nullptr,
-                                              nullptr,
-                                              nullptr,
-                                              nullptr,
-                                              nullptr,
-                                              nullptr,
-                                              JS_GlobalObjectTraceHook};
     static const JSClass globalClass = {"global", JSCLASS_GLOBAL_FLAGS,
-                                        &globalClassOps};
+                                        &JS::DefaultGlobalClassOps};
     return &globalClass;
   }
 
   JSObject* createGlobal() {
     /* Create the global object. */
-    JS::RootedObject newGlobal(cx);
     JS::RealmOptions options;
-    newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
-                                   JS::FireOnNewGlobalHook, options);
-    if (!newGlobal) return nullptr;
-
-    JSAutoRealm ar(cx, newGlobal);
-
-    /* Populate the global object with the standard globals, like Object and
-       Array. */
-    if (!JS::InitRealmStandardClasses(cx)) return nullptr;
-
-    return newGlobal;
+    return JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
+                              JS::FireOnNewGlobalHook, options);
   }
 
   virtual void TearDown() {
     _initialized = false;
 
     if (global) {
       JS::LeaveRealm(cx, nullptr);
       global = nullptr;
--- a/js/src/fuzz-tests/tests.cpp
+++ b/js/src/fuzz-tests/tests.cpp
@@ -18,55 +18,31 @@
 #endif
 
 using namespace mozilla;
 
 JS::PersistentRootedObject gGlobal;
 JSContext* gCx = nullptr;
 
 static const JSClass* getGlobalClass() {
-  static const JSClassOps cOps = {nullptr,
-                                  nullptr,
-                                  nullptr,
-                                  nullptr,
-                                  nullptr,
-                                  nullptr,
-                                  nullptr,
-                                  nullptr,
-                                  nullptr,
-                                  nullptr,
-                                  JS_GlobalObjectTraceHook};
-  static const JSClass c = {"global", JSCLASS_GLOBAL_FLAGS, &cOps};
+  static const JSClass c = {"global", JSCLASS_GLOBAL_FLAGS,
+                            &JS::DefaultGlobalClassOps};
   return &c;
 }
 
 static JSObject* jsfuzz_createGlobal(JSContext* cx, JSPrincipals* principals) {
   /* Create the global object. */
-  JS::RootedObject newGlobal(cx);
   JS::RealmOptions options;
   options.creationOptions()
       .setStreamsEnabled(true)
       .setBigIntEnabled(true)
       .setFieldsEnabled(false)
       .setAwaitFixEnabled(true);
-  newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals,
-                                 JS::FireOnNewGlobalHook, options);
-  if (!newGlobal) {
-    return nullptr;
-  }
-
-  JSAutoRealm ar(cx, newGlobal);
-
-  // Populate the global object with the standard globals like Object and
-  // Array.
-  if (!JS::InitRealmStandardClasses(cx)) {
-    return nullptr;
-  }
-
-  return newGlobal;
+  return JS_NewGlobalObject(cx, getGlobalClass(), principals,
+                            JS::FireOnNewGlobalHook, options);
 }
 
 static bool jsfuzz_init(JSContext** cx, JS::PersistentRootedObject* global) {
   *cx = JS_NewContext(8L * 1024 * 1024);
   if (!*cx) {
     return false;
   }
 
--- a/js/src/gdb/gdb-tests.cpp
+++ b/js/src/gdb/gdb-tests.cpp
@@ -8,31 +8,19 @@
 
 #include "gdb-tests.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/Initialization.h"
 
 using namespace JS;
 
-static const JSClassOps global_classOps = {nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           JS_GlobalObjectTraceHook};
-
 /* The class of the global object. */
 static const JSClass global_class = {"global", JSCLASS_GLOBAL_FLAGS,
-                                     &global_classOps};
+                                     &DefaultGlobalClassOps};
 
 static volatile int dontOptimizeMeAway = 0;
 
 void usePointer(const void* ptr) { dontOptimizeMeAway++; }
 
 template <typename T>
 static inline T* checkPtr(T* ptr) {
   if (!ptr) {
@@ -78,20 +66,16 @@ int main(int argc, const char** argv) {
 
   /* Create the global object. */
   JS::RealmOptions options;
   RootedObject global(
       cx, checkPtr(JS_NewGlobalObject(cx, &global_class, nullptr,
                                       JS::FireOnNewGlobalHook, options)));
   JSAutoRealm ar(cx, global);
 
-  /* Populate the global object with the standard globals,
-     like Object and Array. */
-  checkBool(JS::InitRealmStandardClasses(cx));
-
   argv++;
   while (*argv) {
     const char* name = *argv++;
     GDBFragment* fragment;
     for (fragment = GDBFragment::allFragments; fragment;
          fragment = fragment->next) {
       if (strcmp(fragment->name(), name) == 0) {
         fragment->run(cx, argv);
--- a/js/src/jsapi-tests/testChromeBuffer.cpp
+++ b/js/src/jsapi-tests/testChromeBuffer.cpp
@@ -5,30 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "js/CompilationAndEvaluation.h"
 #include "js/ContextOptions.h"
 #include "jsapi-tests/tests.h"
 
 static TestJSPrincipals system_principals(1);
 
-static const JSClassOps global_classOps = {nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           nullptr,
-                                           JS_GlobalObjectTraceHook};
-
 static const JSClass global_class = {
-    "global", JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS, &global_classOps};
+    "global", JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS,
+    &JS::DefaultGlobalClassOps};
 
 static JS::PersistentRootedObject trusted_glob;
 static JS::PersistentRootedObject trusted_fun;
 
 static bool CallTrusted(JSContext* cx, unsigned argc, JS::Value* vp) {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
 
   bool ok = false;
--- a/js/src/jsapi-tests/testDebugger.cpp
+++ b/js/src/jsapi-tests/testDebugger.cpp
@@ -12,20 +12,16 @@ using namespace js;
 
 BEGIN_TEST(testDebugger_newScriptHook) {
   // Test that top-level indirect eval fires the newScript hook.
   CHECK(JS_DefineDebuggerObject(cx, global));
   JS::RealmOptions options;
   JS::RootedObject g(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
                                             JS::FireOnNewGlobalHook, options));
   CHECK(g);
-  {
-    JSAutoRealm ar(cx, g);
-    CHECK(JS::InitRealmStandardClasses(cx));
-  }
 
   JS::RootedObject gWrapper(cx, g);
   CHECK(JS_WrapObject(cx, &gWrapper));
   JS::RootedValue v(cx, JS::ObjectValue(*gWrapper));
   CHECK(JS_SetProperty(cx, global, "g", v));
 
   EXEC(
       "var dbg = Debugger(g);\n"
--- a/js/src/jsapi-tests/testMutedErrors.cpp
+++ b/js/src/jsapi-tests/testMutedErrors.cpp
@@ -50,17 +50,16 @@ bool eval(const char* asciiChars, bool m
   chars[len] = 0;
 
   JS::RealmOptions globalOptions;
   JS::RootedObject global(
       cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
                              JS::FireOnNewGlobalHook, globalOptions));
   CHECK(global);
   JSAutoRealm ar(cx, global);
-  CHECK(JS::InitRealmStandardClasses(cx));
 
   JS::CompileOptions options(cx);
   options.setMutedErrors(mutedErrors).setFileAndLine("", 0);
 
   JS::SourceText<char16_t> srcBuf;
   CHECK(srcBuf.init(cx, chars.get(), len, JS::SourceOwnership::Borrowed));
 
   return JS::Evaluate(cx, options, srcBuf, rval);
--- a/js/src/jsapi-tests/testSetProperty.cpp
+++ b/js/src/jsapi-tests/testSetProperty.cpp
@@ -3,27 +3,49 @@
  */
 /* 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/. */
 
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testSetProperty_InheritedGlobalSetter) {
-  // This is a JSAPI test because jsapi-test globals do not have a resolve
-  // hook and therefore can use the property cache in some cases where the
-  // shell can't.
+  // This is a JSAPI test because jsapi-test globals can be set up to not have
+  // a resolve hook and therefore can use the property cache in some cases
+  // where the shell can't.
   MOZ_RELEASE_ASSERT(!JS_GetClass(global)->getResolve());
 
+  CHECK(JS::InitRealmStandardClasses(cx));
+
   CHECK(JS_DefineProperty(cx, global, "HOTLOOP", 8, 0));
   EXEC(
       "var n = 0;\n"
       "var global = this;\n"
       "function f() { n++; }\n"
       "Object.defineProperty(Object.prototype, 'x', {set: f});\n"
       "for (var i = 0; i < HOTLOOP; i++)\n"
       "    global.x = i;\n");
   EXEC(
       "if (n != HOTLOOP)\n"
       "    throw 'FAIL';\n");
   return true;
 }
+
+const JSClass* getGlobalClass(void) override {
+  static const JSClassOps noResolveGlobalClassOps = {nullptr,  // add
+                                                     nullptr,  // delete
+                                                     nullptr,  // enumerate
+                                                     nullptr,  // newEnumerate
+                                                     nullptr,  // resolve
+                                                     nullptr,  // mayResolve
+                                                     nullptr,  // finalize
+                                                     nullptr,  // call
+                                                     nullptr,  // hasInstance
+                                                     nullptr,  // construct
+                                                     JS_GlobalObjectTraceHook};
+
+  static const JSClass noResolveGlobalClass = {
+    "testSetProperty_InheritedGlobalSetter_noResolveGlobalClass",
+    JSCLASS_GLOBAL_FLAGS, &noResolveGlobalClassOps};
+
+  return &noResolveGlobalClass;
+}
 END_TEST(testSetProperty_InheritedGlobalSetter)
--- a/js/src/jsapi-tests/tests.cpp
+++ b/js/src/jsapi-tests/tests.cpp
@@ -85,24 +85,16 @@ JSObject* JSAPITest::createGlobal(JSPrin
       .setFieldsEnabled(true)
       .setAwaitFixEnabled(true);
   newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals,
                                  JS::FireOnNewGlobalHook, options);
   if (!newGlobal) {
     return nullptr;
   }
 
-  JSAutoRealm ar(cx, newGlobal);
-
-  // Populate the global object with the standard globals like Object and
-  // Array.
-  if (!JS::InitRealmStandardClasses(cx)) {
-    return nullptr;
-  }
-
   global = newGlobal;
   return newGlobal;
 }
 
 int main(int argc, char* argv[]) {
   int total = 0;
   int failures = 0;
   const char* filter = (argc == 2) ? argv[1] : nullptr;
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -262,28 +262,18 @@ class JSAPITest {
     }
     msgs += message;
     return false;
   }
 
   JSAPITestString messages() const { return msgs; }
 
   static const JSClass* basicGlobalClass() {
-    static const JSClassOps cOps = {nullptr,
-                                    nullptr,
-                                    nullptr,
-                                    nullptr,
-                                    nullptr,
-                                    nullptr,
-                                    nullptr,
-                                    nullptr,
-                                    nullptr,
-                                    nullptr,
-                                    JS_GlobalObjectTraceHook};
-    static const JSClass c = {"global", JSCLASS_GLOBAL_FLAGS, &cOps};
+    static const JSClass c = {"global", JSCLASS_GLOBAL_FLAGS,
+                              &JS::DefaultGlobalClassOps};
     return &c;
   }
 
  protected:
   static bool print(JSContext* cx, unsigned argc, JS::Value* vp) {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
 
     for (unsigned i = 0; i < args.length(); i++) {
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -410,31 +410,19 @@ bool js::HasOffThreadIonCompile(Realm* r
     }
     builder = builder->getNext();
   }
 
   return false;
 }
 #endif
 
-static const JSClassOps parseTaskGlobalClassOps = {nullptr,
-                                                   nullptr,
-                                                   nullptr,
-                                                   nullptr,
-                                                   nullptr,
-                                                   nullptr,
-                                                   nullptr,
-                                                   nullptr,
-                                                   nullptr,
-                                                   nullptr,
-                                                   JS_GlobalObjectTraceHook};
-
 static const JSClass parseTaskGlobalClass = {"internal-parse-task-global",
                                              JSCLASS_GLOBAL_FLAGS,
-                                             &parseTaskGlobalClassOps};
+                                             &JS::DefaultGlobalClassOps};
 
 ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx,
                      JS::OffThreadCompileCallback callback, void* callbackData)
     : kind(kind),
       options(cx),
       parseGlobal(nullptr),
       callback(callback),
       callbackData(callbackData),
--- a/netwerk/base/ProxyAutoConfig.cpp
+++ b/netwerk/base/ProxyAutoConfig.cpp
@@ -621,45 +621,29 @@ class JSContextWrapper {
     if (!mGlobal) {
       JS_ClearPendingException(mContext);
       return NS_ERROR_OUT_OF_MEMORY;
     }
     JS::Rooted<JSObject *> global(mContext, mGlobal);
 
     JSAutoRealm ar(mContext, global);
     AutoPACErrorReporter aper(mContext);
-    if (!JS::InitRealmStandardClasses(mContext)) {
-      return NS_ERROR_FAILURE;
-    }
     if (!JS_DefineFunctions(mContext, global, PACGlobalFunctions)) {
       return NS_ERROR_FAILURE;
     }
 
     JS_FireOnNewGlobalObject(mContext, global);
 
     return NS_OK;
   }
 };
 
-static const JSClassOps sJSContextWrapperGlobalClassOps = {
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    JS_GlobalObjectTraceHook};
-
 const JSClass JSContextWrapper::sGlobalClass = {
     "PACResolutionThreadGlobal", JSCLASS_GLOBAL_FLAGS,
-    &sJSContextWrapperGlobalClassOps};
+    &JS::DefaultGlobalClassOps};
 
 void ProxyAutoConfig::SetThreadLocalIndex(uint32_t index) {
   sRunningIndex = index;
 }
 
 nsresult ProxyAutoConfig::Init(const nsCString &aPACURI,
                                const nsCString &aPACScriptData,
                                bool aIncludePath, uint32_t aExtraHeapSize,
--- a/testing/talos/talos/tests/devtools/addon/content/tests/debugger/debugger-helpers.js
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/debugger/debugger-helpers.js
@@ -244,20 +244,17 @@ async function addBreakpoint(dbg, line, 
   dump(`add breakpoint\n`);
   const source = findSource(dbg, url);
   const location = {
     sourceId: source.id,
     line,
   };
 
   await selectSource(dbg, url);
-
-  const onDispatched = waitForDispatch(dbg, "ADD_BREAKPOINT");
-  dbg.actions.addBreakpoint(location);
-  return onDispatched;
+  await dbg.actions.addBreakpoint(location);
 }
 exports.addBreakpoint = addBreakpoint;
 
 async function removeBreakpoints(dbg, line, url) {
   dump(`remove all breakpoints\n`);
   const breakpoints = dbg.selectors.getBreakpointsList(dbg.getState());
 
   const onBreakpointsCleared = waitForState(
--- a/xpcom/tests/gtest/TestGCPostBarriers.cpp
+++ b/xpcom/tests/gtest/TestGCPostBarriers.cpp
@@ -73,30 +73,18 @@ static void RunTest(JSContext* cx, Array
     ASSERT_TRUE(value.isInt32());
     ASSERT_EQ(static_cast<int32_t>(i), value.toInt32());
   }
 
   JS_RemoveExtraGCRootsTracer(cx, TraceArray<ArrayT>, array);
 }
 
 static void CreateGlobalAndRunTest(JSContext* cx) {
-  static const JSClassOps GlobalClassOps = {nullptr,
-                                            nullptr,
-                                            nullptr,
-                                            nullptr,
-                                            nullptr,
-                                            nullptr,
-                                            nullptr,
-                                            nullptr,
-                                            nullptr,
-                                            nullptr,
-                                            JS_GlobalObjectTraceHook};
-
   static const JSClass GlobalClass = {"global", JSCLASS_GLOBAL_FLAGS,
-                                      &GlobalClassOps};
+                                      &JS::DefaultGlobalClassOps};
 
   JS::RealmOptions options;
   JS::PersistentRootedObject global(cx);
   global = JS_NewGlobalObject(cx, &GlobalClass, nullptr,
                               JS::FireOnNewGlobalHook, options);
   ASSERT_TRUE(global != nullptr);
 
   JS::Realm* oldRealm = JS::EnterRealm(cx, global);