Merge inbound to mozilla-central. a=merge
authorNoemi Erli <nerli@mozilla.com>
Sun, 07 Apr 2019 00:44:56 +0300
changeset 468284 6b5221949e778defe93921b8533f50ca48488a89
parent 468280 dc53fe5c9cedaf4b7620ce76b7f72af51f31948f (current diff)
parent 468283 27111bcac93acd42ab4d830b48300865665d1caf (diff)
child 468287 0c2d3df59443feb80498c1f555e0c3321c2c649b
child 468290 f89cf2e076ecd38a0a894a960de222f18582f511
push id35827
push usernerli@mozilla.com
push dateSat, 06 Apr 2019 21:48:55 +0000
treeherdermozilla-central@6b5221949e77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
6b5221949e77 / 68.0a1 / 20190406214855 / files
nightly linux64
6b5221949e77 / 68.0a1 / 20190406214855 / files
nightly mac
6b5221949e77 / 68.0a1 / 20190406214855 / files
nightly win32
6b5221949e77 / 68.0a1 / 20190406214855 / files
nightly win64
6b5221949e77 / 68.0a1 / 20190406214855 / 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
--- a/devtools/client/debugger/new/panel.js
+++ b/devtools/client/debugger/new/panel.js
@@ -144,21 +144,23 @@ DebuggerPanel.prototype = {
   },
 
   isPaused() {
     const thread = this._selectors.getCurrentThread(this._getState());
     return this._selectors.getIsPaused(this._getState(), thread);
   },
 
   selectSourceURL(url, line, column) {
-    return this._actions.selectSourceURL(url, { line, column });
+    const cx = this._selectors.getContext(this._getState());
+    return this._actions.selectSourceURL(cx, url, { line, column });
   },
 
   selectSource(sourceId, line, column) {
-    return this._actions.selectSource(sourceId, { line, column });
+    const cx = this._selectors.getContext(this._getState());
+    return this._actions.selectSource(cx, sourceId, { line, column });
   },
 
   getSourceByActorId(sourceId) {
     return this._selectors.getSourceByActorId(this._getState(), sourceId);
   },
 
   getSourceByURL(sourceURL) {
     return this._selectors.getSourceByURL(this._getState(), sourceURL);
--- a/devtools/client/debugger/new/src/actions/ast.js
+++ b/devtools/client/debugger/new/src/actions/ast.js
@@ -7,19 +7,20 @@
 import { getSourceFromId, getSelectedLocation } from "../selectors";
 
 import { setInScopeLines } from "./ast/setInScopeLines";
 
 import * as parser from "../workers/parser";
 
 import { isLoaded } from "../utils/source";
 
+import type { Context } from "../types";
 import type { ThunkArgs, Action } from "./types";
 
-export function setOutOfScopeLocations() {
+export function setOutOfScopeLocations(cx: Context) {
   return async ({ dispatch, getState }: ThunkArgs) => {
     const location = getSelectedLocation(getState());
     if (!location) {
       return;
     }
 
     const source = getSourceFromId(getState(), location.sourceId);
 
@@ -33,14 +34,15 @@ export function setOutOfScopeLocations()
         source.id,
         ((location: any): parser.AstPosition)
       );
     }
 
     dispatch(
       ({
         type: "OUT_OF_SCOPE_LOCATIONS",
+        cx,
         locations
       }: Action)
     );
-    dispatch(setInScopeLines());
+    dispatch(setInScopeLines(cx));
   };
 }
--- a/devtools/client/debugger/new/src/actions/ast/setInScopeLines.js
+++ b/devtools/client/debugger/new/src/actions/ast/setInScopeLines.js
@@ -6,30 +6,31 @@
 
 import { getOutOfScopeLocations, getSelectedSource } from "../../selectors";
 import { getSourceLineCount } from "../../utils/source";
 
 import { range, flatMap, uniq, without } from "lodash";
 
 import type { AstLocation } from "../../workers/parser";
 import type { ThunkArgs } from "../types";
+import type { Context } from "../../types";
 
 function getOutOfScopeLines(outOfScopeLocations: ?(AstLocation[])) {
   if (!outOfScopeLocations) {
     return null;
   }
 
   return uniq(
     flatMap(outOfScopeLocations, location =>
       range(location.start.line, location.end.line)
     )
   );
 }
 
-export function setInScopeLines() {
+export function setInScopeLines(cx: Context) {
   return ({ dispatch, getState }: ThunkArgs) => {
     const source = getSelectedSource(getState());
     const outOfScopeLocations = getOutOfScopeLocations(getState());
 
     if (!source || !source.text) {
       return;
     }
 
@@ -39,12 +40,13 @@ export function setInScopeLines() {
     const sourceLines = range(1, sourceNumLines + 1);
 
     const inScopeLines = !linesOutOfScope
       ? sourceLines
       : without(sourceLines, ...linesOutOfScope);
 
     dispatch({
       type: "IN_SCOPE_LINES",
+      cx,
       lines: inScopeLines
     });
   };
 }
--- a/devtools/client/debugger/new/src/actions/breakpoints/breakpointPositions.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/breakpointPositions.js
@@ -12,17 +12,18 @@ import {
   getSourceFromId,
   hasBreakpointPositions,
   getBreakpointPositionsForSource
 } from "../../selectors";
 
 import type {
   MappedLocation,
   SourceLocation,
-  BreakpointPositions
+  BreakpointPositions,
+  Context
 } from "../../types";
 import { makeBreakpointId } from "../../utils/breakpoint";
 import {
   memoizeableAction,
   type MemoizedAction
 } from "../../utils/memoizableAction";
 
 import typeof SourceMaps from "../../../packages/devtools-source-map/src";
@@ -67,17 +68,17 @@ function convertToList(results, source) 
         sourceUrl: url
       });
     }
   }
 
   return positions;
 }
 
-async function _setBreakpointPositions(sourceId, thunkArgs) {
+async function _setBreakpointPositions(cx, sourceId, thunkArgs) {
   const { client, dispatch, getState, sourceMaps } = thunkArgs;
   let generatedSource = getSource(getState(), sourceId);
   if (!generatedSource) {
     return;
   }
 
   let results = {};
   if (isOriginalId(sourceId)) {
@@ -119,36 +120,37 @@ async function _setBreakpointPositions(s
   const source = getSource(getState(), sourceId);
   // NOTE: it's possible that the source was removed during a navigate
   if (!source) {
     return;
   }
 
   dispatch({
     type: "ADD_BREAKPOINT_POSITIONS",
+    cx,
     source: source,
     positions
   });
 
   return positions;
 }
 
 export const setBreakpointPositions: MemoizedAction<
-  { sourceId: string },
+  { cx: Context, sourceId: string },
   ?BreakpointPositions
 > = memoizeableAction("setBreakpointPositions", {
   hasValue: ({ sourceId }, { getState }) =>
     hasBreakpointPositions(getState(), sourceId),
   getValue: ({ sourceId }, { getState }) =>
     getBreakpointPositionsForSource(getState(), sourceId),
   createKey({ sourceId }, { getState }) {
     const generatedSource = getSource(
       getState(),
       isOriginalId(sourceId) ? originalToGeneratedId(sourceId) : sourceId
     );
     const actors = generatedSource
       ? generatedSource.actors.map(({ actor }) => actor)
       : [];
     return [sourceId, ...actors].join(":");
   },
-  action: ({ sourceId }, thunkArgs) =>
-    _setBreakpointPositions(sourceId, thunkArgs)
+  action: ({ cx, sourceId }, thunkArgs) =>
+    _setBreakpointPositions(cx, sourceId, thunkArgs)
 });
--- a/devtools/client/debugger/new/src/actions/breakpoints/index.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/index.js
@@ -32,181 +32,191 @@ import { closeConditionalPanel } from ".
 
 // this will need to be changed so that addCLientBreakpoint is removed
 
 import type { ThunkArgs } from "../types";
 import type {
   Breakpoint,
   Source,
   SourceLocation,
-  XHRBreakpoint
+  XHRBreakpoint,
+  Context
 } from "../../types";
 
 export * from "./breakpointPositions";
 export * from "./modify";
 export * from "./syncBreakpoint";
 
-export function addHiddenBreakpoint(location: SourceLocation) {
+export function addHiddenBreakpoint(cx: Context, location: SourceLocation) {
   return ({ dispatch }: ThunkArgs) => {
-    return dispatch(addBreakpoint(location, { hidden: true }));
+    return dispatch(addBreakpoint(cx, location, { hidden: true }));
   };
 }
 
 /**
  * Disable all breakpoints in a source
  *
  * @memberof actions/breakpoints
  * @static
  */
-export function disableBreakpointsInSource(source: Source) {
+export function disableBreakpointsInSource(cx: Context, source: Source) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
     const breakpoints = getBreakpointsForSource(getState(), source.id);
     for (const breakpoint of breakpoints) {
       if (!breakpoint.disabled) {
-        dispatch(disableBreakpoint(breakpoint));
+        dispatch(disableBreakpoint(cx, breakpoint));
       }
     }
   };
 }
 
 /**
  * Enable all breakpoints in a source
  *
  * @memberof actions/breakpoints
  * @static
  */
-export function enableBreakpointsInSource(source: Source) {
+export function enableBreakpointsInSource(cx: Context, source: Source) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
     const breakpoints = getBreakpointsForSource(getState(), source.id);
     for (const breakpoint of breakpoints) {
       if (breakpoint.disabled) {
-        dispatch(enableBreakpoint(breakpoint));
+        dispatch(enableBreakpoint(cx, breakpoint));
       }
     }
   };
 }
 
 /**
  * Toggle All Breakpoints
  *
  * @memberof actions/breakpoints
  * @static
  */
-export function toggleAllBreakpoints(shouldDisableBreakpoints: boolean) {
+export function toggleAllBreakpoints(
+  cx: Context,
+  shouldDisableBreakpoints: boolean
+) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
     const breakpoints = getBreakpointsList(getState());
 
     for (const breakpoint of breakpoints) {
       if (shouldDisableBreakpoints) {
-        dispatch(disableBreakpoint(breakpoint));
+        dispatch(disableBreakpoint(cx, breakpoint));
       } else {
-        dispatch(enableBreakpoint(breakpoint));
+        dispatch(enableBreakpoint(cx, breakpoint));
       }
     }
   };
 }
 
 /**
  * Toggle Breakpoints
  *
  * @memberof actions/breakpoints
  * @static
  */
 export function toggleBreakpoints(
+  cx: Context,
   shouldDisableBreakpoints: boolean,
   breakpoints: Breakpoint[]
 ) {
   return async ({ dispatch }: ThunkArgs) => {
     const promises = breakpoints.map(
       breakpoint =>
         shouldDisableBreakpoints
-          ? dispatch(disableBreakpoint(breakpoint))
-          : dispatch(enableBreakpoint(breakpoint))
+          ? dispatch(disableBreakpoint(cx, breakpoint))
+          : dispatch(enableBreakpoint(cx, breakpoint))
     );
 
     await Promise.all(promises);
   };
 }
 
 export function toggleBreakpointsAtLine(
+  cx: Context,
   shouldDisableBreakpoints: boolean,
   line: number
 ) {
   return async ({ dispatch, getState }: ThunkArgs) => {
-    const breakpoints = await getBreakpointsAtLine(getState(), line);
-    return dispatch(toggleBreakpoints(shouldDisableBreakpoints, breakpoints));
+    const breakpoints = getBreakpointsAtLine(getState(), line);
+    return dispatch(
+      toggleBreakpoints(cx, shouldDisableBreakpoints, breakpoints)
+    );
   };
 }
 
 /**
  * Removes all breakpoints
  *
  * @memberof actions/breakpoints
  * @static
  */
-export function removeAllBreakpoints() {
+export function removeAllBreakpoints(cx: Context) {
   return async ({ dispatch, getState }: ThunkArgs) => {
     const breakpointList = getBreakpointsList(getState());
     return Promise.all(
-      breakpointList.map(bp => dispatch(removeBreakpoint(bp)))
+      breakpointList.map(bp => dispatch(removeBreakpoint(cx, bp)))
     );
   };
 }
 
 /**
  * Removes breakpoints
  *
  * @memberof actions/breakpoints
  * @static
  */
-export function removeBreakpoints(breakpoints: Breakpoint[]) {
+export function removeBreakpoints(cx: Context, breakpoints: Breakpoint[]) {
   return async ({ dispatch }: ThunkArgs) => {
-    return Promise.all(breakpoints.map(bp => dispatch(removeBreakpoint(bp))));
+    return Promise.all(
+      breakpoints.map(bp => dispatch(removeBreakpoint(cx, bp)))
+    );
   };
 }
 
 /**
  * Removes all breakpoints in a source
  *
  * @memberof actions/breakpoints
  * @static
  */
-export function removeBreakpointsInSource(source: Source) {
+export function removeBreakpointsInSource(cx: Context, source: Source) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
     const breakpoints = getBreakpointsForSource(getState(), source.id);
     for (const breakpoint of breakpoints) {
-      dispatch(removeBreakpoint(breakpoint));
+      dispatch(removeBreakpoint(cx, breakpoint));
     }
   };
 }
 
-export function remapBreakpoints(sourceId: string) {
+export function remapBreakpoints(cx: Context, sourceId: string) {
   return async ({ dispatch, getState, sourceMaps }: ThunkArgs) => {
     const breakpoints = getBreakpointsForSource(getState(), sourceId);
     const newBreakpoints = await remapLocations(
       breakpoints,
       sourceId,
       sourceMaps
     );
 
     // Normally old breakpoints will be clobbered if we re-add them, but when
     // remapping we have changed the source maps and the old breakpoints will
     // have different locations than the new ones. Manually remove the
     // old breakpoints before adding the new ones.
     for (const bp of breakpoints) {
-      dispatch(removeBreakpoint(bp));
+      dispatch(removeBreakpoint(cx, bp));
     }
 
     for (const bp of newBreakpoints) {
-      await dispatch(addBreakpoint(bp.location, bp.options, bp.disabled));
+      await dispatch(addBreakpoint(cx, bp.location, bp.options, bp.disabled));
     }
   };
 }
 
-export function toggleBreakpointAtLine(line: number) {
+export function toggleBreakpointAtLine(cx: Context, line: number) {
   return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const state = getState();
     const selectedSource = getSelectedSource(state);
 
     if (!selectedSource) {
       return;
     }
 
@@ -217,87 +227,99 @@ export function toggleBreakpointAtLine(l
       return;
     }
 
     if (getConditionalPanelLocation(getState())) {
       dispatch(closeConditionalPanel());
     }
 
     if (bp) {
-      return dispatch(removeBreakpoint(bp));
+      return dispatch(removeBreakpoint(cx, bp));
     }
     return dispatch(
-      addBreakpoint({
+      addBreakpoint(cx, {
         sourceId: selectedSource.id,
         sourceUrl: selectedSource.url,
         line: line
       })
     );
   };
 }
 
-export function addBreakpointAtLine(line: number) {
+export function addBreakpointAtLine(cx: Context, line: number) {
   return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const state = getState();
     const source = getSelectedSource(state);
 
     if (!source || isEmptyLineInSource(state, line, source.id)) {
       return;
     }
 
     return dispatch(
-      addBreakpoint({
+      addBreakpoint(cx, {
         sourceId: source.id,
         sourceUrl: source.url,
         column: undefined,
         line
       })
     );
   };
 }
 
-export function removeBreakpointsAtLine(sourceId: string, line: number) {
+export function removeBreakpointsAtLine(
+  cx: Context,
+  sourceId: string,
+  line: number
+) {
   return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const breakpointsAtLine = getBreakpointsForSource(
       getState(),
       sourceId,
       line
     );
-    return dispatch(removeBreakpoints(breakpointsAtLine));
+    return dispatch(removeBreakpoints(cx, breakpointsAtLine));
   };
 }
 
-export function disableBreakpointsAtLine(sourceId: string, line: number) {
+export function disableBreakpointsAtLine(
+  cx: Context,
+  sourceId: string,
+  line: number
+) {
   return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const breakpointsAtLine = getBreakpointsForSource(
       getState(),
       sourceId,
       line
     );
-    return dispatch(toggleBreakpoints(true, breakpointsAtLine));
+    return dispatch(toggleBreakpoints(cx, true, breakpointsAtLine));
   };
 }
 
-export function enableBreakpointsAtLine(sourceId: string, line: number) {
+export function enableBreakpointsAtLine(
+  cx: Context,
+  sourceId: string,
+  line: number
+) {
   return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const breakpointsAtLine = getBreakpointsForSource(
       getState(),
       sourceId,
       line
     );
-    return dispatch(toggleBreakpoints(false, breakpointsAtLine));
+    return dispatch(toggleBreakpoints(cx, false, breakpointsAtLine));
   };
 }
 
-export function toggleDisabledBreakpoint(breakpoint: Breakpoint) {
+export function toggleDisabledBreakpoint(cx: Context, breakpoint: Breakpoint) {
   return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     if (!breakpoint.disabled) {
-      return dispatch(disableBreakpoint(breakpoint));
+      return dispatch(disableBreakpoint(cx, breakpoint));
     }
-    return dispatch(enableBreakpoint(breakpoint));
+    return dispatch(enableBreakpoint(cx, breakpoint));
   };
 }
 
 export function enableXHRBreakpoint(index: number, bp?: XHRBreakpoint) {
   return ({ dispatch, getState, client }: ThunkArgs) => {
     const xhrBreakpoints = getXHRBreakpoints(getState());
     const breakpoint = bp || xhrBreakpoints[index];
     const enabledBreakpoint = {
--- a/devtools/client/debugger/new/src/actions/breakpoints/modify.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/modify.js
@@ -26,17 +26,18 @@ import { recordEvent } from "../../utils
 import { comparePosition } from "../../utils/location";
 import { getTextAtPosition } from "../../utils/source";
 
 import type { ThunkArgs } from "../types";
 import type {
   Breakpoint,
   BreakpointOptions,
   BreakpointPosition,
-  SourceLocation
+  SourceLocation,
+  Context
 } from "../../types";
 
 // 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.
@@ -73,44 +74,46 @@ function clientRemoveBreakpoint(generate
     const breakpointLocation = makeBreakpointLocation(
       getState(),
       generatedLocation
     );
     return client.removeBreakpoint(breakpointLocation);
   };
 }
 
-export function enableBreakpoint(initialBreakpoint: Breakpoint) {
+export function enableBreakpoint(cx: Context, initialBreakpoint: Breakpoint) {
   return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const breakpoint = getBreakpoint(getState(), initialBreakpoint.location);
     if (!breakpoint || !breakpoint.disabled) {
       return;
     }
 
     dispatch({
       type: "SET_BREAKPOINT",
+      cx,
       breakpoint: { ...breakpoint, disabled: false }
     });
 
     return dispatch(clientSetBreakpoint(breakpoint));
   };
 }
 
 export function addBreakpoint(
+  cx: Context,
   initialLocation: SourceLocation,
   options: BreakpointOptions = {},
   disabled: boolean = false,
   shouldCancel: () => boolean = () => false
 ) {
   return async ({ dispatch, getState, sourceMaps, client }: ThunkArgs) => {
     recordEvent("add_breakpoint");
 
     const { sourceId, column } = initialLocation;
 
-    await dispatch(setBreakpointPositions({ sourceId }));
+    await dispatch(setBreakpointPositions({ cx, sourceId }));
 
     const position: ?BreakpointPosition = column
       ? getBreakpointPositionsForLocation(getState(), initialLocation)
       : getFirstBreakpointPosition(getState(), initialLocation);
 
     if (!position) {
       return;
     }
@@ -141,17 +144,17 @@ export function addBreakpoint(
       text,
       originalText
     };
 
     if (shouldCancel()) {
       return;
     }
 
-    dispatch({ type: "SET_BREAKPOINT", breakpoint });
+    dispatch({ type: "SET_BREAKPOINT", cx, 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(generatedLocation));
     }
 
     return dispatch(clientSetBreakpoint(breakpoint));
@@ -159,27 +162,28 @@ export function addBreakpoint(
 }
 
 /**
  * Remove a single breakpoint
  *
  * @memberof actions/breakpoints
  * @static
  */
-export function removeBreakpoint(initialBreakpoint: Breakpoint) {
+export function removeBreakpoint(cx: Context, initialBreakpoint: Breakpoint) {
   return ({ dispatch, getState, client }: ThunkArgs) => {
     recordEvent("remove_breakpoint");
 
     const breakpoint = getBreakpoint(getState(), initialBreakpoint.location);
     if (!breakpoint) {
       return;
     }
 
     dispatch({
       type: "REMOVE_BREAKPOINT",
+      cx,
       location: breakpoint.location
     });
 
     // If the breakpoint is disabled then it is not installed in the server.
     if (breakpoint.disabled) {
       return;
     }
 
@@ -189,66 +193,72 @@ export function removeBreakpoint(initial
 
 /**
  * Remove all installed, pending, and client breakpoints associated with a
  * target generated location.
  *
  * @memberof actions/breakpoints
  * @static
  */
-export function removeBreakpointAtGeneratedLocation(target: SourceLocation) {
+export function removeBreakpointAtGeneratedLocation(
+  cx: Context,
+  target: SourceLocation
+) {
   return ({ dispatch, getState, client }: ThunkArgs) => {
     // Remove any breakpoints matching the generated location.
     const breakpoints = getBreakpointsList(getState());
     for (const { location, generatedLocation } of breakpoints) {
       if (
         generatedLocation.sourceId == target.sourceId &&
         comparePosition(generatedLocation, target)
       ) {
         dispatch({
           type: "REMOVE_BREAKPOINT",
+          cx,
           location
         });
       }
     }
 
     // Remove any remaining pending breakpoints matching the generated location.
     const pending = getPendingBreakpointList(getState());
     for (const { location, generatedLocation } of pending) {
       if (
         generatedLocation.sourceUrl == target.sourceUrl &&
         comparePosition(generatedLocation, target)
       ) {
         dispatch({
           type: "REMOVE_PENDING_BREAKPOINT",
+          cx,
           location
         });
       }
     }
 
     // Remove the breakpoint from the client itself.
     return dispatch(clientRemoveBreakpoint(target));
   };
 }
 
 /**
  * Disable a single breakpoint
  *
  * @memberof actions/breakpoints
  * @static
  */
-export function disableBreakpoint(initialBreakpoint: Breakpoint) {
+export function disableBreakpoint(cx: Context, initialBreakpoint: Breakpoint) {
   return ({ dispatch, getState, client }: ThunkArgs) => {
     const breakpoint = getBreakpoint(getState(), initialBreakpoint.location);
     if (!breakpoint || breakpoint.disabled) {
       return;
     }
 
     dispatch({
       type: "SET_BREAKPOINT",
+      cx,
       breakpoint: { ...breakpoint, disabled: true }
     });
 
     return dispatch(clientRemoveBreakpoint(breakpoint.generatedLocation));
   };
 }
 
 /**
@@ -258,28 +268,30 @@ export function disableBreakpoint(initia
  * @memberof actions/breakpoints
  * @static
  * @param {SourceLocation} location
  *        @see DebuggerController.Breakpoints.addBreakpoint
  * @param {Object} options
  *        Any options to set on the breakpoint
  */
 export function setBreakpointOptions(
+  cx: Context,
   location: SourceLocation,
   options: BreakpointOptions = {}
 ) {
   return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     let breakpoint = getBreakpoint(getState(), location);
     if (!breakpoint) {
-      return dispatch(addBreakpoint(location, options));
+      return dispatch(addBreakpoint(cx, location, options));
     }
 
     // Note: setting a breakpoint's options implicitly enables it.
     breakpoint = { ...breakpoint, disabled: false, options };
 
     dispatch({
       type: "SET_BREAKPOINT",
+      cx,
       breakpoint
     });
 
     return dispatch(clientSetBreakpoint(breakpoint));
   };
 }
--- a/devtools/client/debugger/new/src/actions/breakpoints/syncBreakpoint.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/syncBreakpoint.js
@@ -22,39 +22,42 @@ import { addBreakpoint, removeBreakpoint
 import type { ThunkArgs } from "../types";
 import type { LoadedSymbols } from "../../reducers/types";
 
 import type {
   SourceLocation,
   ASTLocation,
   PendingBreakpoint,
   SourceId,
-  BreakpointPositions
+  BreakpointPositions,
+  Context
 } from "../../types";
 
 async function findBreakpointPosition(
+  cx: Context,
   { getState, dispatch },
   location: SourceLocation
 ) {
   const positions: BreakpointPositions = await dispatch(
-    setBreakpointPositions({ sourceId: location.sourceId })
+    setBreakpointPositions({ cx, sourceId: location.sourceId })
   );
 
   const position = findPosition(positions, location);
   return position && position.generatedLocation;
 }
 
 async function findNewLocation(
+  cx: Context,
   { name, offset, index }: ASTLocation,
   location: SourceLocation,
   source,
   thunkArgs
 ) {
   const symbols: LoadedSymbols = await thunkArgs.dispatch(
-    setSymbols({ source })
+    setSymbols({ cx, source })
   );
   const func = findFunctionByName(symbols, name, index);
 
   // Fallback onto the location line, if we do not find a function is not found
   let line = location.line;
   if (func) {
     line = func.location.start.line + offset.line;
   }
@@ -80,16 +83,17 @@ async function findNewLocation(
 //   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(
+  cx: Context,
   sourceId: SourceId,
   pendingBreakpoint: PendingBreakpoint
 ) {
   return async (thunkArgs: ThunkArgs) => {
     const { getState, client, dispatch } = thunkArgs;
     assertPendingBreakpoint(pendingBreakpoint);
 
     const source = getSource(getState(), sourceId);
@@ -119,62 +123,70 @@ export function syncBreakpoint(
       // breakpoint if the original source was synced to a different location,
       // in which case the client breakpoint has been removed.
       const breakpointLocation = makeBreakpointLocation(
         getState(),
         sourceGeneratedLocation
       );
       return dispatch(
         addBreakpoint(
+          cx,
           sourceGeneratedLocation,
           pendingBreakpoint.options,
           pendingBreakpoint.disabled,
           () => !client.hasBreakpoint(breakpointLocation)
         )
       );
     }
 
     const previousLocation = { ...location, sourceId };
 
     const newLocation = await findNewLocation(
+      cx,
       astLocation,
       previousLocation,
       source,
       thunkArgs
     );
 
     const newGeneratedLocation = await findBreakpointPosition(
+      cx,
       thunkArgs,
       newLocation
     );
 
     if (!newGeneratedLocation) {
       // We couldn't find a new mapping for the breakpoint. If there is a source
       // mapping, remove any breakpoints for the generated location, as if the
       // breakpoint moved. If the old generated location still maps to an
       // original location then we don't want to add a breakpoint for it.
       if (location.sourceUrl != generatedLocation.sourceUrl) {
-        dispatch(removeBreakpointAtGeneratedLocation(sourceGeneratedLocation));
+        dispatch(
+          removeBreakpointAtGeneratedLocation(cx, sourceGeneratedLocation)
+        );
       }
       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.
     if (!isSameLocation) {
-      dispatch(removeBreakpointAtGeneratedLocation(sourceGeneratedLocation));
+      dispatch(
+        removeBreakpointAtGeneratedLocation(cx, sourceGeneratedLocation)
+      );
     }
 
     return dispatch(
       addBreakpoint(
+        cx,
         newLocation,
         pendingBreakpoint.options,
         pendingBreakpoint.disabled
       )
     );
   };
 }
--- a/devtools/client/debugger/new/src/actions/breakpoints/tests/breakpointPositions.spec.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/tests/breakpointPositions.spec.js
@@ -13,20 +13,20 @@ import {
 } from "../../../utils/test-head";
 
 describe("breakpointPositions", () => {
   it("fetches positions", async () => {
     const store = createStore({
       getBreakpointPositions: async () => ({ "9": [1] })
     });
 
-    const { dispatch, getState } = store;
+    const { dispatch, getState, cx } = store;
     await dispatch(actions.newSource(makeSource("foo")));
 
-    dispatch(actions.setBreakpointPositions({ sourceId: "foo" }));
+    dispatch(actions.setBreakpointPositions({ cx, sourceId: "foo" }));
 
     await waitForState(store, state =>
       selectors.hasBreakpointPositions(state, "foo")
     );
 
     expect(
       selectors.getBreakpointPositionsForSource(getState(), "foo")
     ).toEqual([
@@ -53,21 +53,21 @@ describe("breakpointPositions", () => {
     const store = createStore({
       getBreakpointPositions: () =>
         new Promise(r => {
           count++;
           resolve = r;
         })
     });
 
-    const { dispatch, getState } = store;
+    const { dispatch, getState, cx } = store;
     await dispatch(actions.newSource(makeSource("foo")));
 
-    dispatch(actions.setBreakpointPositions({ sourceId: "foo" }));
-    dispatch(actions.setBreakpointPositions({ sourceId: "foo" }));
+    dispatch(actions.setBreakpointPositions({ cx, sourceId: "foo" }));
+    dispatch(actions.setBreakpointPositions({ cx, sourceId: "foo" }));
 
     resolve({ "9": [1] });
     await waitForState(store, state =>
       selectors.hasBreakpointPositions(state, "foo")
     );
 
     expect(
       selectors.getBreakpointPositionsForSource(getState(), "foo")
--- a/devtools/client/debugger/new/src/actions/breakpoints/tests/breakpoints.spec.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/tests/breakpoints.spec.js
@@ -18,137 +18,137 @@ function mockClient(positionsResponse = 
   return {
     ...simpleMockThreadClient,
     getBreakpointPositions: async () => positionsResponse
   };
 }
 
 describe("breakpoints", () => {
   it("should add a breakpoint", async () => {
-    const { dispatch, getState } = createStore(mockClient({ "2": [1] }));
+    const { dispatch, getState, cx } = createStore(mockClient({ "2": [1] }));
     const loc1 = {
       sourceId: "a",
       line: 2,
       column: 1,
       sourceUrl: "http://localhost:8000/examples/a"
     };
 
     const source = makeSource("a");
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
     await dispatch(
-      actions.setSelectedLocation(source, {
+      actions.setSelectedLocation(cx, source, {
         line: 1,
         column: 1,
         sourceId: source.id
       })
     );
 
-    await dispatch(actions.addBreakpoint(loc1));
+    await dispatch(actions.addBreakpoint(cx, loc1));
 
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
     const bp = selectors.getBreakpoint(getState(), loc1);
     expect(bp && bp.location).toEqual(loc1);
     expect(getTelemetryEvents("add_breakpoint")).toHaveLength(1);
 
     const bpSources = selectors.getBreakpointSources(getState());
     expect(bpSources).toMatchSnapshot();
   });
 
   it("should not show a breakpoint that does not have text", async () => {
-    const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
+    const { dispatch, getState, cx } = createStore(mockClient({ "5": [1] }));
     const loc1 = {
       sourceId: "a",
       line: 5,
       column: 1,
       sourceUrl: "http://localhost:8000/examples/a"
     };
     const source = makeSource("a");
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
     await dispatch(
-      actions.setSelectedLocation(source, {
+      actions.setSelectedLocation(cx, source, {
         line: 1,
         column: 1,
         sourceId: source.id
       })
     );
 
-    await dispatch(actions.addBreakpoint(loc1));
+    await dispatch(actions.addBreakpoint(cx, loc1));
 
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
     const bp = selectors.getBreakpoint(getState(), loc1);
     expect(bp && bp.location).toEqual(loc1);
     expect(selectors.getBreakpointSources(getState())).toMatchSnapshot();
   });
 
   it("should show a disabled breakpoint that does not have text", async () => {
-    const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
+    const { dispatch, getState, cx } = createStore(mockClient({ "5": [1] }));
     const loc1 = {
       sourceId: "a",
       line: 5,
       column: 1,
       sourceUrl: "http://localhost:8000/examples/a"
     };
     const source = makeSource("a");
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
     await dispatch(
-      actions.setSelectedLocation(source, {
+      actions.setSelectedLocation(cx, source, {
         line: 1,
         column: 1,
         sourceId: source.id
       })
     );
 
-    await dispatch(actions.addBreakpoint(loc1));
+    await dispatch(actions.addBreakpoint(cx, loc1));
     const breakpoint = selectors.getBreakpoint(getState(), loc1);
     if (!breakpoint) {
       throw new Error("no breakpoint");
     }
 
-    await dispatch(actions.disableBreakpoint(breakpoint));
+    await dispatch(actions.disableBreakpoint(cx, breakpoint));
 
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
     const bp = selectors.getBreakpoint(getState(), loc1);
     expect(bp && bp.location).toEqual(loc1);
     expect(selectors.getBreakpointSources(getState())).toMatchSnapshot();
   });
 
   it("should not re-add a breakpoint", async () => {
-    const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
+    const { dispatch, getState, cx } = createStore(mockClient({ "5": [1] }));
     const loc1 = {
       sourceId: "a",
       line: 5,
       column: 1,
       sourceUrl: "http://localhost:8000/examples/a"
     };
 
     const source = makeSource("a");
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
     await dispatch(
-      actions.setSelectedLocation(source, {
+      actions.setSelectedLocation(cx, source, {
         line: 1,
         column: 1,
         sourceId: source.id
       })
     );
 
-    await dispatch(actions.addBreakpoint(loc1));
+    await dispatch(actions.addBreakpoint(cx, loc1));
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
     const bp = selectors.getBreakpoint(getState(), loc1);
     expect(bp && bp.location).toEqual(loc1);
 
-    await dispatch(actions.addBreakpoint(loc1));
+    await dispatch(actions.addBreakpoint(cx, loc1));
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
   });
 
   it("should remove a breakpoint", async () => {
-    const { dispatch, getState } = createStore(
+    const { dispatch, getState, cx } = createStore(
       mockClient({ "5": [1], "6": [2] })
     );
 
     const loc1 = {
       sourceId: "a",
       line: 5,
       column: 1,
       sourceUrl: "http://localhost:8000/examples/a"
@@ -158,44 +158,44 @@ describe("breakpoints", () => {
       sourceId: "b",
       line: 6,
       column: 2,
       sourceUrl: "http://localhost:8000/examples/b"
     };
 
     const aSource = makeSource("a");
     await dispatch(actions.newSource(aSource));
-    await dispatch(actions.loadSourceText({ source: aSource }));
+    await dispatch(actions.loadSourceText({ cx, source: aSource }));
 
     const bSource = makeSource("b");
     await dispatch(actions.newSource(bSource));
-    await dispatch(actions.loadSourceText({ source: bSource }));
+    await dispatch(actions.loadSourceText({ cx, source: bSource }));
 
     await dispatch(
-      actions.setSelectedLocation(aSource, {
+      actions.setSelectedLocation(cx, aSource, {
         line: 1,
         column: 1,
         sourceId: aSource.id
       })
     );
 
-    await dispatch(actions.addBreakpoint(loc1));
-    await dispatch(actions.addBreakpoint(loc2));
+    await dispatch(actions.addBreakpoint(cx, loc1));
+    await dispatch(actions.addBreakpoint(cx, loc2));
 
     const bp = selectors.getBreakpoint(getState(), loc1);
     if (!bp) {
       throw new Error("no bp");
     }
-    await dispatch(actions.removeBreakpoint(bp));
+    await dispatch(actions.removeBreakpoint(cx, bp));
 
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
   });
 
   it("should disable a breakpoint", async () => {
-    const { dispatch, getState } = createStore(
+    const { dispatch, getState, cx } = createStore(
       mockClient({ "5": [1], "6": [2] })
     );
 
     const loc1 = {
       sourceId: "a",
       line: 5,
       column: 1,
       sourceUrl: "http://localhost:8000/examples/a"
@@ -205,74 +205,74 @@ describe("breakpoints", () => {
       sourceId: "b",
       line: 6,
       column: 2,
       sourceUrl: "http://localhost:8000/examples/b"
     };
 
     const aSource = makeSource("a");
     await dispatch(actions.newSource(aSource));
-    await dispatch(actions.loadSourceText({ source: aSource }));
+    await dispatch(actions.loadSourceText({ cx, source: aSource }));
 
     const bSource = makeSource("b");
     await dispatch(actions.newSource(bSource));
-    await dispatch(actions.loadSourceText({ source: bSource }));
+    await dispatch(actions.loadSourceText({ cx, source: bSource }));
 
-    await dispatch(actions.addBreakpoint(loc1));
-    await dispatch(actions.addBreakpoint(loc2));
+    await dispatch(actions.addBreakpoint(cx, loc1));
+    await dispatch(actions.addBreakpoint(cx, loc2));
 
     const breakpoint = selectors.getBreakpoint(getState(), loc1);
     if (!breakpoint) {
       throw new Error("no breakpoint");
     }
 
-    await dispatch(actions.disableBreakpoint(breakpoint));
+    await dispatch(actions.disableBreakpoint(cx, breakpoint));
 
     const bp = selectors.getBreakpoint(getState(), loc1);
     expect(bp && bp.disabled).toBe(true);
   });
 
   it("should enable breakpoint", async () => {
-    const { dispatch, getState } = createStore(
+    const { dispatch, getState, cx } = createStore(
       mockClient({ "5": [1], "6": [2] })
     );
     const loc = {
       sourceId: "a",
       line: 5,
       column: 1,
       sourceUrl: "http://localhost:8000/examples/a"
     };
 
     const aSource = makeSource("a");
     await dispatch(actions.newSource(aSource));
-    await dispatch(actions.loadSourceText({ source: aSource }));
+    await dispatch(actions.loadSourceText({ cx, source: aSource }));
 
-    await dispatch(actions.addBreakpoint(loc));
+    await dispatch(actions.addBreakpoint(cx, loc));
     let bp = selectors.getBreakpoint(getState(), loc);
     if (!bp) {
       throw new Error("no breakpoint");
     }
 
-    await dispatch(actions.disableBreakpoint(bp));
+    await dispatch(actions.disableBreakpoint(cx, bp));
 
     bp = selectors.getBreakpoint(getState(), loc);
     if (!bp) {
       throw new Error("no breakpoint");
     }
 
     expect(bp && bp.disabled).toBe(true);
 
-    await dispatch(actions.enableBreakpoint(bp));
+    await dispatch(actions.enableBreakpoint(cx, bp));
 
     bp = selectors.getBreakpoint(getState(), loc);
     expect(bp && !bp.disabled).toBe(true);
   });
 
   it("should toggle all the breakpoints", async () => {
-    const { dispatch, getState } = createStore(
+    const { dispatch, getState, cx } = createStore(
       mockClient({ "5": [1], "6": [2] })
     );
 
     const loc1 = {
       sourceId: "a",
       line: 5,
       column: 1,
       sourceUrl: "http://localhost:8000/examples/a"
@@ -282,169 +282,169 @@ describe("breakpoints", () => {
       sourceId: "b",
       line: 6,
       column: 2,
       sourceUrl: "http://localhost:8000/examples/b"
     };
 
     const aSource = makeSource("a");
     await dispatch(actions.newSource(aSource));
-    await dispatch(actions.loadSourceText({ source: aSource }));
+    await dispatch(actions.loadSourceText({ cx, source: aSource }));
 
     const bSource = makeSource("b");
     await dispatch(actions.newSource(bSource));
-    await dispatch(actions.loadSourceText({ source: bSource }));
+    await dispatch(actions.loadSourceText({ cx, source: bSource }));
 
-    await dispatch(actions.addBreakpoint(loc1));
-    await dispatch(actions.addBreakpoint(loc2));
+    await dispatch(actions.addBreakpoint(cx, loc1));
+    await dispatch(actions.addBreakpoint(cx, loc2));
 
-    await dispatch(actions.toggleAllBreakpoints(true));
+    await dispatch(actions.toggleAllBreakpoints(cx, true));
 
     let bp1 = selectors.getBreakpoint(getState(), loc1);
     let bp2 = selectors.getBreakpoint(getState(), loc2);
 
     expect(bp1 && bp1.disabled).toBe(true);
     expect(bp2 && bp2.disabled).toBe(true);
 
-    await dispatch(actions.toggleAllBreakpoints(false));
+    await dispatch(actions.toggleAllBreakpoints(cx, false));
 
     bp1 = selectors.getBreakpoint(getState(), loc1);
     bp2 = selectors.getBreakpoint(getState(), loc2);
     expect(bp1 && bp1.disabled).toBe(false);
     expect(bp2 && bp2.disabled).toBe(false);
   });
 
   it("should toggle a breakpoint at a location", async () => {
     const loc = { sourceId: "foo1", line: 5, column: 1 };
     const getBp = () => selectors.getBreakpoint(getState(), loc);
 
-    const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
+    const { dispatch, getState, cx } = createStore(mockClient({ "5": [1] }));
 
     const source = makeSource("foo1");
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
-    await dispatch(actions.selectLocation(loc));
+    await dispatch(actions.selectLocation(cx, loc));
 
-    await dispatch(actions.toggleBreakpointAtLine(5));
+    await dispatch(actions.toggleBreakpointAtLine(cx, 5));
     const bp = getBp();
     expect(bp && !bp.disabled).toBe(true);
 
-    await dispatch(actions.toggleBreakpointAtLine(5));
+    await dispatch(actions.toggleBreakpointAtLine(cx, 5));
     expect(getBp()).toBe(undefined);
   });
 
   it("should disable/enable a breakpoint at a location", async () => {
     const location = { sourceId: "foo1", line: 5, column: 1 };
     const getBp = () => selectors.getBreakpoint(getState(), location);
 
-    const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
+    const { dispatch, getState, cx } = createStore(mockClient({ "5": [1] }));
 
     const source = makeSource("foo1");
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
-    await dispatch(actions.selectLocation({ sourceId: "foo1", line: 1 }));
+    await dispatch(actions.selectLocation(cx, { sourceId: "foo1", line: 1 }));
 
-    await dispatch(actions.toggleBreakpointAtLine(5));
+    await dispatch(actions.toggleBreakpointAtLine(cx, 5));
     let bp = getBp();
     expect(bp && !bp.disabled).toBe(true);
     bp = getBp();
     if (!bp) {
       throw new Error("no bp");
     }
-    await dispatch(actions.toggleDisabledBreakpoint(bp));
+    await dispatch(actions.toggleDisabledBreakpoint(cx, bp));
     bp = getBp();
     expect(bp && bp.disabled).toBe(true);
   });
 
   it("should set the breakpoint condition", async () => {
-    const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
+    const { dispatch, getState, cx } = createStore(mockClient({ "5": [1] }));
 
     const loc = {
       sourceId: "a",
       line: 5,
       column: 1,
       sourceUrl: "http://localhost:8000/examples/a"
     };
 
     const source = makeSource("a");
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
-    await dispatch(actions.addBreakpoint(loc));
+    await dispatch(actions.addBreakpoint(cx, loc));
 
     let bp = selectors.getBreakpoint(getState(), loc);
     expect(bp && bp.options.condition).toBe(undefined);
 
     await dispatch(
-      actions.setBreakpointOptions(loc, {
+      actions.setBreakpointOptions(cx, loc, {
         condition: "const foo = 0",
         getTextForLine: () => {}
       })
     );
 
     bp = selectors.getBreakpoint(getState(), loc);
     expect(bp && bp.options.condition).toBe("const foo = 0");
   });
 
   it("should set the condition and enable a breakpoint", async () => {
-    const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
+    const { dispatch, getState, cx } = createStore(mockClient({ "5": [1] }));
 
     const loc = {
       sourceId: "a",
       line: 5,
       column: 1,
       sourceUrl: "http://localhost:8000/examples/a"
     };
 
     const source = makeSource("a");
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
-    await dispatch(actions.addBreakpoint(loc));
+    await dispatch(actions.addBreakpoint(cx, loc));
     let bp = selectors.getBreakpoint(getState(), loc);
     if (!bp) {
       throw new Error("no breakpoint");
     }
 
-    await dispatch(actions.disableBreakpoint(bp));
+    await dispatch(actions.disableBreakpoint(cx, bp));
 
     bp = selectors.getBreakpoint(getState(), loc);
     expect(bp && bp.options.condition).toBe(undefined);
 
     await dispatch(
-      actions.setBreakpointOptions(loc, {
+      actions.setBreakpointOptions(cx, loc, {
         condition: "const foo = 0",
         getTextForLine: () => {}
       })
     );
     const newBreakpoint = selectors.getBreakpoint(getState(), loc);
     expect(newBreakpoint && !newBreakpoint.disabled).toBe(true);
     expect(newBreakpoint && newBreakpoint.options.condition).toBe(
       "const foo = 0"
     );
   });
 
   it("should remap breakpoints on pretty print", async () => {
-    const { dispatch, getState } = createStore(mockClient({ "1": [0] }));
+    const { dispatch, getState, cx } = createStore(mockClient({ "1": [0] }));
 
     const loc = {
       sourceId: "a.js",
       line: 1,
       column: 0,
       sourceUrl: "http://localhost:8000/examples/a.js"
     };
 
     const source = makeSource("a.js");
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
-    await dispatch(actions.addBreakpoint(loc));
-    await dispatch(actions.togglePrettyPrint("a.js"));
+    await dispatch(actions.addBreakpoint(cx, loc));
+    await dispatch(actions.togglePrettyPrint(cx, "a.js"));
 
     const breakpoint = selectors.getBreakpointsList(getState())[0];
 
     expect(
       breakpoint.location.sourceUrl &&
         breakpoint.location.sourceUrl.includes("formatted")
     ).toBe(true);
     expect(breakpoint).toMatchSnapshot();
--- a/devtools/client/debugger/new/src/actions/debuggee.js
+++ b/devtools/client/debugger/new/src/actions/debuggee.js
@@ -1,15 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import type { Action, ThunkArgs } from "./types";
 
+import { getContext } from "../selectors";
+
 export function updateWorkers() {
   return async function({ dispatch, getState, client }: ThunkArgs) {
+    const cx = getContext(getState());
     const workers = await client.fetchWorkers();
     const mainThread = client.getMainThread();
-    dispatch(({ type: "SET_WORKERS", workers, mainThread }: Action));
+    dispatch(({ type: "SET_WORKERS", cx, workers, mainThread }: Action));
   };
 }
--- a/devtools/client/debugger/new/src/actions/expressions.js
+++ b/devtools/client/debugger/new/src/actions/expressions.js
@@ -17,84 +17,88 @@ import {
   getIsPaused
 } from "../selectors";
 import { PROMISE } from "./utils/middleware/promise";
 import { wrapExpression } from "../utils/expressions";
 import { features } from "../utils/prefs";
 import { isOriginal } from "../utils/source";
 
 import * as parser from "../workers/parser";
-import type { Expression } from "../types";
+import type { Expression, ThreadContext } from "../types";
 import type { ThunkArgs } from "./types";
 
 /**
  * Add expression for debugger to watch
  *
  * @param {object} expression
  * @param {number} expression.id
  * @memberof actions/pause
  * @static
  */
-export function addExpression(input: string) {
+export function addExpression(cx: ThreadContext, input: string) {
   return async ({ dispatch, getState }: ThunkArgs) => {
     if (!input) {
       return;
     }
 
     const expressionError = await parser.hasSyntaxError(input);
 
     const expression = getExpression(getState(), input);
     if (expression) {
-      return dispatch(evaluateExpression(expression));
+      return dispatch(evaluateExpression(cx, expression));
     }
 
-    dispatch({ type: "ADD_EXPRESSION", input, expressionError });
+    dispatch({ type: "ADD_EXPRESSION", cx, input, expressionError });
 
     const newExpression = getExpression(getState(), input);
     if (newExpression) {
-      return dispatch(evaluateExpression(newExpression));
+      return dispatch(evaluateExpression(cx, newExpression));
     }
   };
 }
 
-export function autocomplete(input: string, cursor: number) {
+export function autocomplete(cx: ThreadContext, input: string, cursor: number) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
     if (!input) {
       return;
     }
-    const thread = getCurrentThread(getState());
-    const frameId = getSelectedFrameId(getState(), thread);
+    const frameId = getSelectedFrameId(getState(), cx.thread);
     const result = await client.autocomplete(input, cursor, frameId);
-    await dispatch({ type: "AUTOCOMPLETE", input, result });
+    await dispatch({ type: "AUTOCOMPLETE", cx, input, result });
   };
 }
 
 export function clearAutocomplete() {
   return { type: "CLEAR_AUTOCOMPLETE" };
 }
 
 export function clearExpressionError() {
   return { type: "CLEAR_EXPRESSION_ERROR" };
 }
 
-export function updateExpression(input: string, expression: Expression) {
+export function updateExpression(
+  cx: ThreadContext,
+  input: string,
+  expression: Expression
+) {
   return async ({ dispatch, getState }: ThunkArgs) => {
     if (!input) {
       return;
     }
 
     const expressionError = await parser.hasSyntaxError(input);
     dispatch({
       type: "UPDATE_EXPRESSION",
+      cx,
       expression,
       input: expressionError ? expression.input : input,
       expressionError
     });
 
-    dispatch(evaluateExpressions());
+    dispatch(evaluateExpressions(cx));
   };
 }
 
 /**
  *
  * @param {object} expression
  * @param {number} expression.id
  * @memberof actions/pause
@@ -110,64 +114,63 @@ export function deleteExpression(express
 }
 
 /**
  *
  * @memberof actions/pause
  * @param {number} selectedFrameId
  * @static
  */
-export function evaluateExpressions() {
+export function evaluateExpressions(cx: ThreadContext) {
   return async function({ dispatch, getState, client }: ThunkArgs) {
     const expressions = getExpressions(getState()).toJS();
     const inputs = expressions.map(({ input }) => input);
-    const thread = getCurrentThread(getState());
-    const frameId = getSelectedFrameId(getState(), thread);
+    const frameId = getSelectedFrameId(getState(), cx.thread);
     const results = await client.evaluateExpressions(inputs, {
       frameId,
-      thread
+      thread: cx.thread
     });
-    dispatch({ type: "EVALUATE_EXPRESSIONS", inputs, results });
+    dispatch({ type: "EVALUATE_EXPRESSIONS", cx, inputs, results });
   };
 }
 
-function evaluateExpression(expression: Expression) {
+function evaluateExpression(cx: ThreadContext, expression: Expression) {
   return async function({ dispatch, getState, client, sourceMaps }: ThunkArgs) {
     if (!expression.input) {
       console.warn("Expressions should not be empty");
       return;
     }
 
     let input = expression.input;
-    const thread = getCurrentThread(getState());
-    const frame = getSelectedFrame(getState(), thread);
+    const frame = getSelectedFrame(getState(), cx.thread);
 
     if (frame) {
       const { location } = frame;
       const source = getSourceFromId(getState(), location.sourceId);
 
       const selectedSource = getSelectedSource(getState());
 
       if (selectedSource && isOriginal(source) && isOriginal(selectedSource)) {
         const mapResult = await dispatch(getMappedExpression(input));
         if (mapResult) {
           input = mapResult.expression;
         }
       }
     }
 
-    const frameId = getSelectedFrameId(getState(), thread);
+    const frameId = getSelectedFrameId(getState(), cx.thread);
 
     return dispatch({
       type: "EVALUATE_EXPRESSION",
-      thread,
+      cx,
+      thread: cx.thread,
       input: expression.input,
       [PROMISE]: client.evaluateInFrame(wrapExpression(input), {
         frameId,
-        thread
+        thread: cx.thread
       })
     });
   };
 }
 
 /**
  * Gets information about original variable names from the source map
  * and replaces all posible generated names.
--- a/devtools/client/debugger/new/src/actions/file-search.js
+++ b/devtools/client/debugger/new/src/actions/file-search.js
@@ -10,42 +10,42 @@ import {
   findNext,
   findPrev,
   removeOverlay,
   searchSourceForHighlight
 } from "../utils/editor";
 import { isWasm, renderWasmText } from "../utils/wasm";
 import { getMatches } from "../workers/search";
 import type { Action, FileTextSearchModifier, ThunkArgs } from "./types";
-import type { WasmSource } from "../types";
+import type { WasmSource, Context } from "../types";
 
 import {
   getSelectedSource,
   getFileSearchModifiers,
   getFileSearchQuery,
   getFileSearchResults
 } from "../selectors";
 
 import {
   closeActiveSearch,
   clearHighlightLineRange,
   setActiveSearch
 } from "./ui";
 type Editor = Object;
 type Match = Object;
 
-export function doSearch(query: string, editor: Editor) {
+export function doSearch(cx: Context, query: string, editor: Editor) {
   return ({ getState, dispatch }: ThunkArgs) => {
     const selectedSource = getSelectedSource(getState());
     if (!selectedSource || !selectedSource.text) {
       return;
     }
 
-    dispatch(setFileSearchQuery(query));
-    dispatch(searchContents(query, editor));
+    dispatch(setFileSearchQuery(cx, query));
+    dispatch(searchContents(cx, query, editor));
   };
 }
 
 export function doSearchForHighlight(
   query: string,
   editor: Editor,
   line: number,
   ch: number
@@ -54,50 +54,54 @@ export function doSearchForHighlight(
     const selectedSource = getSelectedSource(getState());
     if (!selectedSource || !selectedSource.text) {
       return;
     }
     dispatch(searchContentsForHighlight(query, editor, line, ch));
   };
 }
 
-export function setFileSearchQuery(query: string): Action {
+export function setFileSearchQuery(cx: Context, query: string): Action {
   return {
     type: "UPDATE_FILE_SEARCH_QUERY",
+    cx,
     query
   };
 }
 
 export function toggleFileSearchModifier(
+  cx: Context,
   modifier: FileTextSearchModifier
 ): Action {
-  return { type: "TOGGLE_FILE_SEARCH_MODIFIER", modifier };
+  return { type: "TOGGLE_FILE_SEARCH_MODIFIER", cx, modifier };
 }
 
 export function updateSearchResults(
+  cx: Context,
   characterIndex: number,
   line: number,
   matches: Match[]
 ): Action {
   const matchIndex = matches.findIndex(
     elm => elm.line === line && elm.ch === characterIndex
   );
 
   return {
     type: "UPDATE_SEARCH_RESULTS",
+    cx,
     results: {
       matches,
       matchIndex,
       count: matches.length,
       index: characterIndex
     }
   };
 }
 
-export function searchContents(query: string, editor: Object) {
+export function searchContents(cx: Context, query: string, editor: Object) {
   return async ({ getState, dispatch }: ThunkArgs) => {
     const modifiers = getFileSearchModifiers(getState());
     const selectedSource = getSelectedSource(getState());
 
     if (!editor || !selectedSource || !selectedSource.text || !modifiers) {
       return;
     }
 
@@ -119,17 +123,17 @@ export function searchContents(query: st
 
     const res = find(ctx, query, true, _modifiers);
     if (!res) {
       return;
     }
 
     const { ch, line } = res;
 
-    dispatch(updateSearchResults(ch, line, matches));
+    dispatch(updateSearchResults(cx, ch, line, matches));
   };
 }
 
 export function searchContentsForHighlight(
   query: string,
   editor: Object,
   line: number,
   ch: number
@@ -150,17 +154,17 @@ export function searchContentsForHighlig
 
     const ctx = { ed: editor, cm: editor.codeMirror };
     const _modifiers = modifiers.toJS();
 
     searchSourceForHighlight(ctx, false, query, true, _modifiers, line, ch);
   };
 }
 
-export function traverseResults(rev: boolean, editor: Editor) {
+export function traverseResults(cx: Context, rev: boolean, editor: Editor) {
   return async ({ getState, dispatch }: ThunkArgs) => {
     if (!editor) {
       return;
     }
 
     const ctx = { ed: editor, cm: editor.codeMirror };
 
     const query = getFileSearchQuery(getState());
@@ -175,26 +179,26 @@ export function traverseResults(rev: boo
       const matchedLocations = matches || [];
       const findArgs = [ctx, query, true, modifiers.toJS()];
       const results = rev ? findPrev(...findArgs) : findNext(...findArgs);
 
       if (!results) {
         return;
       }
       const { ch, line } = results;
-      dispatch(updateSearchResults(ch, line, matchedLocations));
+      dispatch(updateSearchResults(cx, ch, line, matchedLocations));
     }
   };
 }
 
-export function closeFileSearch(editor: Editor) {
+export function closeFileSearch(cx: Context, editor: Editor) {
   return ({ getState, dispatch }: ThunkArgs) => {
     if (editor) {
       const query = getFileSearchQuery(getState());
       const ctx = { ed: editor, cm: editor.codeMirror };
       removeOverlay(ctx, query);
     }
 
-    dispatch(setFileSearchQuery(""));
+    dispatch(setFileSearchQuery(cx, ""));
     dispatch(closeActiveSearch());
     dispatch(clearHighlightLineRange());
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/breakOnNext.js
+++ b/devtools/client/debugger/new/src/actions/pause/breakOnNext.js
@@ -1,24 +1,23 @@
 /* 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 { getCurrentThread } from "../../selectors";
 import type { ThunkArgs } from "../types";
+import type { ThreadContext } from "../../types";
 
 /**
  * Debugger breakOnNext command.
  * It's different from the comand action because we also want to
  * highlight the pause icon.
  *
  * @memberof actions/pause
  * @static
  */
-export function breakOnNext() {
+export function breakOnNext(cx: ThreadContext) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
-    const thread = getCurrentThread(getState());
-    await client.breakOnNext(thread);
-    return dispatch({ type: "BREAK_ON_NEXT", thread });
+    await client.breakOnNext(cx.thread);
+    return dispatch({ type: "BREAK_ON_NEXT", thread: cx.thread });
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/commands.js
+++ b/devtools/client/debugger/new/src/actions/pause/commands.js
@@ -1,183 +1,183 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
 /* 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 {
-  getIsPaused,
-  getCurrentThread,
   getSource,
   getTopFrame,
-  getSelectedFrame
+  getSelectedFrame,
+  getThreadContext
 } from "../../selectors";
 import { PROMISE } from "../utils/middleware/promise";
 import { getNextStep } from "../../workers/parser";
 import { addHiddenBreakpoint } from "../breakpoints";
 import { evaluateExpressions } from "../expressions";
 import { selectLocation } from "../sources";
+import { fetchScopes } from "./fetchScopes";
 import { features } from "../../utils/prefs";
 import { recordEvent } from "../../utils/telemetry";
+import assert from "../../utils/assert";
 
-import type { Source, ThreadId } from "../../types";
+import type { Source, ThreadId, Context, ThreadContext } from "../../types";
 import type { ThunkArgs } from "../types";
 import type { Command } from "../../reducers/types";
 
-export function selectThread(thread: ThreadId) {
+export function selectThread(cx: Context, thread: ThreadId) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
-    await dispatch({ type: "SELECT_THREAD", thread });
-    dispatch(evaluateExpressions());
+    await dispatch({ cx, type: "SELECT_THREAD", thread });
+
+    // Get a new context now that the current thread has changed.
+    const threadcx = getThreadContext(getState());
+    assert(threadcx.thread == thread, "Thread mismatch");
+
+    dispatch(evaluateExpressions(threadcx));
 
     const frame = getSelectedFrame(getState(), thread);
     if (frame) {
-      dispatch(selectLocation(frame.location));
+      dispatch(selectLocation(threadcx, frame.location));
+      dispatch(fetchScopes(threadcx));
     }
   };
 }
 
 /**
  * Debugger commands like stepOver, stepIn, stepUp
  *
  * @param string $0.type
  * @memberof actions/pause
  * @static
  */
-export function command(thread: ThreadId, type: Command) {
+export function command(cx: ThreadContext, type: Command) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
     if (type) {
       return dispatch({
         type: "COMMAND",
         command: type,
-        thread,
-        [PROMISE]: client[type](thread)
+        cx,
+        thread: cx.thread,
+        [PROMISE]: client[type](cx.thread)
       });
     }
   };
 }
 
 /**
  * StepIn
  * @memberof actions/pause
  * @static
  * @returns {Function} {@link command}
  */
-export function stepIn() {
+export function stepIn(cx: ThreadContext) {
   return ({ dispatch, getState }: ThunkArgs) => {
-    const thread = getCurrentThread(getState());
-    if (getIsPaused(getState(), thread)) {
-      return dispatch(command(thread, "stepIn"));
+    if (cx.isPaused) {
+      return dispatch(command(cx, "stepIn"));
     }
   };
 }
 
 /**
  * stepOver
  * @memberof actions/pause
  * @static
  * @returns {Function} {@link command}
  */
-export function stepOver() {
+export function stepOver(cx: ThreadContext) {
   return ({ dispatch, getState }: ThunkArgs) => {
-    const thread = getCurrentThread(getState());
-    if (getIsPaused(getState(), thread)) {
-      return dispatch(astCommand(thread, "stepOver"));
+    if (cx.isPaused) {
+      return dispatch(astCommand(cx, "stepOver"));
     }
   };
 }
 
 /**
  * stepOut
  * @memberof actions/pause
  * @static
  * @returns {Function} {@link command}
  */
-export function stepOut() {
+export function stepOut(cx: ThreadContext) {
   return ({ dispatch, getState }: ThunkArgs) => {
-    const thread = getCurrentThread(getState());
-    if (getIsPaused(getState(), thread)) {
-      return dispatch(command(thread, "stepOut"));
+    if (cx.isPaused) {
+      return dispatch(command(cx, "stepOut"));
     }
   };
 }
 
 /**
  * resume
  * @memberof actions/pause
  * @static
  * @returns {Function} {@link command}
  */
-export function resume() {
+export function resume(cx: ThreadContext) {
   return ({ dispatch, getState }: ThunkArgs) => {
-    const thread = getCurrentThread(getState());
-    if (getIsPaused(getState(), thread)) {
+    if (cx.isPaused) {
       recordEvent("continue");
-      return dispatch(command(thread, "resume"));
+      return dispatch(command(cx, "resume"));
     }
   };
 }
 
 /**
  * rewind
  * @memberof actions/pause
  * @static
  * @returns {Function} {@link command}
  */
-export function rewind() {
+export function rewind(cx: ThreadContext) {
   return ({ dispatch, getState }: ThunkArgs) => {
-    const thread = getCurrentThread(getState());
-    if (getIsPaused(getState(), thread)) {
-      return dispatch(command(thread, "rewind"));
+    if (cx.isPaused) {
+      return dispatch(command(cx, "rewind"));
     }
   };
 }
 
 /**
  * reverseStepIn
  * @memberof actions/pause
  * @static
  * @returns {Function} {@link command}
  */
-export function reverseStepIn() {
+export function reverseStepIn(cx: ThreadContext) {
   return ({ dispatch, getState }: ThunkArgs) => {
-    const thread = getCurrentThread(getState());
-    if (getIsPaused(getState(), thread)) {
-      return dispatch(command(thread, "reverseStepIn"));
+    if (cx.isPaused) {
+      return dispatch(command(cx, "reverseStepIn"));
     }
   };
 }
 
 /**
  * reverseStepOver
  * @memberof actions/pause
  * @static
  * @returns {Function} {@link command}
  */
-export function reverseStepOver() {
+export function reverseStepOver(cx: ThreadContext) {
   return ({ dispatch, getState }: ThunkArgs) => {
-    const thread = getCurrentThread(getState());
-    if (getIsPaused(getState(), thread)) {
-      return dispatch(astCommand(thread, "reverseStepOver"));
+    if (cx.isPaused) {
+      return dispatch(astCommand(cx, "reverseStepOver"));
     }
   };
 }
 
 /**
  * reverseStepOut
  * @memberof actions/pause
  * @static
  * @returns {Function} {@link command}
  */
-export function reverseStepOut() {
+export function reverseStepOut(cx: ThreadContext) {
   return ({ dispatch, getState }: ThunkArgs) => {
-    const thread = getCurrentThread(getState());
-    if (getIsPaused(getState(), thread)) {
-      return dispatch(command(thread, "reverseStepOut"));
+    if (cx.isPaused) {
+      return dispatch(command(cx, "reverseStepOut"));
     }
   };
 }
 
 /*
  * Checks for await or yield calls on the paused line
  * This avoids potentially expensive parser calls when we are likely
  * not at an async expression.
@@ -200,31 +200,31 @@ function hasAwait(source: Source, pauseL
 }
 
 /**
  * @memberOf actions/pause
  * @static
  * @param stepType
  * @returns {function(ThunkArgs)}
  */
-export function astCommand(thread: ThreadId, stepType: Command) {
+export function astCommand(cx: ThreadContext, stepType: Command) {
   return async ({ dispatch, getState, sourceMaps }: ThunkArgs) => {
     if (!features.asyncStepping) {
-      return dispatch(command(thread, stepType));
+      return dispatch(command(cx, stepType));
     }
 
     if (stepType == "stepOver") {
       // This type definition is ambiguous:
-      const frame: any = getTopFrame(getState(), thread);
+      const frame: any = getTopFrame(getState(), cx.thread);
       const source = getSource(getState(), frame.location.sourceId);
 
       if (source && hasAwait(source, frame.location)) {
         const nextLocation = await getNextStep(source.id, frame.location);
         if (nextLocation) {
-          await dispatch(addHiddenBreakpoint(nextLocation));
-          return dispatch(command(thread, "resume"));
+          await dispatch(addHiddenBreakpoint(cx, nextLocation));
+          return dispatch(command(cx, "resume"));
         }
       }
     }
 
-    return dispatch(command(thread, stepType));
+    return dispatch(command(cx, stepType));
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/continueToHere.js
+++ b/devtools/client/debugger/new/src/actions/pause/continueToHere.js
@@ -1,46 +1,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/>. */
 
 // @flow
 
 import {
-  getCurrentThread,
   getSelectedSource,
   getSelectedFrame,
   getCanRewind
 } from "../../selectors";
 import { addHiddenBreakpoint } from "../breakpoints";
 import { resume, rewind } from "./commands";
 
 import type { ThunkArgs } from "../types";
+import type { ThreadContext } from "../../types";
 
-export function continueToHere(line: number, column?: number) {
+export function continueToHere(
+  cx: ThreadContext,
+  line: number,
+  column?: number
+) {
   return async function({ dispatch, getState }: ThunkArgs) {
-    const thread = getCurrentThread(getState());
     const selectedSource = getSelectedSource(getState());
-    const selectedFrame = getSelectedFrame(getState(), thread);
+    const selectedFrame = getSelectedFrame(getState(), cx.thread);
 
     if (!selectedFrame || !selectedSource) {
       return;
     }
 
     const debugLine = selectedFrame.location.line;
     if (debugLine == line) {
       return;
     }
 
     const action =
       getCanRewind(getState()) && line < debugLine ? rewind : resume;
 
     await dispatch(
-      addHiddenBreakpoint({
+      addHiddenBreakpoint(cx, {
         line,
         column: column,
         sourceId: selectedSource.id
       })
     );
 
-    dispatch(action());
+    dispatch(action(cx));
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/fetchScopes.js
+++ b/devtools/client/debugger/new/src/actions/pause/fetchScopes.js
@@ -2,28 +2,29 @@
  * 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 { getSelectedFrame, getGeneratedFrameScope } from "../../selectors";
 import { mapScopes } from "./mapScopes";
 import { PROMISE } from "../utils/middleware/promise";
-import type { ThreadId } from "../../types";
+import type { ThreadContext } from "../../types";
 import type { ThunkArgs } from "../types";
 
-export function fetchScopes(thread: ThreadId) {
+export function fetchScopes(cx: ThreadContext) {
   return async function({ dispatch, getState, client, sourceMaps }: ThunkArgs) {
-    const frame = getSelectedFrame(getState(), thread);
+    const frame = getSelectedFrame(getState(), cx.thread);
     if (!frame || getGeneratedFrameScope(getState(), frame.id)) {
       return;
     }
 
     const scopes = dispatch({
       type: "ADD_SCOPES",
-      thread,
+      cx,
+      thread: cx.thread,
       frame,
       [PROMISE]: client.getFrameScopes(frame)
     });
 
-    await dispatch(mapScopes(scopes, frame));
+    await dispatch(mapScopes(cx, scopes, frame));
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/mapFrames.js
+++ b/devtools/client/debugger/new/src/actions/pause/mapFrames.js
@@ -11,17 +11,17 @@ import {
   getSourceFromId,
   getSelectedFrame
 } from "../../selectors";
 
 import assert from "../../utils/assert";
 import { findClosestFunction } from "../../utils/ast";
 import { setSymbols } from "../sources/symbols";
 
-import type { Frame, ThreadId } from "../../types";
+import type { Frame, ThreadContext } from "../../types";
 import type { State } from "../../reducers/types";
 import type { ThunkArgs } from "../types";
 
 import { isGeneratedId } from "devtools-source-map";
 
 function isFrameBlackboxed(state, frame) {
   const source = getSource(state, frame.location.sourceId);
   return source && source.isBlackBoxed;
@@ -150,53 +150,54 @@ async function expandFrames(
         generatedLocation: frame.generatedLocation,
         originalDisplayName: originalFrame.displayName
       });
     });
   }
   return result;
 }
 
-async function updateFrameSymbols(frames, { dispatch, getState }) {
+async function updateFrameSymbols(cx, frames, { dispatch, getState }) {
   await Promise.all(
     frames.map(frame => {
       const source = getSourceFromId(getState(), frame.location.sourceId);
-      return dispatch(setSymbols({ source }));
+      return dispatch(setSymbols({ cx, source }));
     })
   );
 }
 
 /**
  * Map call stack frame locations and display names to originals.
  * e.g.
  * 1. When the debuggee pauses
  * 2. When a source is pretty printed
  * 3. When symbols are loaded
  * @memberof actions/pause
  * @static
  */
-export function mapFrames(thread: ThreadId) {
+export function mapFrames(cx: ThreadContext) {
   return async function(thunkArgs: ThunkArgs) {
     const { dispatch, getState, sourceMaps } = thunkArgs;
-    const frames = getFrames(getState(), thread);
+    const frames = getFrames(getState(), cx.thread);
     if (!frames) {
       return;
     }
 
     let mappedFrames = await updateFrameLocations(frames, sourceMaps);
-    await updateFrameSymbols(mappedFrames, thunkArgs);
+    await updateFrameSymbols(cx, mappedFrames, thunkArgs);
 
     mappedFrames = await expandFrames(mappedFrames, sourceMaps, getState);
     mappedFrames = mapDisplayNames(mappedFrames, getState);
 
     const selectedFrameId = getSelectedFrameId(
       getState(),
-      thread,
+      cx.thread,
       mappedFrames
     );
     dispatch({
       type: "MAP_FRAMES",
-      thread,
+      cx,
+      thread: cx.thread,
       frames: mappedFrames,
       selectedFrameId
     });
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/mapScopes.js
+++ b/devtools/client/debugger/new/src/actions/pause/mapScopes.js
@@ -5,81 +5,90 @@
 // @flow
 
 import {
   getSource,
   getMapScopes,
   getSelectedFrame,
   getSelectedGeneratedScope,
   getSelectedOriginalScope,
-  getCurrentThread
+  getThreadContext
 } from "../../selectors";
 import { loadSourceText } from "../sources/loadSourceText";
 import { PROMISE } from "../utils/middleware/promise";
+import assert from "../../utils/assert";
 
 import { features } from "../../utils/prefs";
 import { log } from "../../utils/log";
 import { isGenerated, isOriginal } from "../../utils/source";
-import type { Frame, Scope } from "../../types";
+import type { Frame, Scope, ThreadContext } from "../../types";
 
 import type { ThunkArgs } from "../types";
 
 import { buildMappedScopes } from "../../utils/pause/mapScopes";
 
 export function toggleMapScopes() {
   return async function({ dispatch, getState, client, sourceMaps }: ThunkArgs) {
     if (getMapScopes(getState())) {
       return dispatch({ type: "TOGGLE_MAP_SCOPES", mapScopes: false });
     }
 
     dispatch({ type: "TOGGLE_MAP_SCOPES", mapScopes: true });
 
-    const thread = getCurrentThread(getState());
-    if (getSelectedOriginalScope(getState(), thread)) {
+    const cx = getThreadContext(getState());
+
+    if (getSelectedOriginalScope(getState(), cx.thread)) {
       return;
     }
 
-    const scopes = getSelectedGeneratedScope(getState(), thread);
-    const frame = getSelectedFrame(getState(), thread);
+    const scopes = getSelectedGeneratedScope(getState(), cx.thread);
+    const frame = getSelectedFrame(getState(), cx.thread);
     if (!scopes || !frame) {
       return;
     }
 
-    dispatch(mapScopes(Promise.resolve(scopes.scope), frame));
+    dispatch(mapScopes(cx, Promise.resolve(scopes.scope), frame));
   };
 }
 
-export function mapScopes(scopes: Promise<Scope>, frame: Frame) {
+export function mapScopes(
+  cx: ThreadContext,
+  scopes: Promise<Scope>,
+  frame: Frame
+) {
   return async function({ dispatch, getState, client, sourceMaps }: ThunkArgs) {
+    assert(cx.thread == frame.thread, "Thread mismatch");
+
     const generatedSource = getSource(
       getState(),
       frame.generatedLocation.sourceId
     );
 
     const source = getSource(getState(), frame.location.sourceId);
 
     await dispatch({
       type: "MAP_SCOPES",
-      thread: frame.thread,
+      cx,
+      thread: cx.thread,
       frame,
       [PROMISE]: (async function() {
         if (
           !features.mapScopes ||
           !source ||
           !generatedSource ||
           generatedSource.isWasm ||
           source.isPrettyPrinted ||
           isGenerated(source)
         ) {
           return null;
         }
 
-        await dispatch(loadSourceText({ source }));
+        await dispatch(loadSourceText({ cx, source }));
         if (isOriginal(source)) {
-          await dispatch(loadSourceText({ source: generatedSource }));
+          await dispatch(loadSourceText({ cx, source: generatedSource }));
         }
 
         try {
           return await buildMappedScopes(
             source,
             frame,
             await scopes,
             sourceMaps,
--- a/devtools/client/debugger/new/src/actions/pause/paused.js
+++ b/devtools/client/debugger/new/src/actions/pause/paused.js
@@ -2,24 +2,26 @@
  * 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 {
   getHiddenBreakpoint,
   isEvaluatingExpression,
   getSelectedFrame,
-  wasStepping
+  wasStepping,
+  getThreadContext
 } from "../../selectors";
 
 import { mapFrames } from ".";
 import { removeBreakpoint } from "../breakpoints";
 import { evaluateExpressions } from "../expressions";
 import { selectLocation } from "../sources";
 import { togglePaneCollapse } from "../ui";
+import assert from "../../utils/assert";
 
 import { fetchScopes } from "./fetchScopes";
 
 import type { Pause } from "../../types";
 import type { ThunkArgs } from "../types";
 
 /**
  * Debugger has just paused
@@ -37,34 +39,38 @@ export function paused(pauseInfo: Pause)
       type: "PAUSED",
       thread,
       why,
       frames,
       selectedFrameId: topFrame ? topFrame.id : undefined,
       loadedObjects: loadedObjects || []
     });
 
+    // Get a context capturing the newly paused and selected thread.
+    const cx = getThreadContext(getState());
+    assert(cx.thread == thread, "Thread mismatch");
+
     const hiddenBreakpoint = getHiddenBreakpoint(getState());
     if (hiddenBreakpoint) {
-      dispatch(removeBreakpoint(hiddenBreakpoint));
+      dispatch(removeBreakpoint(cx, hiddenBreakpoint));
     }
 
-    await dispatch(mapFrames(thread));
+    await dispatch(mapFrames(cx));
 
     const selectedFrame = getSelectedFrame(getState(), thread);
     if (selectedFrame) {
-      await dispatch(selectLocation(selectedFrame.location));
+      await dispatch(selectLocation(cx, selectedFrame.location));
     }
 
     if (!wasStepping(getState(), thread)) {
       dispatch(togglePaneCollapse("end", false));
     }
 
-    await dispatch(fetchScopes(thread));
+    await dispatch(fetchScopes(cx));
 
     // Run after fetching scoping data so that it may make use of the sourcemap
     // expression mappings for local variables.
     const atException = why.type == "exception";
     if (!atException || !isEvaluatingExpression(getState(), thread)) {
-      await dispatch(evaluateExpressions());
+      await dispatch(evaluateExpressions(cx));
     }
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/resumed.js
+++ b/devtools/client/debugger/new/src/actions/pause/resumed.js
@@ -1,15 +1,15 @@
 /* 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 { isStepping, getPauseReason } from "../../selectors";
+import { isStepping, getPauseReason, getThreadContext } from "../../selectors";
 import { evaluateExpressions } from "../expressions";
 import { inDebuggerEval } from "../../utils/pause";
 
 import type { ThunkArgs } from "../types";
 import type { ResumedPacket } from "../../client/firefox/types";
 
 /**
  * Debugger has just resumed
@@ -21,13 +21,14 @@ export function resumed(packet: ResumedP
   return async ({ dispatch, client, getState }: ThunkArgs) => {
     const thread = packet.from;
     const why = getPauseReason(getState(), thread);
     const wasPausedInEval = inDebuggerEval(why);
     const wasStepping = isStepping(getState(), thread);
 
     dispatch({ type: "RESUME", thread, wasStepping });
 
-    if (!wasStepping && !wasPausedInEval) {
-      await dispatch(evaluateExpressions());
+    const cx = getThreadContext(getState());
+    if (!wasStepping && !wasPausedInEval && cx.thread == thread) {
+      await dispatch(evaluateExpressions(cx));
     }
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/selectFrame.js
+++ b/devtools/client/debugger/new/src/actions/pause/selectFrame.js
@@ -2,30 +2,34 @@
  * 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 { selectLocation } from "../sources";
 import { evaluateExpressions } from "../expressions";
 import { fetchScopes } from "./fetchScopes";
+import assert from "../../utils/assert";
 
-import type { Frame } from "../../types";
+import type { Frame, ThreadContext } from "../../types";
 import type { ThunkArgs } from "../types";
 
 /**
  * @memberof actions/pause
  * @static
  */
-export function selectFrame(frame: Frame) {
+export function selectFrame(cx: ThreadContext, frame: Frame) {
   return async ({ dispatch, client, getState, sourceMaps }: ThunkArgs) => {
+    assert(cx.thread == frame.thread, "Thread mismatch");
+
     dispatch({
       type: "SELECT_FRAME",
-      thread: frame.thread,
+      cx,
+      thread: cx.thread,
       frame
     });
 
-    dispatch(selectLocation(frame.location));
+    dispatch(selectLocation(cx, frame.location));
 
-    dispatch(evaluateExpressions());
-    dispatch(fetchScopes(frame.thread));
+    dispatch(evaluateExpressions(cx));
+    dispatch(fetchScopes(cx));
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/setPopupObjectProperties.js
+++ b/devtools/client/debugger/new/src/actions/pause/setPopupObjectProperties.js
@@ -1,30 +1,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
-import { getPopupObjectProperties, getCurrentThread } from "../../selectors";
+import { getPopupObjectProperties } from "../../selectors";
 import type { ThunkArgs } from "../types";
+import type { ThreadContext } from "../../types";
 
 /**
  * @memberof actions/pause
  * @static
  */
-export function setPopupObjectProperties(object: any, properties: Object) {
+export function setPopupObjectProperties(
+  cx: ThreadContext,
+  object: any,
+  properties: Object
+) {
   return ({ dispatch, client, getState }: ThunkArgs) => {
     const objectId = object.actor || object.objectId;
-    const thread = getCurrentThread(getState());
 
-    if (getPopupObjectProperties(getState(), thread, object.actor)) {
+    if (getPopupObjectProperties(getState(), cx.thread, object.actor)) {
       return;
     }
 
     dispatch({
       type: "SET_POPUP_OBJECT_PROPERTIES",
-      thread,
+      cx,
+      thread: cx.thread,
       objectId,
       properties
     });
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/tests/pause.spec.js
+++ b/devtools/client/debugger/new/src/actions/pause/tests/pause.spec.js
@@ -104,91 +104,96 @@ function resumedPacket() {
 describe("pause", () => {
   describe("stepping", () => {
     it("should set and clear the command", async () => {
       const { dispatch, getState } = createStore(mockThreadClient);
       const mockPauseInfo = createPauseInfo();
 
       await dispatch(actions.newSource(makeSource("foo1")));
       await dispatch(actions.paused(mockPauseInfo));
-      const stepped = dispatch(actions.stepIn());
+      const cx = selectors.getThreadContext(getState());
+      const stepped = dispatch(actions.stepIn(cx));
       expect(isStepping(getState(), "FakeThread")).toBeTruthy();
       if (!stepInResolve) {
         throw new Error("no stepInResolve");
       }
       await stepInResolve();
       await stepped;
       expect(isStepping(getState(), "FakeThread")).toBeFalsy();
     });
 
     it("should only step when paused", async () => {
       const client = { stepIn: jest.fn() };
-      const { dispatch } = createStore(client);
+      const { dispatch, cx } = createStore(client);
 
-      dispatch(actions.stepIn());
+      dispatch(actions.stepIn(cx));
       expect(client.stepIn.mock.calls).toHaveLength(0);
     });
 
     it("should step when paused", async () => {
       const { dispatch, getState } = createStore(mockThreadClient);
       const mockPauseInfo = createPauseInfo();
 
       await dispatch(actions.newSource(makeSource("foo1")));
       await dispatch(actions.paused(mockPauseInfo));
-      dispatch(actions.stepIn());
+      const cx = selectors.getThreadContext(getState());
+      dispatch(actions.stepIn(cx));
       expect(isStepping(getState(), "FakeThread")).toBeTruthy();
     });
 
     it("should step over when paused", async () => {
       const store = createStore(mockThreadClient);
       const { dispatch, getState } = store;
       const mockPauseInfo = createPauseInfo();
 
       await dispatch(actions.newSource(makeSource("foo1")));
       await dispatch(actions.paused(mockPauseInfo));
+      const cx = selectors.getThreadContext(getState());
       const getNextStepSpy = jest.spyOn(parser, "getNextStep");
-      dispatch(actions.stepOver());
+      dispatch(actions.stepOver(cx));
       expect(getNextStepSpy).not.toBeCalled();
       expect(isStepping(getState(), "FakeThread")).toBeTruthy();
     });
 
     it("should step over when paused before an await", async () => {
       const store = createStore(mockThreadClient);
-      const { dispatch } = store;
+      const { dispatch, getState } = store;
       const mockPauseInfo = createPauseInfo({
         sourceId: "await",
         line: 2,
         column: 0
       });
 
       const source = makeSource("await");
       await dispatch(actions.newSource(source));
 
       await dispatch(actions.paused(mockPauseInfo));
+      const cx = selectors.getThreadContext(getState());
       const getNextStepSpy = jest.spyOn(parser, "getNextStep");
-      dispatch(actions.stepOver());
+      dispatch(actions.stepOver(cx));
       expect(getNextStepSpy).toBeCalled();
       getNextStepSpy.mockRestore();
     });
 
     it("should step over when paused after an await", async () => {
       const store = createStore(mockThreadClient);
-      const { dispatch } = store;
+      const { dispatch, getState } = store;
       const mockPauseInfo = createPauseInfo({
         sourceId: "await",
         line: 2,
         column: 6
       });
 
       const source = makeSource("await");
       await dispatch(actions.newSource(source));
 
       await dispatch(actions.paused(mockPauseInfo));
+      const cx = selectors.getThreadContext(getState());
       const getNextStepSpy = jest.spyOn(parser, "getNextStep");
-      dispatch(actions.stepOver());
+      dispatch(actions.stepOver(cx));
       expect(getNextStepSpy).toBeCalled();
       getNextStepSpy.mockRestore();
     });
 
     it("getting frame scopes with bindings", async () => {
       const generatedLocation = {
         sourceId: "foo",
         line: 1,
@@ -362,32 +367,37 @@ describe("pause", () => {
           thread: "FakeThread"
         }
       ]);
     });
   });
 
   describe("resumed", () => {
     it("should not evaluate expression while stepping", async () => {
-      const client = { evaluateExpressions: jest.fn() };
-      const { dispatch } = createStore(client);
+      const client = { ...mockThreadClient, evaluateExpressions: jest.fn() };
+      const { dispatch, getState } = createStore(client);
+      const mockPauseInfo = createPauseInfo();
 
-      dispatch(actions.stepIn());
+      await dispatch(actions.newSource(makeSource("foo1")));
+      await dispatch(actions.paused(mockPauseInfo));
+
+      const cx = selectors.getThreadContext(getState());
+      dispatch(actions.stepIn(cx));
       await dispatch(actions.resumed(resumedPacket()));
       expect(client.evaluateExpressions.mock.calls).toHaveLength(1);
     });
 
     it("resuming - will re-evaluate watch expressions", async () => {
       const store = createStore(mockThreadClient);
-      const { dispatch, getState } = store;
+      const { dispatch, getState, cx } = store;
       const mockPauseInfo = createPauseInfo();
 
       await dispatch(actions.newSource(makeSource("foo1")));
       await dispatch(actions.newSource(makeSource("foo")));
-      dispatch(actions.addExpression("foo"));
+      await dispatch(actions.addExpression(cx, "foo"));
       await waitForState(store, state => selectors.getExpression(state, "foo"));
 
       mockThreadClient.evaluateExpressions = () => new Promise(r => r(["YAY"]));
       await dispatch(actions.paused(mockPauseInfo));
 
       await dispatch(actions.resumed(resumedPacket()));
       const expression = selectors.getExpression(getState(), "foo");
       expect(expression.value).toEqual("YAY");
--- a/devtools/client/debugger/new/src/actions/preview.js
+++ b/devtools/client/debugger/new/src/actions/preview.js
@@ -17,17 +17,17 @@ import {
   getSelectedFrame,
   getSymbols,
   getCurrentThread
 } from "../selectors";
 
 import { getMappedExpression } from "./expressions";
 
 import type { Action, ThunkArgs } from "./types";
-import type { Position } from "../types";
+import type { Position, Context } from "../types";
 import type { AstLocation } from "../workers/parser";
 
 function findExpressionMatch(state, codeMirror, tokenPos) {
   const source = getSelectedSource(state);
   if (!source) {
     return;
   }
 
@@ -38,16 +38,17 @@ function findExpressionMatch(state, code
     match = getExpressionFromCoords(codeMirror, tokenPos);
   } else {
     match = findBestMatchExpression(symbols, tokenPos);
   }
   return match;
 }
 
 export function updatePreview(
+  cx: Context,
   target: HTMLElement,
   tokenPos: Object,
   codeMirror: any
 ) {
   return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const cursorPos = target.getBoundingClientRect();
 
     if (
@@ -63,29 +64,31 @@ export function updatePreview(
     }
 
     const { expression, location } = match;
 
     if (isConsole(expression)) {
       return;
     }
 
-    dispatch(setPreview(expression, location, tokenPos, cursorPos));
+    dispatch(setPreview(cx, expression, location, tokenPos, cursorPos));
   };
 }
 
 export function setPreview(
+  cx: Context,
   expression: string,
   location: AstLocation,
   tokenPos: Position,
   cursorPos: ClientRect
 ) {
   return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     await dispatch({
       type: "SET_PREVIEW",
+      cx,
       [PROMISE]: (async function() {
         const source = getSelectedSource(getState());
         if (!source) {
           return;
         }
 
         const thread = getCurrentThread(getState());
         const selectedFrame = getSelectedFrame(getState(), thread);
@@ -121,22 +124,23 @@ export function setPreview(
           tokenPos,
           cursorPos
         };
       })()
     });
   };
 }
 
-export function clearPreview() {
+export function clearPreview(cx: Context) {
   return ({ dispatch, getState, client }: ThunkArgs) => {
     const currentSelection = getPreview(getState());
     if (!currentSelection) {
       return;
     }
 
     return dispatch(
       ({
-        type: "CLEAR_SELECTION"
+        type: "CLEAR_SELECTION",
+        cx
       }: Action)
     );
   };
 }
--- a/devtools/client/debugger/new/src/actions/project-text-search.js
+++ b/devtools/client/debugger/new/src/actions/project-text-search.js
@@ -15,103 +15,109 @@ import { isThirdParty } from "../utils/s
 import { loadSourceText } from "./sources/loadSourceText";
 import {
   statusType,
   getTextSearchOperation,
   getTextSearchStatus
 } from "../reducers/project-text-search";
 
 import type { Action, ThunkArgs } from "./types";
+import type { Context } from "../types";
 import type { SearchOperation } from "../reducers/project-text-search";
 
-export function addSearchQuery(query: string): Action {
-  return { type: "ADD_QUERY", query };
+export function addSearchQuery(cx: Context, query: string): Action {
+  return { type: "ADD_QUERY", cx, query };
 }
 
-export function addOngoingSearch(ongoingSearch: SearchOperation): Action {
-  return { type: "ADD_ONGOING_SEARCH", ongoingSearch };
+export function addOngoingSearch(
+  cx: Context,
+  ongoingSearch: SearchOperation
+): Action {
+  return { type: "ADD_ONGOING_SEARCH", cx, ongoingSearch };
 }
 
 export function addSearchResult(
+  cx: Context,
   sourceId: string,
   filepath: string,
   matches: Object[]
 ): Action {
   return {
     type: "ADD_SEARCH_RESULT",
+    cx,
     result: { sourceId, filepath, matches }
   };
 }
 
-export function clearSearchResults(): Action {
-  return { type: "CLEAR_SEARCH_RESULTS" };
+export function clearSearchResults(cx: Context): Action {
+  return { type: "CLEAR_SEARCH_RESULTS", cx };
 }
 
-export function clearSearch(): Action {
-  return { type: "CLEAR_SEARCH" };
+export function clearSearch(cx: Context): Action {
+  return { type: "CLEAR_SEARCH", cx };
 }
 
-export function updateSearchStatus(status: string): Action {
-  return { type: "UPDATE_STATUS", status };
+export function updateSearchStatus(cx: Context, status: string): Action {
+  return { type: "UPDATE_STATUS", cx, status };
 }
 
-export function closeProjectSearch() {
+export function closeProjectSearch(cx: Context) {
   return ({ dispatch, getState }: ThunkArgs) => {
-    dispatch(stopOngoingSearch());
+    dispatch(stopOngoingSearch(cx));
     dispatch({ type: "CLOSE_PROJECT_SEARCH" });
   };
 }
 
-export function stopOngoingSearch() {
+export function stopOngoingSearch(cx: Context) {
   return ({ dispatch, getState }: ThunkArgs) => {
     const state = getState();
     const ongoingSearch = getTextSearchOperation(state);
     const status = getTextSearchStatus(state);
     if (ongoingSearch && status !== statusType.done) {
       ongoingSearch.cancel();
-      dispatch(updateSearchStatus(statusType.cancelled));
+      dispatch(updateSearchStatus(cx, statusType.cancelled));
     }
   };
 }
 
-export function searchSources(query: string) {
+export function searchSources(cx: Context, query: string) {
   let cancelled = false;
 
   const search = async ({ dispatch, getState }: ThunkArgs) => {
-    dispatch(stopOngoingSearch());
-    await dispatch(addOngoingSearch(search));
-    await dispatch(clearSearchResults());
-    await dispatch(addSearchQuery(query));
-    dispatch(updateSearchStatus(statusType.fetching));
+    dispatch(stopOngoingSearch(cx));
+    await dispatch(addOngoingSearch(cx, search));
+    await dispatch(clearSearchResults(cx));
+    await dispatch(addSearchQuery(cx, query));
+    dispatch(updateSearchStatus(cx, statusType.fetching));
     const validSources = getSourceList(getState()).filter(
       source => !hasPrettySource(getState(), source.id) && !isThirdParty(source)
     );
     for (const source of validSources) {
       if (cancelled) {
         return;
       }
-      await dispatch(loadSourceText({ source }));
-      await dispatch(searchSource(source.id, query));
+      await dispatch(loadSourceText({ cx, source }));
+      await dispatch(searchSource(cx, source.id, query));
     }
-    dispatch(updateSearchStatus(statusType.done));
+    dispatch(updateSearchStatus(cx, statusType.done));
   };
 
   search.cancel = () => {
     cancelled = true;
   };
 
   return search;
 }
 
-export function searchSource(sourceId: string, query: string) {
+export function searchSource(cx: Context, sourceId: string, query: string) {
   return async ({ dispatch, getState }: ThunkArgs) => {
     const source = getSource(getState(), sourceId);
     if (!source) {
       return;
     }
 
     const matches = await findSourceMatches(source, query);
     if (!matches.length) {
       return;
     }
-    dispatch(addSearchResult(source.id, source.url, matches));
+    dispatch(addSearchResult(cx, source.id, source.url, matches));
   };
 }
--- a/devtools/client/debugger/new/src/actions/source-tree.js
+++ b/devtools/client/debugger/new/src/actions/source-tree.js
@@ -1,27 +1,29 @@
 /* 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 { Action, FocusItem, ThunkArgs } from "./types";
+import type { Context } from "../types";
 
 export function setExpandedState(thread: string, expanded: Set<string>) {
   return ({ dispatch, getState }: ThunkArgs) => {
     dispatch(
       ({
         type: "SET_EXPANDED_STATE",
         thread,
         expanded
       }: Action)
     );
   };
 }
 
-export function focusItem(item: FocusItem) {
+export function focusItem(cx: Context, item: FocusItem) {
   return ({ dispatch, getState }: ThunkArgs) => {
     dispatch({
       type: "SET_FOCUSED_SOURCE_ITEM",
+      cx,
       item
     });
   };
 }
--- a/devtools/client/debugger/new/src/actions/sources/blackbox.js
+++ b/devtools/client/debugger/new/src/actions/sources/blackbox.js
@@ -11,28 +11,28 @@
 
 import { isOriginalId, originalToGeneratedId } from "devtools-source-map";
 import { recordEvent } from "../../utils/telemetry";
 import { features } from "../../utils/prefs";
 import { getSourceFromId } from "../../selectors";
 
 import { PROMISE } from "../utils/middleware/promise";
 
-import type { Source } from "../../types";
+import type { Source, Context } from "../../types";
 import type { ThunkArgs } from "../types";
 
 async function blackboxActors(state, client, sourceId, isBlackBoxed, range?) {
   const source = getSourceFromId(state, sourceId);
   for (const sourceActor of source.actors) {
     await client.blackBox(sourceActor, isBlackBoxed, range);
   }
   return { isBlackBoxed: !isBlackBoxed };
 }
 
-export function toggleBlackBox(source: Source) {
+export function toggleBlackBox(cx: Context, source: Source) {
   return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const { isBlackBoxed } = source;
 
     if (!isBlackBoxed) {
       recordEvent("blackbox");
     }
 
     let sourceId, range;
@@ -40,16 +40,17 @@ export function toggleBlackBox(source: S
       range = await sourceMaps.getFileGeneratedRange(source);
       sourceId = originalToGeneratedId(source.id);
     } else {
       sourceId = source.id;
     }
 
     return dispatch({
       type: "BLACKBOX",
+      cx,
       source,
       [PROMISE]: blackboxActors(
         getState(),
         client,
         sourceId,
         isBlackBoxed,
         range
       )
--- a/devtools/client/debugger/new/src/actions/sources/loadSourceText.js
+++ b/devtools/client/debugger/new/src/actions/sources/loadSourceText.js
@@ -22,17 +22,17 @@ import {
   memoizeableAction,
   type MemoizedAction
 } from "../../utils/memoizableAction";
 
 import { Telemetry } from "devtools-modules";
 
 import type { ThunkArgs } from "../types";
 
-import type { Source } from "../../types";
+import type { Source, Context } from "../../types";
 
 // Measures the time it takes for a source to load
 const loadSourceHistogram = "DEVTOOLS_DEBUGGER_LOAD_SOURCE_MS";
 const telemetry = new Telemetry();
 
 async function loadSource(
   state,
   source: Source,
@@ -68,16 +68,17 @@ async function loadSource(
 
   return {
     text: response.source,
     contentType: response.contentType || "text/javascript"
   };
 }
 
 async function loadSourceTextPromise(
+  cx: Context,
   source: Source,
   { dispatch, getState, client, sourceMaps }: ThunkArgs
 ): Promise<?Source> {
   const epoch = getSourcesEpoch(getState());
   await dispatch({
     type: "LOAD_SOURCE_TEXT",
     sourceId: source.id,
     epoch,
@@ -87,40 +88,41 @@ async function loadSourceTextPromise(
   const newSource = getSource(getState(), source.id);
 
   if (!newSource) {
     return;
   }
 
   if (!newSource.isWasm && isLoaded(newSource)) {
     parser.setSource(newSource);
-    dispatch(setBreakpointPositions({ sourceId: newSource.id }));
+    dispatch(setBreakpointPositions({ cx, sourceId: newSource.id }));
 
     // Update the text in any breakpoints for this source by re-adding them.
     const breakpoints = getBreakpointsForSource(getState(), source.id);
     for (const { location, options, disabled } of breakpoints) {
-      await dispatch(addBreakpoint(location, options, disabled));
+      await dispatch(addBreakpoint(cx, location, options, disabled));
     }
   }
 
   return newSource;
 }
 
-export function loadSourceById(sourceId: string) {
+export function loadSourceById(cx: Context, sourceId: string) {
   return ({ getState, dispatch }: ThunkArgs) => {
     const source = getSourceFromId(getState(), sourceId);
-    return dispatch(loadSourceText({ source }));
+    return dispatch(loadSourceText({ cx, source }));
   };
 }
 
 export const loadSourceText: MemoizedAction<
-  { source: Source },
+  { cx: Context, source: Source },
   ?Source
 > = memoizeableAction("loadSourceText", {
   exitEarly: ({ source }) => !source,
   hasValue: ({ source }, { getState }) => isLoaded(source),
   getValue: ({ source }, { getState }) => getSource(getState(), source.id),
   createKey: ({ source }, { getState }) => {
     const epoch = getSourcesEpoch(getState());
     return `${epoch}:${source.id}`;
   },
-  action: ({ source }, thunkArgs) => loadSourceTextPromise(source, thunkArgs)
+  action: ({ cx, source }, thunkArgs) =>
+    loadSourceTextPromise(cx, source, thunkArgs)
 });
--- a/devtools/client/debugger/new/src/actions/sources/newSources.js
+++ b/devtools/client/debugger/new/src/actions/sources/newSources.js
@@ -23,23 +23,24 @@ import {
   isOriginal,
   isInlineScript
 } from "../../utils/source";
 import {
   getBlackBoxList,
   getSource,
   getPendingSelectedLocation,
   getPendingBreakpointsForSource,
-  hasBreakpointPositions
+  hasBreakpointPositions,
+  getContext
 } from "../../selectors";
 
 import { prefs } from "../../utils/prefs";
 import sourceQueue from "../../utils/source-queue";
 
-import type { Source, SourceId } from "../../types";
+import type { Source, SourceId, Context } from "../../types";
 import type { Action, ThunkArgs } from "../types";
 
 function createOriginalSource(
   originalUrl,
   generatedSource,
   sourceMaps
 ): Source {
   return {
@@ -52,47 +53,47 @@ function createOriginalSource(
     loadedState: "unloaded",
     introductionUrl: null,
     introductionType: undefined,
     isExtension: false,
     actors: []
   };
 }
 
-function loadSourceMaps(sources: Source[]) {
+function loadSourceMaps(cx: Context, sources: Source[]) {
   return async function({
     dispatch,
     sourceMaps
   }: ThunkArgs): Promise<Promise<Source>[]> {
     const sourceList = await Promise.all(
       sources.map(async ({ id }) => {
-        const originalSources = await dispatch(loadSourceMap(id));
+        const originalSources = await dispatch(loadSourceMap(cx, id));
         sourceQueue.queueSources(originalSources);
         return originalSources;
       })
     );
 
     await sourceQueue.flush();
 
     // We would like to sync breakpoints after we are done
     // loading source maps as sometimes generated and original
     // files share the same paths.
     for (const source of sources) {
-      dispatch(checkPendingBreakpoints(source.id));
+      dispatch(checkPendingBreakpoints(cx, source.id));
     }
 
     return flatten(sourceList);
   };
 }
 
 /**
  * @memberof actions/sources
  * @static
  */
-function loadSourceMap(sourceId: SourceId) {
+function loadSourceMap(cx: Context, sourceId: SourceId) {
   return async function({
     dispatch,
     getState,
     sourceMaps
   }: ThunkArgs): Promise<Source[]> {
     const source = getSource(getState(), sourceId);
 
     if (
@@ -126,60 +127,61 @@ function loadSourceMap(sourceId: SourceI
       // this was previously using 'source' and was at risk of resetting the
       // 'loadedState' field to 'loading', putting it in an inconsistent state.
       const currentSource = getSource(getState(), sourceId);
 
       // If this source doesn't have a sourcemap, enable it for pretty printing
       dispatch(
         ({
           type: "UPDATE_SOURCE",
+          cx,
           // NOTE: Flow https://github.com/facebook/flow/issues/6342 issue
           source: (({ ...currentSource, sourceMapURL: "" }: any): Source)
         }: Action)
       );
       return [];
     }
 
     return urls.map(url => createOriginalSource(url, source, sourceMaps));
   };
 }
 
 // If a request has been made to show this source, go ahead and
 // select it.
-function checkSelectedSource(sourceId: string) {
+function checkSelectedSource(cx: Context, sourceId: string) {
   return async ({ dispatch, getState }: ThunkArgs) => {
     const source = getSource(getState(), sourceId);
     const pendingLocation = getPendingSelectedLocation(getState());
 
     if (!pendingLocation || !pendingLocation.url || !source || !source.url) {
       return;
     }
 
     const pendingUrl = pendingLocation.url;
     const rawPendingUrl = getRawSourceURL(pendingUrl);
 
     if (rawPendingUrl === source.url) {
       if (isPrettyURL(pendingUrl)) {
-        const prettySource = await dispatch(togglePrettyPrint(source.id));
-        return dispatch(checkPendingBreakpoints(prettySource.id));
+        const prettySource = await dispatch(togglePrettyPrint(cx, source.id));
+        return dispatch(checkPendingBreakpoints(cx, prettySource.id));
       }
 
       await dispatch(
-        selectLocation({
+        selectLocation(cx, {
           sourceId: source.id,
           line:
             typeof pendingLocation.line === "number" ? pendingLocation.line : 0,
           column: pendingLocation.column
         })
       );
     }
   };
 }
 
-function checkPendingBreakpoints(sourceId: string) {
+function checkPendingBreakpoints(cx: Context, sourceId: string) {
   return async ({ dispatch, getState }: ThunkArgs) => {
     // source may have been modified by selectLocation
     const source = getSource(getState(), sourceId);
     if (!source) {
       return;
     }
 
     const pendingBreakpoints = getPendingBreakpointsForSource(
@@ -187,35 +189,35 @@ function checkPendingBreakpoints(sourceI
       source
     );
 
     if (pendingBreakpoints.length === 0) {
       return;
     }
 
     // load the source text if there is a pending breakpoint for it
-    await dispatch(loadSourceText({ source }));
+    await dispatch(loadSourceText({ cx, source }));
 
     await Promise.all(
       pendingBreakpoints.map(bp => {
-        return dispatch(syncBreakpoint(sourceId, bp));
+        return dispatch(syncBreakpoint(cx, sourceId, bp));
       })
     );
   };
 }
 
-function restoreBlackBoxedSources(sources: Source[]) {
+function restoreBlackBoxedSources(cx: Context, sources: Source[]) {
   return async ({ dispatch }: ThunkArgs) => {
     const tabs = getBlackBoxList();
     if (tabs.length == 0) {
       return;
     }
     for (const source of sources) {
       if (tabs.includes(source.url) && !source.isBlackBoxed) {
-        dispatch(toggleBlackBox(source));
+        dispatch(toggleBlackBox(cx, source));
       }
     }
   };
 }
 
 /**
  * Handler for the debugger client's unsolicited newSource notification.
  * @memberof actions/sources
@@ -224,35 +226,37 @@ function restoreBlackBoxedSources(source
 export function newSource(source: Source) {
   return async ({ dispatch }: ThunkArgs) => {
     await dispatch(newSources([source]));
   };
 }
 
 export function newSources(sources: Source[]) {
   return async ({ dispatch, getState }: ThunkArgs) => {
+    const cx = getContext(getState());
+
     const _newSources = sources.filter(
       source => !getSource(getState(), source.id) || isInlineScript(source)
     );
 
     const sourcesNeedingPositions = _newSources.filter(source =>
       hasBreakpointPositions(getState(), source.id)
     );
 
-    dispatch({ type: "ADD_SOURCES", sources });
+    dispatch({ type: "ADD_SOURCES", cx, sources });
 
     for (const source of _newSources) {
-      dispatch(checkSelectedSource(source.id));
+      dispatch(checkSelectedSource(cx, source.id));
     }
 
     // Adding new sources may have cleared this file's breakpoint positions
     // in cases where a new <script> loaded in the HTML, so we manually
     // re-request new breakpoint positions.
     for (const source of sourcesNeedingPositions) {
       if (!hasBreakpointPositions(getState(), source.id)) {
-        dispatch(setBreakpointPositions({ sourceId: source.id }));
+        dispatch(setBreakpointPositions({ cx, sourceId: source.id }));
       }
     }
 
-    dispatch(restoreBlackBoxedSources(_newSources));
-    dispatch(loadSourceMaps(_newSources));
+    dispatch(restoreBlackBoxedSources(cx, _newSources));
+    dispatch(loadSourceMaps(cx, _newSources));
   };
 }
--- a/devtools/client/debugger/new/src/actions/sources/prettyPrint.js
+++ b/devtools/client/debugger/new/src/actions/sources/prettyPrint.js
@@ -13,24 +13,24 @@ import { prettyPrint } from "../../worke
 import { getPrettySourceURL, isLoaded } from "../../utils/source";
 import { loadSourceText } from "./loadSourceText";
 import { mapFrames } from "../pause";
 import { selectSpecificLocation } from "../sources";
 
 import {
   getSource,
   getSourceFromId,
-  getSourceThreads,
   getSourceByURL,
-  getSelectedLocation
+  getSelectedLocation,
+  getThreadContext
 } from "../../selectors";
 
 import type { Action, ThunkArgs } from "../types";
 import { selectSource } from "./select";
-import type { JsSource, Source } from "../../types";
+import type { JsSource, Source, Context } from "../../types";
 
 export async function prettyPrintSource(
   sourceMaps: any,
   prettySource: Source,
   generatedSource: any
 ) {
   const url = getPrettySourceURL(generatedSource.url);
   const { code, mappings } = await prettyPrint({
@@ -46,17 +46,17 @@ export async function prettyPrintSource(
   }
   return {
     id: prettySource.id,
     text: code,
     contentType: "text/javascript"
   };
 }
 
-export function createPrettySource(sourceId: string) {
+export function createPrettySource(cx: Context, sourceId: string) {
   return async ({ dispatch, getState, sourceMaps }: ThunkArgs) => {
     const source = getSourceFromId(getState(), sourceId);
     const url = getPrettySourceURL(source.url);
     const id = await sourceMaps.generatedToOriginalId(sourceId, url);
 
     const prettySource: JsSource = {
       url,
       relativeUrl: url,
@@ -67,82 +67,82 @@ export function createPrettySource(sourc
       contentType: "text/javascript",
       loadedState: "loading",
       introductionUrl: null,
       introductionType: undefined,
       isExtension: false,
       actors: []
     };
 
-    dispatch(({ type: "ADD_SOURCE", source: prettySource }: Action));
-    await dispatch(selectSource(prettySource.id));
+    dispatch(({ type: "ADD_SOURCE", cx, source: prettySource }: Action));
+    await dispatch(selectSource(cx, prettySource.id));
 
     return prettySource;
   };
 }
 
-function selectPrettyLocation(prettySource: Source) {
+function selectPrettyLocation(cx: Context, prettySource: Source) {
   return async ({ dispatch, sourceMaps, getState }: ThunkArgs) => {
     let location = getSelectedLocation(getState());
 
     if (location) {
       location = await sourceMaps.getOriginalLocation(location);
       return dispatch(
-        selectSpecificLocation({ ...location, sourceId: prettySource.id })
+        selectSpecificLocation(cx, { ...location, sourceId: prettySource.id })
       );
     }
 
-    return dispatch(selectSource(prettySource.id));
+    return dispatch(selectSource(cx, prettySource.id));
   };
 }
 
 /**
  * Toggle the pretty printing of a source's text. All subsequent calls to
  * |getText| will return the pretty-toggled text. Nothing will happen for
  * non-javascript files.
  *
  * @memberof actions/sources
  * @static
  * @param string id The source form from the RDP.
  * @returns Promise
  *          A promise that resolves to [aSource, prettyText] or rejects to
  *          [aSource, error].
  */
-export function togglePrettyPrint(sourceId: string) {
+export function togglePrettyPrint(cx: Context, sourceId: string) {
   return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const source = getSource(getState(), sourceId);
     if (!source) {
       return {};
     }
 
     if (!source.isPrettyPrinted) {
       recordEvent("pretty_print");
     }
 
     if (!isLoaded(source)) {
-      await dispatch(loadSourceText({ source }));
+      await dispatch(loadSourceText({ cx, source }));
     }
 
     assert(
       sourceMaps.isGeneratedId(sourceId),
       "Pretty-printing only allowed on generated sources"
     );
 
     const url = getPrettySourceURL(source.url);
     const prettySource = getSourceByURL(getState(), url);
 
     if (prettySource) {
-      return dispatch(selectPrettyLocation(prettySource));
+      return dispatch(selectPrettyLocation(cx, prettySource));
     }
 
-    const newPrettySource = await dispatch(createPrettySource(sourceId));
-    await dispatch(selectPrettyLocation(newPrettySource));
+    const newPrettySource = await dispatch(createPrettySource(cx, sourceId));
+    await dispatch(selectPrettyLocation(cx, newPrettySource));
 
-    const threads = getSourceThreads(getState(), source);
-    await Promise.all(threads.map(thread => dispatch(mapFrames(thread))));
+    const threadcx = getThreadContext(getState());
+    await dispatch(mapFrames(threadcx));
 
-    await dispatch(setSymbols({ source: newPrettySource }));
+    await dispatch(setSymbols({ cx, source: newPrettySource }));
 
-    await dispatch(remapBreakpoints(sourceId));
+    await dispatch(remapBreakpoints(cx, sourceId));
 
     return newPrettySource;
   };
 }
--- a/devtools/client/debugger/new/src/actions/sources/select.js
+++ b/devtools/client/debugger/new/src/actions/sources/select.js
@@ -30,100 +30,116 @@ import {
   getSource,
   getSourceByURL,
   getPrettySource,
   getActiveSearch,
   getSelectedLocation,
   getSelectedSource
 } from "../../selectors";
 
-import type { SourceLocation, PartialPosition, Source } from "../../types";
+import type {
+  SourceLocation,
+  PartialPosition,
+  Source,
+  Context
+} from "../../types";
 import type { ThunkArgs } from "../types";
 
 export const setSelectedLocation = (
+  cx: Context,
   source: Source,
   location: SourceLocation
 ) => ({
   type: "SET_SELECTED_LOCATION",
+  cx,
   source,
   location
 });
 
-export const setPendingSelectedLocation = (url: string, options: Object) => ({
+export const setPendingSelectedLocation = (
+  cx: Context,
+  url: string,
+  options: Object
+) => ({
   type: "SET_PENDING_SELECTED_LOCATION",
+  cx,
   url: url,
   line: options.location ? options.location.line : null
 });
 
-export const clearSelectedLocation = () => ({
-  type: "CLEAR_SELECTED_LOCATION"
+export const clearSelectedLocation = (cx: Context) => ({
+  type: "CLEAR_SELECTED_LOCATION",
+  cx
 });
 
 /**
  * Deterministically select a source that has a given URL. This will
  * work regardless of the connection status or if the source exists
  * yet.
  *
  * This exists mostly for external things to interact with the
  * debugger.
  *
  * @memberof actions/sources
  * @static
  */
 export function selectSourceURL(
+  cx: Context,
   url: string,
   options: PartialPosition = { line: 1 }
 ) {
   return async ({ dispatch, getState, sourceMaps }: ThunkArgs) => {
     const source = getSourceByURL(getState(), url);
     if (!source) {
-      return dispatch(setPendingSelectedLocation(url, options));
+      return dispatch(setPendingSelectedLocation(cx, url, options));
     }
 
     const sourceId = source.id;
     const location = createLocation({ ...options, sourceId });
-    return dispatch(selectLocation(location));
+    return dispatch(selectLocation(cx, location));
   };
 }
 
 /**
  * @memberof actions/sources
  * @static
  */
 export function selectSource(
+  cx: Context,
   sourceId: string,
   options: PartialPosition = { line: 1 }
 ) {
   return async ({ dispatch }: ThunkArgs) => {
     const location = createLocation({ ...options, sourceId });
-    return dispatch(selectSpecificLocation(location));
+    return dispatch(selectSpecificLocation(cx, location));
   };
 }
 
 /**
  * @memberof actions/sources
  * @static
  */
 export function selectLocation(
+  cx: Context,
   location: SourceLocation,
   { keepContext = true }: Object = {}
 ) {
   return async ({ dispatch, getState, sourceMaps, client }: ThunkArgs) => {
     const currentSource = getSelectedSource(getState());
 
     if (!client) {
       // No connection, do nothing. This happens when the debugger is
       // shut down too fast and it tries to display a default source.
       return;
     }
 
     let source = getSource(getState(), location.sourceId);
     if (!source) {
       // If there is no source we deselect the current selected source
-      return dispatch(clearSelectedLocation());
+      return dispatch(clearSelectedLocation(cx));
     }
 
     const activeSearch = getActiveSearch(getState());
     if (activeSearch && activeSearch !== "file") {
       dispatch(closeActiveSearch());
     }
 
     // Preserve the current source map context (original / generated)
@@ -138,74 +154,74 @@ export function selectLocation(
       source = getSourceFromId(getState(), location.sourceId);
     }
 
     const tabSources = getSourcesForTabs(getState());
     if (!tabSources.includes(source)) {
       dispatch(addTab(source));
     }
 
-    dispatch(setSelectedLocation(source, location));
+    dispatch(setSelectedLocation(cx, source, location));
 
-    await dispatch(loadSourceText({ source }));
+    await dispatch(loadSourceText({ cx, source }));
     const loadedSource = getSource(getState(), source.id);
 
     if (!loadedSource) {
       // If there was a navigation while we were loading the loadedSource
       return;
     }
 
     if (
       keepContext &&
       prefs.autoPrettyPrint &&
       !getPrettySource(getState(), loadedSource.id) &&
       shouldPrettyPrint(loadedSource) &&
       isMinified(loadedSource)
     ) {
-      await dispatch(togglePrettyPrint(loadedSource.id));
-      dispatch(closeTab(loadedSource));
+      await dispatch(togglePrettyPrint(cx, loadedSource.id));
+      dispatch(closeTab(cx, loadedSource));
     }
 
-    dispatch(setSymbols({ source: loadedSource }));
-    dispatch(setOutOfScopeLocations());
+    dispatch(setSymbols({ cx, source: loadedSource }));
+    dispatch(setOutOfScopeLocations(cx));
 
     // If a new source is selected update the file search results
     const newSource = getSelectedSource(getState());
     if (currentSource && currentSource !== newSource) {
-      dispatch(updateActiveFileSearch());
+      dispatch(updateActiveFileSearch(cx));
     }
   };
 }
 
 /**
  * @memberof actions/sources
  * @static
  */
-export function selectSpecificLocation(location: SourceLocation) {
-  return selectLocation(location, { keepContext: false });
+export function selectSpecificLocation(cx: Context, location: SourceLocation) {
+  return selectLocation(cx, location, { keepContext: false });
 }
 
 /**
  * @memberof actions/sources
  * @static
  */
-export function jumpToMappedLocation(location: SourceLocation) {
+export function jumpToMappedLocation(cx: Context, location: SourceLocation) {
   return async function({ dispatch, getState, client, sourceMaps }: ThunkArgs) {
     if (!client) {
       return;
     }
 
     const pairedLocation = await mapLocation(getState(), sourceMaps, location);
 
-    return dispatch(selectSpecificLocation({ ...pairedLocation }));
+    return dispatch(selectSpecificLocation(cx, { ...pairedLocation }));
   };
 }
 
-export function jumpToMappedSelectedLocation() {
+export function jumpToMappedSelectedLocation(cx: Context) {
   return async function({ dispatch, getState }: ThunkArgs) {
     const location = getSelectedLocation(getState());
     if (!location) {
       return;
     }
 
-    await dispatch(jumpToMappedLocation(location));
+    await dispatch(jumpToMappedLocation(cx, location));
   };
 }
--- a/devtools/client/debugger/new/src/actions/sources/symbols.js
+++ b/devtools/client/debugger/new/src/actions/sources/symbols.js
@@ -13,44 +13,45 @@ import { loadSourceText } from "./loadSo
 import * as parser from "../../workers/parser";
 
 import { isLoaded } from "../../utils/source";
 import {
   memoizeableAction,
   type MemoizedAction
 } from "../../utils/memoizableAction";
 
-import type { Source } from "../../types";
+import type { Source, Context } from "../../types";
 import type { Symbols } from "../../reducers/types";
 
-async function doSetSymbols(source, { dispatch, getState }) {
+async function doSetSymbols(cx, source, { dispatch, getState }) {
   const sourceId = source.id;
 
   if (!isLoaded(source)) {
-    await dispatch(loadSourceText({ source }));
+    await dispatch(loadSourceText({ cx, source }));
   }
 
   await dispatch({
     type: "SET_SYMBOLS",
+    cx,
     sourceId,
     [PROMISE]: parser.getSymbols(sourceId)
   });
 
   const symbols = getSymbols(getState(), source);
   if (symbols && symbols.framework) {
     dispatch(updateTab(source, symbols.framework));
   }
 
   return symbols;
 }
 
-type Args = { source: Source };
+type Args = { cx: Context, source: Source };
 
 export const setSymbols: MemoizedAction<Args, ?Symbols> = memoizeableAction(
   "setSymbols",
   {
     exitEarly: ({ source }) => source.isWasm,
     hasValue: ({ source }, { getState }) => hasSymbols(getState(), source),
     getValue: ({ source }, { getState }) => getSymbols(getState(), source),
     createKey: ({ source }) => source.id,
-    action: ({ source }, thunkArgs) => doSetSymbols(source, thunkArgs)
+    action: ({ cx, source }, thunkArgs) => doSetSymbols(cx, source, thunkArgs)
   }
 );
--- a/devtools/client/debugger/new/src/actions/sources/tests/blackbox.spec.js
+++ b/devtools/client/debugger/new/src/actions/sources/tests/blackbox.spec.js
@@ -9,21 +9,21 @@ import {
   selectors,
   createStore,
   makeSource
 } from "../../../utils/test-head";
 
 describe("blackbox", () => {
   it("should blackbox a source", async () => {
     const store = createStore({ blackBox: async () => true });
-    const { dispatch, getState } = store;
+    const { dispatch, getState, cx } = store;
 
     const foo1Source = makeSource("foo1");
     await dispatch(actions.newSource(foo1Source));
-    await dispatch(actions.toggleBlackBox(foo1Source));
+    await dispatch(actions.toggleBlackBox(cx, foo1Source));
 
     const fooSource = selectors.getSource(getState(), "foo1");
 
     if (!fooSource) {
       throw new Error("foo should exist");
     }
 
     const thread = foo1Source.actors[0].thread;
--- a/devtools/client/debugger/new/src/actions/sources/tests/loadSource.spec.js
+++ b/devtools/client/debugger/new/src/actions/sources/tests/loadSource.spec.js
@@ -16,31 +16,31 @@ import {
   createSource,
   sourceThreadClient
 } from "../../tests/helpers/threadClient.js";
 import { getBreakpointsList } from "../../../selectors";
 
 describe("loadSourceText", () => {
   it("should load source text", async () => {
     const store = createStore(sourceThreadClient);
-    const { dispatch, getState } = store;
+    const { dispatch, getState, cx } = store;
 
     const foo1Source = makeSource("foo1");
     await dispatch(actions.newSource(foo1Source));
-    await dispatch(actions.loadSourceText({ source: foo1Source }));
+    await dispatch(actions.loadSourceText({ cx, source: foo1Source }));
     const fooSource = selectors.getSource(getState(), "foo1");
 
     if (!fooSource || typeof fooSource.text != "string") {
       throw new Error("bad fooSource");
     }
     expect(fooSource.text.indexOf("return foo1")).not.toBe(-1);
 
     const baseFoo2Source = makeSource("foo2");
     await dispatch(actions.newSource(baseFoo2Source));
-    await dispatch(actions.loadSourceText({ source: baseFoo2Source }));
+    await dispatch(actions.loadSourceText({ cx, source: baseFoo2Source }));
     const foo2Source = selectors.getSource(getState(), "foo2");
 
     if (!foo2Source || typeof foo2Source.text != "string") {
       throw new Error("bad fooSource");
     }
     expect(foo2Source.text.indexOf("return foo2")).not.toBe(-1);
   });
 
@@ -68,148 +68,148 @@ describe("loadSourceText", () => {
             sourceId: fooOrigSource.id
           })),
         getOriginalSourceText: async s => ({
           text: fooOrigContent.source,
           contentType: fooOrigContent.contentType
         })
       }
     );
-    const { dispatch, getState } = store;
+    const { cx, dispatch, getState } = store;
 
     await dispatch(actions.newSource(fooOrigSource));
     await dispatch(actions.newSource(fooGenSource));
 
     const location = {
       sourceId: fooOrigSource.id,
       line: 1,
       column: 0
     };
-    await dispatch(actions.addBreakpoint(location, {}));
+    await dispatch(actions.addBreakpoint(cx, location, {}));
 
     const breakpoint = getBreakpointsList(getState())[0];
 
     expect(breakpoint.text).toBe("");
     expect(breakpoint.originalText).toBe("");
 
-    await dispatch(actions.loadSourceText({ source: fooOrigSource }));
+    await dispatch(actions.loadSourceText({ cx, source: fooOrigSource }));
 
     const breakpoint1 = getBreakpointsList(getState())[0];
     expect(breakpoint1.text).toBe("");
     expect(breakpoint1.originalText).toBe("var fooOrig = 42;");
 
-    await dispatch(actions.loadSourceText({ source: fooGenSource }));
+    await dispatch(actions.loadSourceText({ cx, source: fooGenSource }));
 
     const breakpoint2 = getBreakpointsList(getState())[0];
     expect(breakpoint2.text).toBe("var fooGen = 42;");
     expect(breakpoint2.originalText).toBe("var fooOrig = 42;");
   });
 
   it("loads two sources w/ one request", async () => {
     let resolve;
     let count = 0;
-    const { dispatch, getState } = createStore({
+    const { dispatch, getState, cx } = createStore({
       sourceContents: () =>
         new Promise(r => {
           count++;
           resolve = r;
         }),
       getBreakpointPositions: async () => ({})
     });
     const id = "foo";
     const baseSource = makeSource(id, { loadedState: "unloaded" });
 
     await dispatch(actions.newSource(baseSource));
 
     let source = selectors.getSourceFromId(getState(), id);
-    dispatch(actions.loadSourceText({ source }));
+    dispatch(actions.loadSourceText({ cx, source }));
 
     source = selectors.getSourceFromId(getState(), id);
-    const loading = dispatch(actions.loadSourceText({ source }));
+    const loading = dispatch(actions.loadSourceText({ cx, source }));
 
     if (!resolve) {
       throw new Error("no resolve");
     }
     resolve({ source: "yay", contentType: "text/javascript" });
     await loading;
     expect(count).toEqual(1);
 
     source = selectors.getSource(getState(), id);
     expect(source && source.text).toEqual("yay");
   });
 
   it("doesn't re-load loaded sources", async () => {
     let resolve;
     let count = 0;
-    const { dispatch, getState } = createStore({
+    const { dispatch, getState, cx } = createStore({
       sourceContents: () =>
         new Promise(r => {
           count++;
           resolve = r;
         }),
       getBreakpointPositions: async () => ({})
     });
     const id = "foo";
     const baseSource = makeSource(id, { loadedState: "unloaded" });
 
     await dispatch(actions.newSource(baseSource));
     let source = selectors.getSourceFromId(getState(), id);
-    const loading = dispatch(actions.loadSourceText({ source }));
+    const loading = dispatch(actions.loadSourceText({ cx, source }));
 
     if (!resolve) {
       throw new Error("no resolve");
     }
     resolve({ source: "yay", contentType: "text/javascript" });
     await loading;
 
     source = selectors.getSourceFromId(getState(), id);
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
     expect(count).toEqual(1);
 
     source = selectors.getSource(getState(), id);
     expect(source && source.text).toEqual("yay");
   });
 
   it("should cache subsequent source text loads", async () => {
-    const { dispatch, getState } = createStore(sourceThreadClient);
+    const { dispatch, getState, cx } = createStore(sourceThreadClient);
 
     const source = makeSource("foo1");
     dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
     const prevSource = selectors.getSourceFromId(getState(), "foo1");
 
-    await dispatch(actions.loadSourceText({ source: prevSource }));
+    await dispatch(actions.loadSourceText({ cx, source: prevSource }));
     const curSource = selectors.getSource(getState(), "foo1");
 
     expect(prevSource === curSource).toBeTruthy();
   });
 
   it("should indicate a loading source", async () => {
     const store = createStore(sourceThreadClient);
-    const { dispatch } = store;
+    const { dispatch, cx } = store;
 
     const source = makeSource("foo2");
     await dispatch(actions.newSource(source));
 
     const wasLoading = watchForState(store, state => {
       const fooSource = selectors.getSource(state, "foo2");
       return fooSource && fooSource.loadedState === "loading";
     });
 
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
     expect(wasLoading()).toBe(true);
   });
 
   it("should indicate an errored source text", async () => {
-    const { dispatch, getState } = createStore(sourceThreadClient);
+    const { dispatch, getState, cx } = createStore(sourceThreadClient);
 
     const source = makeSource("bad-id");
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
     const badSource = selectors.getSource(getState(), "bad-id");
 
     if (!badSource || !badSource.error) {
       throw new Error("bad badSource");
     }
     expect(badSource.error.indexOf("unknown source")).not.toBe(-1);
   });
 });
--- a/devtools/client/debugger/new/src/actions/sources/tests/newSources.spec.js
+++ b/devtools/client/debugger/new/src/actions/sources/tests/newSources.spec.js
@@ -40,19 +40,19 @@ describe("sources - new sources", () => 
 
     await dispatch(actions.newSource(makeSource("base.js")));
     await dispatch(actions.newSource(makeSource("base.js")));
 
     expect(getSourceCount(getState())).toEqual(1);
   });
 
   it("should automatically select a pending source", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
     const baseSource = makeSource("base.js");
-    await dispatch(actions.selectSourceURL(baseSource.url));
+    await dispatch(actions.selectSourceURL(cx, baseSource.url));
 
     expect(getSelectedSource(getState())).toBe(undefined);
     await dispatch(actions.newSource(baseSource));
 
     const selected = getSelectedSource(getState());
     expect(selected && selected.url).toBe(baseSource.url);
   });
 
--- a/devtools/client/debugger/new/src/actions/sources/tests/prettyPrint.spec.js
+++ b/devtools/client/debugger/new/src/actions/sources/tests/prettyPrint.spec.js
@@ -9,37 +9,37 @@ import {
   selectors,
   createStore,
   makeSource
 } from "../../../utils/test-head";
 import { createPrettySource } from "../prettyPrint";
 import { sourceThreadClient } from "../../tests/helpers/threadClient.js";
 
 describe("sources - pretty print", () => {
-  const { dispatch, getState } = createStore(sourceThreadClient);
+  const { dispatch, getState, cx } = createStore(sourceThreadClient);
 
   it("returns a pretty source for a minified file", async () => {
     const url = "base.js";
     const source = makeSource(url);
     await dispatch(actions.newSource(source));
-    await dispatch(createPrettySource(source.id));
+    await dispatch(createPrettySource(cx, source.id));
 
     const prettyURL = `${source.url}:formatted`;
     const pretty = selectors.getSourceByURL(getState(), prettyURL);
     expect(pretty && pretty.contentType).toEqual("text/javascript");
     expect(pretty && pretty.url.includes(prettyURL)).toEqual(true);
     expect(pretty).toMatchSnapshot();
   });
 
   it("should create a source when first toggling pretty print", async () => {
     const source = makeSource("foobar.js", { loadedState: "loaded" });
-    await dispatch(actions.togglePrettyPrint(source.id));
+    await dispatch(actions.togglePrettyPrint(cx, source.id));
     expect(selectors.getSourceCount(getState())).toEqual(2);
   });
 
   it("should not make a second source when toggling pretty print", async () => {
     const source = makeSource("foobar.js", { loadedState: "loaded" });
-    await dispatch(actions.togglePrettyPrint(source.id));
+    await dispatch(actions.togglePrettyPrint(cx, source.id));
     expect(selectors.getSourceCount(getState())).toEqual(2);
-    await dispatch(actions.togglePrettyPrint(source.id));
+    await dispatch(actions.togglePrettyPrint(cx, source.id));
     expect(selectors.getSourceCount(getState())).toEqual(2);
   });
 });
--- a/devtools/client/debugger/new/src/actions/sources/tests/select.spec.js
+++ b/devtools/client/debugger/new/src/actions/sources/tests/select.spec.js
@@ -45,18 +45,19 @@ describe("sources", () => {
       actions.paused({
         thread: "FakeThread",
         why: { type: "debuggerStatement" },
         frame,
         frames: [frame]
       })
     );
 
+    const cx = selectors.getThreadContext(getState());
     await dispatch(
-      actions.selectLocation({ sourceId: "foo1", line: 1, column: 5 })
+      actions.selectLocation(cx, { sourceId: "foo1", line: 1, column: 5 })
     );
 
     const selectedSource = getSelectedSource(getState());
     if (!selectedSource) {
       throw new Error("bad selectedSource");
     }
     expect(selectedSource.id).toEqual("foo1");
 
@@ -67,93 +68,93 @@ describe("sources", () => {
     expect(source.id).toEqual("foo1");
 
     await waitForState(store, state => getOutOfScopeLocations(state));
     const locations = getOutOfScopeLocations(getState());
     expect(locations).toHaveLength(1);
   });
 
   it("should select next tab on tab closed if no previous tab", async () => {
-    const { dispatch, getState } = createStore(sourceThreadClient);
+    const { dispatch, getState, cx } = createStore(sourceThreadClient);
 
     const fooSource = makeSource("foo.js");
 
     await dispatch(actions.newSource(fooSource));
     await dispatch(actions.newSource(makeSource("bar.js")));
     await dispatch(actions.newSource(makeSource("baz.js")));
 
     // 3rd tab
-    await dispatch(actions.selectLocation(initialLocation("foo.js")));
+    await dispatch(actions.selectLocation(cx, initialLocation("foo.js")));
 
     // 2nd tab
-    await dispatch(actions.selectLocation(initialLocation("bar.js")));
+    await dispatch(actions.selectLocation(cx, initialLocation("bar.js")));
 
     // 1st tab
-    await dispatch(actions.selectLocation(initialLocation("baz.js")));
+    await dispatch(actions.selectLocation(cx, initialLocation("baz.js")));
 
     // 3rd tab is reselected
-    await dispatch(actions.selectLocation(initialLocation("foo.js")));
+    await dispatch(actions.selectLocation(cx, initialLocation("foo.js")));
 
     // closes the 1st tab, which should have no previous tab
-    await dispatch(actions.closeTab(fooSource));
+    await dispatch(actions.closeTab(cx, fooSource));
 
     const selected = getSelectedSource(getState());
     expect(selected && selected.id).toBe("bar.js");
     expect(getSourceTabs(getState())).toHaveLength(2);
   });
 
   it("should open a tab for the source", async () => {
-    const { dispatch, getState } = createStore(sourceThreadClient);
+    const { dispatch, getState, cx } = createStore(sourceThreadClient);
     await dispatch(actions.newSource(makeSource("foo.js")));
-    dispatch(actions.selectLocation(initialLocation("foo.js")));
+    dispatch(actions.selectLocation(cx, initialLocation("foo.js")));
 
     const tabs = getSourceTabs(getState());
     expect(tabs).toHaveLength(1);
     expect(tabs[0].url).toEqual("http://localhost:8000/examples/foo.js");
   });
 
   it("should select previous tab on tab closed", async () => {
-    const { dispatch, getState } = createStore(sourceThreadClient);
+    const { dispatch, getState, cx } = createStore(sourceThreadClient);
     await dispatch(actions.newSource(makeSource("foo.js")));
     await dispatch(actions.newSource(makeSource("bar.js")));
 
     const bazSource = makeSource("baz.js");
     await dispatch(actions.newSource(bazSource));
 
-    await dispatch(actions.selectLocation(initialLocation("foo.js")));
-    await dispatch(actions.selectLocation(initialLocation("bar.js")));
-    await dispatch(actions.selectLocation(initialLocation("baz.js")));
-    await dispatch(actions.closeTab(bazSource));
+    await dispatch(actions.selectLocation(cx, initialLocation("foo.js")));
+    await dispatch(actions.selectLocation(cx, initialLocation("bar.js")));
+    await dispatch(actions.selectLocation(cx, initialLocation("baz.js")));
+    await dispatch(actions.closeTab(cx, bazSource));
 
     const selected = getSelectedSource(getState());
     expect(selected && selected.id).toBe("bar.js");
     expect(getSourceTabs(getState())).toHaveLength(2);
   });
 
   it("should keep the selected source when other tab closed", async () => {
-    const { dispatch, getState } = createStore(sourceThreadClient);
+    const { dispatch, getState, cx } = createStore(sourceThreadClient);
 
     const bazSource = makeSource("baz.js");
 
     await dispatch(actions.newSource(makeSource("foo.js")));
     await dispatch(actions.newSource(makeSource("bar.js")));
     await dispatch(actions.newSource(bazSource));
 
     // 3rd tab
-    await dispatch(actions.selectLocation(initialLocation("foo.js")));
+    await dispatch(actions.selectLocation(cx, initialLocation("foo.js")));
 
     // 2nd tab
-    await dispatch(actions.selectLocation(initialLocation("bar.js")));
+    await dispatch(actions.selectLocation(cx, initialLocation("bar.js")));
 
     // 1st tab
-    await dispatch(actions.selectLocation(initialLocation("baz.js")));
+    await dispatch(actions.selectLocation(cx, initialLocation("baz.js")));
 
     // 3rd tab is reselected
-    await dispatch(actions.selectLocation(initialLocation("foo.js")));
-    await dispatch(actions.closeTab(bazSource));
+    await dispatch(actions.selectLocation(cx, initialLocation("foo.js")));
+    await dispatch(actions.closeTab(cx, bazSource));
 
     const selected = getSelectedSource(getState());
     expect(selected && selected.id).toBe("foo.js");
     expect(getSourceTabs(getState())).toHaveLength(2);
   });
 
   it("should not select new sources that lack a URL", async () => {
     const { dispatch, getState } = createStore(sourceThreadClient);
@@ -163,68 +164,68 @@ describe("sources", () => {
     await dispatch(actions.newSource(source));
 
     expect(getSourceCount(getState())).toEqual(1);
     const selectedLocation = getSelectedLocation(getState());
     expect(selectedLocation).toEqual(undefined);
   });
 
   it("sets and clears selected location correctly", () => {
-    const { dispatch, getState } = createStore(sourceThreadClient);
+    const { dispatch, getState, cx } = createStore(sourceThreadClient);
     const source = makeSource("testSource");
     const location = ({ test: "testLocation" }: any);
 
     // set value
-    dispatch(actions.setSelectedLocation(source, location));
+    dispatch(actions.setSelectedLocation(cx, source, location));
     expect(getSelectedLocation(getState())).toEqual({
       sourceId: source.id,
       ...location
     });
 
     // clear value
-    dispatch(actions.clearSelectedLocation());
+    dispatch(actions.clearSelectedLocation(cx));
     expect(getSelectedLocation(getState())).toEqual(null);
   });
 
   it("sets and clears pending selected location correctly", () => {
-    const { dispatch, getState } = createStore(sourceThreadClient);
+    const { dispatch, getState, cx } = createStore(sourceThreadClient);
     const url = "testURL";
     const options = { location: { line: "testLine" } };
 
     // set value
-    dispatch(actions.setPendingSelectedLocation(url, options));
+    dispatch(actions.setPendingSelectedLocation(cx, url, options));
     const setResult = getState().sources.pendingSelectedLocation;
     expect(setResult).toEqual({
       url,
       line: options.location.line
     });
 
     // clear value
-    dispatch(actions.clearSelectedLocation());
+    dispatch(actions.clearSelectedLocation(cx));
     const clearResult = getState().sources.pendingSelectedLocation;
     expect(clearResult).toEqual({ url: "" });
   });
 
   it("should keep the generated the viewing context", async () => {
     const store = createStore(sourceThreadClient);
-    const { dispatch, getState } = store;
+    const { dispatch, getState, cx } = store;
     const baseSource = makeSource("base.js");
     await dispatch(actions.newSource(baseSource));
 
     await dispatch(
-      actions.selectLocation({ sourceId: baseSource.id, line: 1 })
+      actions.selectLocation(cx, { sourceId: baseSource.id, line: 1 })
     );
 
     const selected = getSelectedSource(getState());
     expect(selected && selected.id).toBe(baseSource.id);
     await waitForState(store, state => getSymbols(state, baseSource));
   });
 
   it("should keep the original the viewing context", async () => {
-    const { dispatch, getState } = createStore(
+    const { dispatch, getState, cx } = createStore(
       sourceThreadClient,
       {},
       {
         getOriginalLocation: async location => ({ ...location, line: 12 }),
         getOriginalLocations: async items => items,
         getGeneratedLocation: async location => ({ ...location, line: 12 }),
         getOriginalSourceText: async () => ({ source: "" }),
         getGeneratedRangesForOriginal: async () => []
@@ -232,60 +233,62 @@ describe("sources", () => {
     );
 
     const baseSource = makeSource("base.js");
     await dispatch(actions.newSource(baseSource));
 
     const originalBaseSource = makeOriginalSource("base.js");
     await dispatch(actions.newSource(originalBaseSource));
 
-    await dispatch(actions.selectSource(originalBaseSource.id));
+    await dispatch(actions.selectSource(cx, originalBaseSource.id));
 
     const fooSource = makeSource("foo.js");
     await dispatch(actions.newSource(fooSource));
-    await dispatch(actions.selectLocation({ sourceId: fooSource.id, line: 1 }));
+    await dispatch(
+      actions.selectLocation(cx, { sourceId: fooSource.id, line: 1 })
+    );
 
     const selected = getSelectedLocation(getState());
     expect(selected && selected.line).toBe(12);
   });
 
   it("should change the original the viewing context", async () => {
-    const { dispatch, getState } = createStore(
+    const { dispatch, getState, cx } = createStore(
       sourceThreadClient,
       {},
       {
         getOriginalLocation: async location => ({ ...location, line: 12 }),
         getOriginalLocations: async items => items,
         getGeneratedRangesForOriginal: async () => [],
         getOriginalSourceText: async () => ({ source: "" })
       }
     );
 
     await dispatch(actions.newSource(makeSource("base.js")));
     const baseSource = makeOriginalSource("base.js");
 
     await dispatch(actions.newSource(baseSource));
-    await dispatch(actions.selectSource(baseSource.id));
+    await dispatch(actions.selectSource(cx, baseSource.id));
 
     await dispatch(
-      actions.selectSpecificLocation({
+      actions.selectSpecificLocation(cx, {
         sourceId: baseSource.id,
         line: 1
       })
     );
 
     const selected = getSelectedLocation(getState());
     expect(selected && selected.line).toBe(1);
   });
 
   describe("selectSourceURL", () => {
     it("should automatically select a pending source", async () => {
-      const { dispatch, getState } = createStore(sourceThreadClient);
+      const { dispatch, getState, cx } = createStore(sourceThreadClient);
       const baseSource = makeSource("base.js");
-      await dispatch(actions.selectSourceURL(baseSource.url));
+      await dispatch(actions.selectSourceURL(cx, baseSource.url));
 
       expect(getSelectedSource(getState())).toBe(undefined);
       await dispatch(actions.newSource(baseSource));
 
       const selected = getSelectedSource(getState());
       expect(selected && selected.url).toBe(baseSource.url);
     });
   });
--- a/devtools/client/debugger/new/src/actions/tabs.js
+++ b/devtools/client/debugger/new/src/actions/tabs.js
@@ -18,17 +18,17 @@ import {
   getSourceTabs,
   getSourceByURL,
   getNewSelectedSourceId,
   removeSourceFromTabList,
   removeSourcesFromTabList
 } from "../selectors";
 
 import type { Action, ThunkArgs } from "./types";
-import type { Source } from "../types";
+import type { Source, Context } from "../types";
 
 export function updateTab(source: Source, framework: string): Action {
   const { url, id: sourceId } = source;
   const isOriginal = isOriginalId(source.id);
 
   return {
     type: "UPDATE_TAB",
     url,
@@ -57,39 +57,39 @@ export function moveTab(url: string, tab
     tabIndex
   };
 }
 
 /**
  * @memberof actions/tabs
  * @static
  */
-export function closeTab(source: Source) {
+export function closeTab(cx: Context, source: Source) {
   return ({ dispatch, getState, client }: ThunkArgs) => {
     const { id, url } = source;
 
     removeDocument(id);
 
     const tabs = removeSourceFromTabList(getSourceTabs(getState()), source);
     const sourceId = getNewSelectedSourceId(getState(), tabs);
     dispatch(({ type: "CLOSE_TAB", url, tabs }: Action));
-    dispatch(selectSource(sourceId));
+    dispatch(selectSource(cx, sourceId));
   };
 }
 
 /**
  * @memberof actions/tabs
  * @static
  */
-export function closeTabs(urls: string[]) {
+export function closeTabs(cx: Context, urls: string[]) {
   return ({ dispatch, getState, client }: ThunkArgs) => {
     const sources = urls
       .map(url => getSourceByURL(getState(), url))
       .filter(Boolean);
     sources.map(source => removeDocument(source.id));
 
     const tabs = removeSourcesFromTabList(getSourceTabs(getState()), sources);
     dispatch(({ type: "CLOSE_TABS", sources, tabs }: Action));
 
     const sourceId = getNewSelectedSourceId(getState(), tabs);
-    dispatch(selectSource(sourceId));
+    dispatch(selectSource(cx, sourceId));
   };
 }
--- a/devtools/client/debugger/new/src/actions/tests/ast.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/ast.spec.js
@@ -61,23 +61,23 @@ const evaluationResult = {
   this: { actor: "this", preview: {} }
 };
 
 describe("ast", () => {
   describe("setSymbols", () => {
     describe("when the source is loaded", () => {
       it("should be able to set symbols", async () => {
         const store = createStore(threadClient);
-        const { dispatch, getState } = store;
+        const { dispatch, getState, cx } = store;
         const base = makeSource("base.js");
         await dispatch(actions.newSource(base));
-        await dispatch(actions.loadSourceText({ source: base }));
+        await dispatch(actions.loadSourceText({ cx, source: base }));
 
         const loadedSource = selectors.getSourceFromId(getState(), base.id);
-        await dispatch(actions.setSymbols({ source: loadedSource }));
+        await dispatch(actions.setSymbols({ cx, source: loadedSource }));
         await waitForState(store, state => !isSymbolsLoading(state, base));
 
         const baseSymbols = getSymbols(getState(), base);
         expect(baseSymbols).toMatchSnapshot();
       });
     });
 
     describe("when the source is not loaded", () => {
@@ -97,84 +97,91 @@ describe("ast", () => {
         const baseSymbols = getSymbols(getState());
         expect(baseSymbols).toEqual(null);
       });
     });
 
     describe("frameworks", () => {
       it("should detect react components", async () => {
         const store = createStore(threadClient, {}, sourceMaps);
-        const { dispatch, getState } = store;
+        const { cx, dispatch, getState } = store;
         const source = makeOriginalSource("reactComponent.js");
 
         await dispatch(actions.newSource(makeSource("reactComponent.js")));
 
         await dispatch(actions.newSource(source));
 
-        await dispatch(actions.loadSourceText({ source }));
+        await dispatch(actions.loadSourceText({ cx, source }));
         const loadedSource = selectors.getSourceFromId(getState(), source.id);
-        await dispatch(actions.setSymbols({ source: loadedSource }));
+        await dispatch(actions.setSymbols({ cx, source: loadedSource }));
 
         expect(getFramework(getState(), source)).toBe("React");
       });
 
       it("should not give false positive on non react components", async () => {
         const store = createStore(threadClient);
-        const { dispatch, getState } = store;
+        const { cx, dispatch, getState } = store;
         const base = makeSource("base.js");
         await dispatch(actions.newSource(base));
-        await dispatch(actions.loadSourceText({ source: base }));
-        await dispatch(actions.setSymbols({ source: base }));
+        await dispatch(actions.loadSourceText({ cx, source: base }));
+        await dispatch(actions.setSymbols({ cx, source: base }));
 
         expect(getFramework(getState(), base)).toBe(undefined);
       });
     });
   });
 
   describe("getOutOfScopeLocations", () => {
     beforeEach(async () => {
       prefs.autoPrettyPrint = false;
     });
 
     it("with selected line", async () => {
       const store = createStore(threadClient);
-      const { dispatch, getState } = store;
+      const { dispatch, getState, cx } = store;
       const source = makeSource("scopes.js");
       await dispatch(actions.newSource(source));
 
       await dispatch(
-        actions.selectLocation({ sourceId: "scopes.js", line: 5 })
+        actions.selectLocation(cx, { sourceId: "scopes.js", line: 5 })
       );
 
+      // Make sure the state has finished updating before pausing.
+      await waitForState(store, state => {
+        const symbols = getSymbols(state, source);
+        return symbols && !symbols.loading && getOutOfScopeLocations(state);
+      });
+
       const frame = makeFrame({ id: "1", sourceId: "scopes.js" });
       await dispatch(
         actions.paused({
           thread: "FakeThread",
           why: { type: "debuggerStatement" },
           frame,
           frames: [frame]
         })
       );
 
-      await dispatch(actions.setOutOfScopeLocations());
+      const ncx = selectors.getThreadContext(getState());
+      await dispatch(actions.setOutOfScopeLocations(ncx));
 
       await waitForState(store, state => getOutOfScopeLocations(state));
 
       const locations = getOutOfScopeLocations(getState());
       const lines = getInScopeLines(getState());
 
       expect(locations).toMatchSnapshot();
       expect(lines).toMatchSnapshot();
     });
 
     it("without a selected line", async () => {
-      const { dispatch, getState } = createStore(threadClient);
+      const { dispatch, getState, cx } = createStore(threadClient);
       const base = makeSource("base.js");
       await dispatch(actions.newSource(base));
-      await dispatch(actions.selectSource("base.js"));
+      await dispatch(actions.selectSource(cx, "base.js"));
 
       const locations = getOutOfScopeLocations(getState());
       // const lines = getInScopeLines(getState());
 
       expect(locations).toEqual(null);
 
       // This check is disabled as locations that are in/out of scope may not
       // have completed yet when the selectSource promise finishes.
--- a/devtools/client/debugger/new/src/actions/tests/expressions.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/expressions.spec.js
@@ -46,114 +46,117 @@ const mockThreadClient = {
         matchProp: "to"
       });
     });
   }
 };
 
 describe("expressions", () => {
   it("should add an expression", async () => {
-    const { dispatch, getState } = createStore(mockThreadClient);
+    const { dispatch, getState, cx } = createStore(mockThreadClient);
 
-    await dispatch(actions.addExpression("foo"));
+    await dispatch(actions.addExpression(cx, "foo"));
     expect(selectors.getExpressions(getState()).size).toBe(1);
   });
 
   it("should not add empty expressions", () => {
-    const { dispatch, getState } = createStore(mockThreadClient);
+    const { dispatch, getState, cx } = createStore(mockThreadClient);
 
-    dispatch(actions.addExpression((undefined: any)));
-    dispatch(actions.addExpression(""));
+    dispatch(actions.addExpression(cx, (undefined: any)));
+    dispatch(actions.addExpression(cx, ""));
     expect(selectors.getExpressions(getState()).size).toBe(0);
   });
 
   it("should not add invalid expressions", async () => {
-    const { dispatch, getState } = createStore(mockThreadClient);
-    await dispatch(actions.addExpression("foo#"));
+    const { dispatch, getState, cx } = createStore(mockThreadClient);
+    await dispatch(actions.addExpression(cx, "foo#"));
     const state = getState();
     expect(selectors.getExpressions(state).size).toBe(0);
     expect(selectors.getExpressionError(state)).toBe(true);
   });
 
   it("should update an expression", async () => {
-    const { dispatch, getState } = createStore(mockThreadClient);
+    const { dispatch, getState, cx } = createStore(mockThreadClient);
 
-    await dispatch(actions.addExpression("foo"));
+    await dispatch(actions.addExpression(cx, "foo"));
     const expression = selectors.getExpression(getState(), "foo");
-    await dispatch(actions.updateExpression("bar", expression));
+    await dispatch(actions.updateExpression(cx, "bar", expression));
 
     expect(selectors.getExpression(getState(), "bar").input).toBe("bar");
   });
 
   it("should not update an expression w/ invalid code", async () => {
-    const { dispatch, getState } = createStore(mockThreadClient);
+    const { dispatch, getState, cx } = createStore(mockThreadClient);
 
-    await dispatch(actions.addExpression("foo"));
+    await dispatch(actions.addExpression(cx, "foo"));
     const expression = selectors.getExpression(getState(), "foo");
-    await dispatch(actions.updateExpression("#bar", expression));
+    await dispatch(actions.updateExpression(cx, "#bar", expression));
     expect(selectors.getExpression(getState(), "bar")).toBeUndefined();
   });
 
   it("should delete an expression", async () => {
-    const { dispatch, getState } = createStore(mockThreadClient);
+    const { dispatch, getState, cx } = createStore(mockThreadClient);
 
-    await dispatch(actions.addExpression("foo"));
-    await dispatch(actions.addExpression("bar"));
+    await dispatch(actions.addExpression(cx, "foo"));
+    await dispatch(actions.addExpression(cx, "bar"));
     expect(selectors.getExpressions(getState()).size).toBe(2);
 
     const expression = selectors.getExpression(getState(), "foo");
     dispatch(actions.deleteExpression(expression));
     expect(selectors.getExpressions(getState()).size).toBe(1);
     expect(selectors.getExpression(getState(), "bar").input).toBe("bar");
   });
 
   it("should evaluate expressions global scope", async () => {
-    const { dispatch, getState } = createStore(mockThreadClient);
-
-    await dispatch(actions.addExpression("foo"));
-    await dispatch(actions.addExpression("bar"));
+    const { dispatch, getState, cx } = createStore(mockThreadClient);
+    await dispatch(actions.addExpression(cx, "foo"));
+    await dispatch(actions.addExpression(cx, "bar"));
     expect(selectors.getExpression(getState(), "foo").value).toBe("bla");
     expect(selectors.getExpression(getState(), "bar").value).toBe("bla");
 
-    await dispatch(actions.evaluateExpressions());
+    await dispatch(actions.evaluateExpressions(cx));
     expect(selectors.getExpression(getState(), "foo").value).toBe("bla");
     expect(selectors.getExpression(getState(), "bar").value).toBe("bla");
   });
 
   it("should evaluate expressions in specific scope", async () => {
     const { dispatch, getState } = createStore(mockThreadClient);
-    await createFrames(dispatch);
+    await createFrames(getState, dispatch);
+
+    const cx = selectors.getThreadContext(getState());
     await dispatch(actions.newSource(makeSource("source")));
-    await dispatch(actions.addExpression("foo"));
-    await dispatch(actions.addExpression("bar"));
+    await dispatch(actions.addExpression(cx, "foo"));
+    await dispatch(actions.addExpression(cx, "bar"));
 
     expect(selectors.getExpression(getState(), "foo").value).toBe("boo");
     expect(selectors.getExpression(getState(), "bar").value).toBe("boo");
 
-    await dispatch(actions.evaluateExpressions());
+    await dispatch(actions.evaluateExpressions(cx));
 
     expect(selectors.getExpression(getState(), "foo").value).toBe("boo");
     expect(selectors.getExpression(getState(), "bar").value).toBe("boo");
   });
 
   it("should get the autocomplete matches for the input", async () => {
-    const { dispatch, getState } = createStore(mockThreadClient);
-    await dispatch(actions.autocomplete("to", 2));
+    const { cx, dispatch, getState } = createStore(mockThreadClient);
+    await dispatch(actions.autocomplete(cx, "to", 2));
     expect(selectors.getAutocompleteMatchset(getState())).toMatchSnapshot();
   });
 });
 
-async function createFrames(dispatch) {
+async function createFrames(getState, dispatch) {
   const frame = makeMockFrame();
   await dispatch(actions.newSource(makeSource("example.js")));
   await dispatch(actions.newSource(makeSource("source")));
 
   await dispatch(
     actions.paused({
-      thread: "UnknownThread",
+      thread: "FakeThread",
       frame,
       frames: [frame],
       why: { type: "just because" }
     })
   );
 
-  await dispatch(actions.selectFrame(frame));
+  await dispatch(
+    actions.selectFrame(selectors.getThreadContext(getState()), frame)
+  );
 }
--- a/devtools/client/debugger/new/src/actions/tests/file-search.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/file-search.spec.js
@@ -9,55 +9,55 @@ import { createStore, selectors, actions
 const {
   getFileSearchQuery,
   getFileSearchModifiers,
   getFileSearchResults
 } = selectors;
 
 describe("file text search", () => {
   it("should update search results", () => {
-    const { dispatch, getState } = createStore();
+    const { dispatch, getState, cx } = createStore();
     expect(getFileSearchResults(getState())).toEqual({
       matches: [],
       matchIndex: -1,
       index: -1,
       count: 0
     });
 
     const matches = [{ line: 1, ch: 3 }, { line: 3, ch: 2 }];
-    dispatch(actions.updateSearchResults(2, 3, matches));
+    dispatch(actions.updateSearchResults(cx, 2, 3, matches));
 
     expect(getFileSearchResults(getState())).toEqual({
       count: 2,
       index: 2,
       matchIndex: 1,
       matches: matches
     });
   });
 
   it("should update the file search query", () => {
-    const { dispatch, getState } = createStore();
+    const { dispatch, getState, cx } = createStore();
     let fileSearchQueryState = getFileSearchQuery(getState());
     expect(fileSearchQueryState).toBe("");
-    dispatch(actions.setFileSearchQuery("foobar"));
+    dispatch(actions.setFileSearchQuery(cx, "foobar"));
     fileSearchQueryState = getFileSearchQuery(getState());
     expect(fileSearchQueryState).toBe("foobar");
   });
 
   it("should toggle a file search modifier", () => {
-    const { dispatch, getState } = createStore();
+    const { dispatch, getState, cx } = createStore();
     let fileSearchModState = getFileSearchModifiers(getState());
     expect(fileSearchModState.get("caseSensitive")).toBe(false);
-    dispatch(actions.toggleFileSearchModifier("caseSensitive"));
+    dispatch(actions.toggleFileSearchModifier(cx, "caseSensitive"));
     fileSearchModState = getFileSearchModifiers(getState());
     expect(fileSearchModState.get("caseSensitive")).toBe(true);
   });
 
   it("should toggle a file search query cleaning", () => {
-    const { dispatch, getState } = createStore();
-    dispatch(actions.setFileSearchQuery("foobar"));
+    const { dispatch, getState, cx } = createStore();
+    dispatch(actions.setFileSearchQuery(cx, "foobar"));
     let fileSearchQueryState = getFileSearchQuery(getState());
     expect(fileSearchQueryState).toBe("foobar");
-    dispatch(actions.setFileSearchQuery(""));
+    dispatch(actions.setFileSearchQuery(cx, ""));
     fileSearchQueryState = getFileSearchQuery(getState());
     expect(fileSearchQueryState).toBe("");
   });
 });
--- a/devtools/client/debugger/new/src/actions/tests/navigation.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/navigation.spec.js
@@ -43,21 +43,21 @@ describe("navigation", () => {
         "actor",
         false
       )
     );
     expect(selectors.getDebuggeeUrl(getState())).toEqual("http://test.com/foo");
   });
 
   it("navigation closes project-search", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
     const mockQuery = "foo";
 
     await dispatch(actions.newSource(makeSource("foo1")));
-    await dispatch(actions.searchSources(mockQuery));
+    await dispatch(actions.searchSources(cx, mockQuery));
 
     let results = getTextSearchResults(getState());
     expect(results).toHaveLength(1);
     expect(selectors.getTextSearchQuery(getState())).toEqual("foo");
     expect(getTextSearchStatus(getState())).toEqual("DONE");
 
     await dispatch(actions.willNavigate("will-navigate"));
 
@@ -72,31 +72,31 @@ describe("navigation", () => {
     dispatch(actions.setActiveSearch("project"));
     expect(getActiveSearch(getState())).toBe("project");
 
     await dispatch(actions.willNavigate("will-navigate"));
     expect(getActiveSearch(getState())).toBe(null);
   });
 
   it("navigation clears the file-search query", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
 
-    dispatch(actions.setFileSearchQuery("foobar"));
+    dispatch(actions.setFileSearchQuery(cx, "foobar"));
     expect(getFileSearchQuery(getState())).toBe("foobar");
 
     await dispatch(actions.willNavigate("will-navigate"));
 
     expect(getFileSearchQuery(getState())).toBe("");
   });
 
   it("navigation clears the file-search results", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
 
     const searchResults = [{ line: 1, ch: 3 }, { line: 3, ch: 2 }];
-    dispatch(actions.updateSearchResults(2, 3, searchResults));
+    dispatch(actions.updateSearchResults(cx, 2, 3, searchResults));
     expect(getFileSearchResults(getState())).toEqual({
       count: 2,
       index: 2,
       matchIndex: 1,
       matches: searchResults
     });
 
     await dispatch(actions.willNavigate("will-navigate"));
--- a/devtools/client/debugger/new/src/actions/tests/pending-breakpoints.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/pending-breakpoints.spec.js
@@ -64,31 +64,31 @@ function mockSourceMaps() {
       { start: { line: 0, column: 0 }, end: { line: 10, column: 10 } }
     ],
     getOriginalLocations: async items => items
   };
 }
 
 describe("when adding breakpoints", () => {
   it("a corresponding pending breakpoint should be added", async () => {
-    const { dispatch, getState } = createStore(
+    const { dispatch, getState, cx } = createStore(
       mockClient({ "5": [1] }),
       loadInitialState(),
       mockSourceMaps()
     );
 
     const source = makeSource("foo.js");
     await dispatch(actions.newSource(source));
     await dispatch(actions.newSource(makeSource("foo.js")));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
     const bp = generateBreakpoint("foo.js", 5, 1);
     const id = makePendingLocationId(bp.location);
 
-    await dispatch(actions.addBreakpoint(bp.location));
+    await dispatch(actions.addBreakpoint(cx, bp.location));
     const pendingBps = selectors.getPendingBreakpoints(getState());
 
     expect(selectors.getPendingBreakpointList(getState())).toHaveLength(2);
     expect(pendingBps[id]).toMatchSnapshot();
   });
 
   describe("adding and deleting breakpoints", () => {
     let breakpoint1;
@@ -99,160 +99,160 @@ describe("when adding breakpoints", () =
     beforeEach(() => {
       breakpoint1 = generateBreakpoint("foo");
       breakpoint2 = generateBreakpoint("foo2");
       breakpointLocationId1 = makePendingLocationId(breakpoint1.location);
       breakpointLocationId2 = makePendingLocationId(breakpoint2.location);
     });
 
     it("add a corresponding pendingBreakpoint for each addition", async () => {
-      const { dispatch, getState } = createStore(
+      const { dispatch, getState, cx } = createStore(
         mockClient({ "5": [0] }),
         loadInitialState(),
         mockSourceMaps()
       );
 
       const source1 = makeSource("foo");
       const source2 = makeSource("foo2");
 
       await dispatch(actions.newSource(makeSource("foo")));
       await dispatch(actions.newSource(makeSource("foo2")));
 
       await dispatch(actions.newSource(source1));
       await dispatch(actions.newSource(source2));
 
-      await dispatch(actions.loadSourceText({ source: source1 }));
-      await dispatch(actions.loadSourceText({ source: source2 }));
+      await dispatch(actions.loadSourceText({ cx, source: source1 }));
+      await dispatch(actions.loadSourceText({ cx, source: source2 }));
 
-      await dispatch(actions.addBreakpoint(breakpoint1.location));
-      await dispatch(actions.addBreakpoint(breakpoint2.location));
+      await dispatch(actions.addBreakpoint(cx, breakpoint1.location));
+      await dispatch(actions.addBreakpoint(cx, breakpoint2.location));
 
       const pendingBps = selectors.getPendingBreakpoints(getState());
 
       // NOTE the sourceId should be `foo2/originalSource`, but is `foo2`
       // because we do not have a real source map for `getOriginalLocation`
       // to map.
       expect(pendingBps[breakpointLocationId1]).toMatchSnapshot();
       expect(pendingBps[breakpointLocationId2]).toMatchSnapshot();
     });
 
     it("hidden breakponts do not create pending bps", async () => {
-      const { dispatch, getState } = createStore(
+      const { dispatch, getState, cx } = createStore(
         mockClient({ "5": [0] }),
         loadInitialState(),
         mockSourceMaps()
       );
 
       const source = makeSource("foo");
       await dispatch(actions.newSource(makeSource("foo")));
       await dispatch(actions.newSource(source));
-      await dispatch(actions.loadSourceText({ source }));
+      await dispatch(actions.loadSourceText({ cx, source }));
 
       await dispatch(
-        actions.addBreakpoint(breakpoint1.location, { hidden: true })
+        actions.addBreakpoint(cx, breakpoint1.location, { hidden: true })
       );
       const pendingBps = selectors.getPendingBreakpoints(getState());
 
       expect(pendingBps[breakpointLocationId1]).toBeUndefined();
     });
 
     it("remove a corresponding pending breakpoint when deleting", async () => {
-      const { dispatch, getState } = createStore(
+      const { dispatch, getState, cx } = createStore(
         mockClient({ "5": [0] }),
         loadInitialState(),
         mockSourceMaps()
       );
 
       await dispatch(actions.newSource(makeSource("foo")));
       await dispatch(actions.newSource(makeSource("foo2")));
 
       const source1 = makeSource("foo");
       const source2 = makeSource("foo2");
 
       await dispatch(actions.newSource(source1));
       await dispatch(actions.newSource(source2));
 
-      await dispatch(actions.loadSourceText({ source: source1 }));
-      await dispatch(actions.loadSourceText({ source: source2 }));
+      await dispatch(actions.loadSourceText({ cx, source: source1 }));
+      await dispatch(actions.loadSourceText({ cx, source: source2 }));
 
-      await dispatch(actions.addBreakpoint(breakpoint1.location));
-      await dispatch(actions.addBreakpoint(breakpoint2.location));
-      await dispatch(actions.removeBreakpoint(breakpoint1));
+      await dispatch(actions.addBreakpoint(cx, breakpoint1.location));
+      await dispatch(actions.addBreakpoint(cx, breakpoint2.location));
+      await dispatch(actions.removeBreakpoint(cx, breakpoint1));
 
       const pendingBps = selectors.getPendingBreakpoints(getState());
       expect(pendingBps.hasOwnProperty(breakpointLocationId1)).toBe(false);
       expect(pendingBps.hasOwnProperty(breakpointLocationId2)).toBe(true);
     });
   });
 });
 
 describe("when changing an existing breakpoint", () => {
   it("updates corresponding pendingBreakpoint", async () => {
-    const { dispatch, getState } = createStore(
+    const { dispatch, getState, cx } = createStore(
       mockClient({ "5": [0] }),
       loadInitialState(),
       mockSourceMaps()
     );
     const bp = generateBreakpoint("foo");
     const id = makePendingLocationId(bp.location);
 
     const source = makeSource("foo");
     await dispatch(actions.newSource(source));
     await dispatch(actions.newSource(makeSource("foo")));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
-    await dispatch(actions.addBreakpoint(bp.location));
+    await dispatch(actions.addBreakpoint(cx, bp.location));
     await dispatch(
-      actions.setBreakpointOptions(bp.location, { condition: "2" })
+      actions.setBreakpointOptions(cx, bp.location, { condition: "2" })
     );
     const bps = selectors.getPendingBreakpoints(getState());
     const breakpoint = bps[id];
     expect(breakpoint.options.condition).toBe("2");
   });
 
   it("if disabled, updates corresponding pendingBreakpoint", async () => {
-    const { dispatch, getState } = createStore(
+    const { dispatch, getState, cx } = createStore(
       mockClient({ "5": [0] }),
       loadInitialState(),
       mockSourceMaps()
     );
     const bp = generateBreakpoint("foo");
     const id = makePendingLocationId(bp.location);
 
     await dispatch(actions.newSource(makeSource("foo")));
 
     const source = makeSource("foo");
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
-    await dispatch(actions.addBreakpoint(bp.location));
-    await dispatch(actions.disableBreakpoint(bp));
+    await dispatch(actions.addBreakpoint(cx, bp.location));
+    await dispatch(actions.disableBreakpoint(cx, bp));
     const bps = selectors.getPendingBreakpoints(getState());
     const breakpoint = bps[id];
     expect(breakpoint.disabled).toBe(true);
   });
 
   it("does not delete the pre-existing pendingBreakpoint", async () => {
-    const { dispatch, getState } = createStore(
+    const { dispatch, getState, cx } = createStore(
       mockClient({ "5": [0] }),
       loadInitialState(),
       mockSourceMaps()
     );
     const bp = generateBreakpoint("foo.js");
 
     const source = makeSource("foo.js");
     await dispatch(actions.newSource(source));
     await dispatch(actions.newSource(makeSource("foo.js")));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
     const id = makePendingLocationId(bp.location);
 
-    await dispatch(actions.addBreakpoint(bp.location));
+    await dispatch(actions.addBreakpoint(cx, bp.location));
     await dispatch(
-      actions.setBreakpointOptions(bp.location, { condition: "2" })
+      actions.setBreakpointOptions(cx, bp.location, { condition: "2" })
     );
     const bps = selectors.getPendingBreakpoints(getState());
     const breakpoint = bps[id];
     expect(breakpoint.options.condition).toBe("2");
   });
 });
 
 describe("initializing when pending breakpoints exist in prefs", () => {
@@ -262,68 +262,68 @@ describe("initializing when pending brea
       loadInitialState(),
       mockSourceMaps()
     );
     const bps = selectors.getPendingBreakpoints(getState());
     expect(bps).toMatchSnapshot();
   });
 
   it("re-adding breakpoints update existing pending breakpoints", async () => {
-    const { dispatch, getState } = createStore(
+    const { dispatch, getState, cx } = createStore(
       mockClient({ "5": [1, 2] }),
       loadInitialState(),
       mockSourceMaps()
     );
     const bar = generateBreakpoint("bar.js", 5, 1);
 
     await dispatch(actions.newSource(makeSource("bar.js")));
 
     const source = makeSource("bar.js");
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
-    await dispatch(actions.addBreakpoint(bar.location));
+    await dispatch(actions.loadSourceText({ cx, source }));
+    await dispatch(actions.addBreakpoint(cx, bar.location));
 
     const bps = selectors.getPendingBreakpointList(getState());
     expect(bps).toHaveLength(2);
   });
 
   it("adding bps doesn't remove existing pending breakpoints", async () => {
-    const { dispatch, getState } = createStore(
+    const { dispatch, getState, cx } = createStore(
       mockClient({ "5": [0] }),
       loadInitialState(),
       mockSourceMaps()
     );
     const bp = generateBreakpoint("foo.js");
 
     const source = makeSource("foo.js");
     await dispatch(actions.newSource(source));
     await dispatch(actions.newSource(makeSource("foo.js")));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
-    await dispatch(actions.addBreakpoint(bp.location));
+    await dispatch(actions.addBreakpoint(cx, bp.location));
 
     const bps = selectors.getPendingBreakpointList(getState());
     expect(bps).toHaveLength(2);
   });
 });
 
 describe("initializing with disabled pending breakpoints in prefs", () => {
   it("syncs breakpoints with pending breakpoints", async () => {
     const store = createStore(
       mockClient({ "5": [2] }),
       loadInitialState({ disabled: true }),
       mockSourceMaps()
     );
 
-    const { getState, dispatch } = store;
+    const { getState, dispatch, cx } = store;
     const source = makeSource("bar.js");
 
     await dispatch(actions.newSource(makeSource("bar.js")));
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
     await waitForState(store, state => {
       const bps = selectors.getBreakpointsForSource(state, source.id);
       return bps && Object.values(bps).length > 0;
     });
 
     const bp = selectors.getBreakpointForLocation(getState(), {
       line: 5,
@@ -341,25 +341,25 @@ describe("initializing with disabled pen
 
 describe("adding sources", () => {
   it("corresponding breakpoints are added for a single source", async () => {
     const store = createStore(
       mockClient({ "5": [2] }),
       loadInitialState({ disabled: true }),
       mockSourceMaps()
     );
-    const { getState, dispatch } = store;
+    const { getState, dispatch, cx } = store;
 
     expect(selectors.getBreakpointCount(getState())).toEqual(0);
 
     const source = makeSource("bar.js");
 
     await dispatch(actions.newSource(makeSource("bar.js")));
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
     await waitForState(store, state => selectors.getBreakpointCount(state) > 0);
 
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
   });
 
   it("corresponding breakpoints are added to the original source", async () => {
     const source = makeSource("bar.js", { sourceMapURL: "foo" });
@@ -391,24 +391,24 @@ describe("adding sources", () => {
   });
 
   it("add corresponding breakpoints for multiple sources", async () => {
     const store = createStore(
       mockClient({ "5": [2] }),
       loadInitialState({ disabled: true }),
       mockSourceMaps()
     );
-    const { getState, dispatch } = store;
+    const { getState, dispatch, cx } = store;
 
     expect(selectors.getBreakpointCount(getState())).toEqual(0);
 
     const source1 = makeSource("bar.js");
     const source2 = makeSource("foo.js");
     await dispatch(actions.newSource(makeSource("bar.js")));
     await dispatch(actions.newSource(makeSource("foo.js")));
     await dispatch(actions.newSources([source1, source2]));
-    await dispatch(actions.loadSourceText({ source: source1 }));
-    await dispatch(actions.loadSourceText({ source: source2 }));
+    await dispatch(actions.loadSourceText({ cx, source: source1 }));
+    await dispatch(actions.loadSourceText({ cx, source: source2 }));
 
     await waitForState(store, state => selectors.getBreakpointCount(state) > 0);
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
   });
 });
--- a/devtools/client/debugger/new/src/actions/tests/project-text-search.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/project-text-search.spec.js
@@ -39,34 +39,34 @@ const sources = {
 
 const threadClient = {
   sourceContents: async ({ source }) => sources[source],
   getBreakpointPositions: async () => ({})
 };
 
 describe("project text search", () => {
   it("should add a project text search query", () => {
-    const { dispatch, getState } = createStore();
+    const { dispatch, getState, cx } = createStore();
     const mockQuery = "foo";
 
-    dispatch(actions.addSearchQuery(mockQuery));
+    dispatch(actions.addSearchQuery(cx, mockQuery));
 
     expect(getTextSearchQuery(getState())).toEqual(mockQuery);
   });
 
   it("should search all the loaded sources based on the query", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
     const mockQuery = "foo";
     const source1 = makeSource("foo1");
     const source2 = makeSource("foo2");
 
     await dispatch(actions.newSource(source1));
     await dispatch(actions.newSource(source2));
 
-    await dispatch(actions.searchSources(mockQuery));
+    await dispatch(actions.searchSources(cx, mockQuery));
 
     const results = getTextSearchResults(getState());
     expect(results).toMatchSnapshot();
   });
 
   it("should ignore sources with minified versions", async () => {
     const source1 = makeSource("bar", {
       sourceMapURL: "bar:formatted",
@@ -82,82 +82,82 @@ describe("project text search", () => {
         contentType: "text/javascript"
       }),
       applySourceMap: async () => {},
       getOriginalURLs: async () => [source2.url],
       getGeneratedRangesForOriginal: async () => [],
       getOriginalLocations: async items => items
     };
 
-    const { dispatch, getState } = createStore(threadClient, {}, mockMaps);
+    const { dispatch, getState, cx } = createStore(threadClient, {}, mockMaps);
     const mockQuery = "bla";
 
     await dispatch(actions.newSource(source1));
     await dispatch(actions.newSource(source2));
 
-    await dispatch(actions.searchSources(mockQuery));
+    await dispatch(actions.searchSources(cx, mockQuery));
 
     const results = getTextSearchResults(getState());
     expect(results).toMatchSnapshot();
   });
 
   it("should search a specific source", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
 
     const source = makeSource("bar");
     await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText({ source }));
+    await dispatch(actions.loadSourceText({ cx, source }));
 
-    dispatch(actions.addSearchQuery("bla"));
+    dispatch(actions.addSearchQuery(cx, "bla"));
 
     const barSource = getSource(getState(), "bar");
     if (!barSource) {
       throw new Error("no barSource");
     }
     const sourceId = barSource.id;
 
-    await dispatch(actions.searchSource(sourceId, "bla"), "bla");
+    await dispatch(actions.searchSource(cx, sourceId, "bla"), "bla");
 
     const results = getTextSearchResults(getState());
 
     expect(results).toMatchSnapshot();
     expect(results).toHaveLength(1);
   });
 
   it("should clear all the search results", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
     const mockQuery = "foo";
 
     await dispatch(actions.newSource(makeSource("foo1")));
-    await dispatch(actions.searchSources(mockQuery));
+    await dispatch(actions.searchSources(cx, mockQuery));
 
     expect(getTextSearchResults(getState())).toMatchSnapshot();
 
-    await dispatch(actions.clearSearchResults());
+    await dispatch(actions.clearSearchResults(cx));
 
     expect(getTextSearchResults(getState())).toMatchSnapshot();
   });
 
   it("should set the status properly", () => {
-    const { dispatch, getState } = createStore();
+    const { dispatch, getState, cx } = createStore();
     const mockStatus = "Fetching";
-    dispatch(actions.updateSearchStatus(mockStatus));
+    dispatch(actions.updateSearchStatus(cx, mockStatus));
     expect(getTextSearchStatus(getState())).toEqual(mockStatus);
   });
 
   it("should close project search", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
     const mockQuery = "foo";
 
     await dispatch(actions.newSource(makeSource("foo1")));
-    await dispatch(actions.searchSources(mockQuery));
+    await dispatch(actions.searchSources(cx, mockQuery));
 
     expect(getTextSearchResults(getState())).toMatchSnapshot();
 
-    dispatch(actions.closeProjectSearch());
+    dispatch(actions.closeProjectSearch(cx));
 
     expect(getTextSearchQuery(getState())).toEqual("");
 
     const results = getTextSearchResults(getState());
 
     expect(results).toMatchSnapshot();
     expect(results).toHaveLength(0);
     const status = getTextSearchStatus(getState());
--- a/devtools/client/debugger/new/src/actions/tests/setProjectDirectoryRoot.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/setProjectDirectoryRoot.spec.js
@@ -12,67 +12,67 @@ import {
 } from "../../utils/test-head";
 
 import type { Source } from "../../types";
 
 const { getProjectDirectoryRoot, getDisplayedSources } = selectors;
 
 describe("setProjectDirectoryRoot", () => {
   it("should set domain directory as root", async () => {
-    const { dispatch, getState } = createStore();
-    dispatch(actions.setProjectDirectoryRoot("example.com"));
+    const { dispatch, getState, cx } = createStore();
+    dispatch(actions.setProjectDirectoryRoot(cx, "example.com"));
     expect(getProjectDirectoryRoot(getState())).toBe("example.com");
   });
 
   it("should set a directory as root directory", async () => {
-    const { dispatch, getState } = createStore();
-    dispatch(actions.setProjectDirectoryRoot("/example.com/foo"));
+    const { dispatch, getState, cx } = createStore();
+    dispatch(actions.setProjectDirectoryRoot(cx, "/example.com/foo"));
     expect(getProjectDirectoryRoot(getState())).toBe("/example.com/foo");
   });
 
   it("should add to the directory ", () => {
-    const { dispatch, getState } = createStore();
-    dispatch(actions.setProjectDirectoryRoot("/example.com/foo"));
-    dispatch(actions.setProjectDirectoryRoot("/foo/bar"));
+    const { dispatch, getState, cx } = createStore();
+    dispatch(actions.setProjectDirectoryRoot(cx, "/example.com/foo"));
+    dispatch(actions.setProjectDirectoryRoot(cx, "/foo/bar"));
     expect(getProjectDirectoryRoot(getState())).toBe("/example.com/foo/bar");
   });
 
   it("should update the directory ", () => {
-    const { dispatch, getState } = createStore();
-    dispatch(actions.setProjectDirectoryRoot("/example.com/foo"));
-    dispatch(actions.clearProjectDirectoryRoot());
-    dispatch(actions.setProjectDirectoryRoot("/example.com/bar"));
+    const { dispatch, getState, cx } = createStore();
+    dispatch(actions.setProjectDirectoryRoot(cx, "/example.com/foo"));
+    dispatch(actions.clearProjectDirectoryRoot(cx));
+    dispatch(actions.setProjectDirectoryRoot(cx, "/example.com/bar"));
     expect(getProjectDirectoryRoot(getState())).toBe("/example.com/bar");
   });
 
   it("should filter sources", async () => {
     const store = createStore({});
-    const { dispatch, getState } = store;
+    const { dispatch, getState, cx } = store;
     await dispatch(actions.newSource(makeSource("js/scopes.js")));
     await dispatch(actions.newSource(makeSource("lib/vendor.js")));
 
-    dispatch(actions.setProjectDirectoryRoot("localhost:8000/examples/js"));
+    dispatch(actions.setProjectDirectoryRoot(cx, "localhost:8000/examples/js"));
 
     const filteredSourcesByThread = getDisplayedSources(getState());
     const filteredSources = Object.values(filteredSourcesByThread)[0];
     const firstSource: Source = (Object.values(filteredSources)[0]: any);
 
     expect(firstSource.url).toEqual(
       "http://localhost:8000/examples/js/scopes.js"
     );
 
     expect(firstSource.relativeUrl).toEqual("scopes.js");
   });
 
   it("should update the child directory ", () => {
-    const { dispatch, getState } = createStore();
-    dispatch(actions.setProjectDirectoryRoot("example.com"));
-    dispatch(actions.setProjectDirectoryRoot("example.com/foo/bar"));
+    const { dispatch, getState, cx } = createStore();
+    dispatch(actions.setProjectDirectoryRoot(cx, "example.com"));
+    dispatch(actions.setProjectDirectoryRoot(cx, "example.com/foo/bar"));
     expect(getProjectDirectoryRoot(getState())).toBe("example.com/foo/bar");
   });
 
   it("should update the child directory when domain name is Webpack://", () => {
-    const { dispatch, getState } = createStore();
-    dispatch(actions.setProjectDirectoryRoot("webpack://"));
-    dispatch(actions.setProjectDirectoryRoot("webpack:///app"));
+    const { dispatch, getState, cx } = createStore();
+    dispatch(actions.setProjectDirectoryRoot(cx, "webpack://"));
+    dispatch(actions.setProjectDirectoryRoot(cx, "webpack:///app"));
     expect(getProjectDirectoryRoot(getState())).toBe("webpack:///app");
   });
 });
--- a/devtools/client/debugger/new/src/actions/tests/tabs.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/tabs.spec.js
@@ -11,121 +11,125 @@ import {
   makeSource
 } from "../../utils/test-head";
 const { getSelectedSource, getSourceTabs } = selectors;
 
 import { sourceThreadClient as threadClient } from "./helpers/threadClient.js";
 
 describe("closing tabs", () => {
   it("closing a tab", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
 
     const fooSource = makeSource("foo.js");
     await dispatch(actions.newSource(fooSource));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
-    dispatch(actions.closeTab(fooSource));
+    await dispatch(actions.selectLocation(cx, { sourceId: "foo.js", line: 1 }));
+    dispatch(actions.closeTab(cx, fooSource));
 
     expect(getSelectedSource(getState())).toBe(undefined);
     expect(getSourceTabs(getState())).toHaveLength(0);
   });
 
   it("closing the inactive tab", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
 
     const fooSource = makeSource("foo.js");
     await dispatch(actions.newSource(fooSource));
     await dispatch(actions.newSource(makeSource("bar.js")));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
-    await dispatch(actions.selectLocation({ sourceId: "bar.js", line: 1 }));
-    dispatch(actions.closeTab(fooSource));
+    await dispatch(actions.selectLocation(cx, { sourceId: "foo.js", line: 1 }));
+    await dispatch(actions.selectLocation(cx, { sourceId: "bar.js", line: 1 }));
+    dispatch(actions.closeTab(cx, fooSource));
 
     const selected = getSelectedSource(getState());
     expect(selected && selected.id).toBe("bar.js");
     expect(getSourceTabs(getState())).toHaveLength(1);
   });
 
   it("closing the only tab", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
 
     const fooSource = makeSource("foo.js");
     await dispatch(actions.newSource(fooSource));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
-    dispatch(actions.closeTab(fooSource));
+    await dispatch(actions.selectLocation(cx, { sourceId: "foo.js", line: 1 }));
+    dispatch(actions.closeTab(cx, fooSource));
 
     expect(getSelectedSource(getState())).toBe(undefined);
     expect(getSourceTabs(getState())).toHaveLength(0);
   });
 
   it("closing the active tab", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
 
     const barSource = makeSource("bar.js");
     await dispatch(actions.newSource(makeSource("foo.js")));
     await dispatch(actions.newSource(barSource));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
-    await dispatch(actions.selectLocation({ sourceId: "bar.js", line: 1 }));
-    await dispatch(actions.closeTab(barSource));
+    await dispatch(actions.selectLocation(cx, { sourceId: "foo.js", line: 1 }));
+    await dispatch(actions.selectLocation(cx, { sourceId: "bar.js", line: 1 }));
+    await dispatch(actions.closeTab(cx, barSource));
 
     const selected = getSelectedSource(getState());
     expect(selected && selected.id).toBe("foo.js");
     expect(getSourceTabs(getState())).toHaveLength(1);
   });
 
   it("closing many inactive tabs", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
 
     const fooSource = makeSource("foo.js");
     const barSource = makeSource("bar.js");
     await dispatch(actions.newSource(fooSource));
     await dispatch(actions.newSource(barSource));
     await dispatch(actions.newSource(makeSource("bazz.js")));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
-    await dispatch(actions.selectLocation({ sourceId: "bar.js", line: 1 }));
-    await dispatch(actions.selectLocation({ sourceId: "bazz.js", line: 1 }));
+    await dispatch(actions.selectLocation(cx, { sourceId: "foo.js", line: 1 }));
+    await dispatch(actions.selectLocation(cx, { sourceId: "bar.js", line: 1 }));
+    await dispatch(
+      actions.selectLocation(cx, { sourceId: "bazz.js", line: 1 })
+    );
 
     const tabs = [
       "http://localhost:8000/examples/foo.js",
       "http://localhost:8000/examples/bar.js"
     ];
-    dispatch(actions.closeTabs(tabs));
+    dispatch(actions.closeTabs(cx, tabs));
 
     const selected = getSelectedSource(getState());
     expect(selected && selected.id).toBe("bazz.js");
     expect(getSourceTabs(getState())).toHaveLength(1);
   });
 
   it("closing many tabs including the active tab", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
 
     await dispatch(actions.newSource(makeSource("foo.js")));
     await dispatch(actions.newSource(makeSource("bar.js")));
     await dispatch(actions.newSource(makeSource("bazz.js")));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
-    await dispatch(actions.selectLocation({ sourceId: "bar.js", line: 1 }));
-    await dispatch(actions.selectLocation({ sourceId: "bazz.js", line: 1 }));
+    await dispatch(actions.selectLocation(cx, { sourceId: "foo.js", line: 1 }));
+    await dispatch(actions.selectLocation(cx, { sourceId: "bar.js", line: 1 }));
+    await dispatch(
+      actions.selectLocation(cx, { sourceId: "bazz.js", line: 1 })
+    );
     const tabs = [
       "http://localhost:8000/examples/bar.js",
       "http://localhost:8000/examples/bazz.js"
     ];
-    await dispatch(actions.closeTabs(tabs));
+    await dispatch(actions.closeTabs(cx, tabs));
 
     const selected = getSelectedSource(getState());
     expect(selected && selected.id).toBe("foo.js");
     expect(getSourceTabs(getState())).toHaveLength(1);
   });
 
   it("closing all the tabs", async () => {
-    const { dispatch, getState } = createStore(threadClient);
+    const { dispatch, getState, cx } = createStore(threadClient);
 
     await dispatch(actions.newSource(makeSource("foo.js")));
     await dispatch(actions.newSource(makeSource("bar.js")));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
-    await dispatch(actions.selectLocation({ sourceId: "bar.js", line: 1 }));
+    await dispatch(actions.selectLocation(cx, { sourceId: "foo.js", line: 1 }));
+    await dispatch(actions.selectLocation(cx, { sourceId: "bar.js", line: 1 }));
     await dispatch(
-      actions.closeTabs([
+      actions.closeTabs(cx, [
         "http://localhost:8000/examples/foo.js",
         "http://localhost:8000/examples/bar.js"
       ])
     );
 
     expect(getSelectedSource(getState())).toBe(undefined);
     expect(getSourceTabs(getState())).toHaveLength(0);
   });
--- a/devtools/client/debugger/new/src/actions/tests/ui.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/ui.spec.js
@@ -80,67 +80,67 @@ describe("ui", () => {
     dispatch(actions.highlightLineRange(range));
     dispatch(actions.clearHighlightLineRange());
     expect(getHighlightedLineRange(getState())).toEqual({});
   });
 });
 
 describe("setProjectDirectoryRoot", () => {
   it("should set domain directory as root", () => {
-    const { dispatch, getState } = createStore();
-    dispatch(actions.setProjectDirectoryRoot("example.com"));
+    const { dispatch, getState, cx } = createStore();
+    dispatch(actions.setProjectDirectoryRoot(cx, "example.com"));
     expect(getProjectDirectoryRoot(getState())).toBe("example.com");
   });
 
   it("should set a directory as root directory", () => {
-    const { dispatch, getState } = createStore();
-    dispatch(actions.setProjectDirectoryRoot("/example.com/foo"));
+    const { dispatch, getState, cx } = createStore();
+    dispatch(actions.setProjectDirectoryRoot(cx, "/example.com/foo"));
     expect(getProjectDirectoryRoot(getState())).toBe("/example.com/foo");
   });
 
   it("should add to the directory ", () => {
-    const { dispatch, getState } = createStore();
-    dispatch(actions.setProjectDirectoryRoot("/example.com/foo"));
-    dispatch(actions.setProjectDirectoryRoot("/foo/bar"));
+    const { dispatch, getState, cx } = createStore();
+    dispatch(actions.setProjectDirectoryRoot(cx, "/example.com/foo"));
+    dispatch(actions.setProjectDirectoryRoot(cx, "/foo/bar"));
     expect(getProjectDirectoryRoot(getState())).toBe("/example.com/foo/bar");
   });
 
   it("should update the directory ", () => {
-    const { dispatch, getState } = createStore();
-    dispatch(actions.setProjectDirectoryRoot("/example.com/foo"));
-    dispatch(actions.clearProjectDirectoryRoot());
-    dispatch(actions.setProjectDirectoryRoot("/example.com/bar"));
+    const { dispatch, getState, cx } = createStore();
+    dispatch(actions.setProjectDirectoryRoot(cx, "/example.com/foo"));
+    dispatch(actions.clearProjectDirectoryRoot(cx));
+    dispatch(actions.setProjectDirectoryRoot(cx, "/example.com/bar"));
     expect(getProjectDirectoryRoot(getState())).toBe("/example.com/bar");
   });
 
   it("should filter sources", async () => {
     const store = createStore({});
-    const { dispatch, getState } = store;
+    const { dispatch, getState, cx } = store;
     await dispatch(actions.newSource(makeSource("js/scopes.js")));
     await dispatch(actions.newSource(makeSource("lib/vendor.js")));
 
-    dispatch(actions.setProjectDirectoryRoot("localhost:8000/examples/js"));
+    dispatch(actions.setProjectDirectoryRoot(cx, "localhost:8000/examples/js"));
 
     const filteredSourcesByThread = getDisplayedSources(getState());
     const filteredSources = Object.values(filteredSourcesByThread)[0];
     const firstSource: Source = (Object.values(filteredSources)[0]: any);
 
     expect(firstSource.url).toEqual(
       "http://localhost:8000/examples/js/scopes.js"
     );
 
     expect(firstSource.relativeUrl).toEqual("scopes.js");
   });
 
   it("should update the child directory ", () => {
-    const { dispatch, getState } = createStore();
-    dispatch(actions.setProjectDirectoryRoot("example.com"));
-    dispatch(actions.setProjectDirectoryRoot("example.com/foo/bar"));
+    const { dispatch, getState, cx } = createStore();
+    dispatch(actions.setProjectDirectoryRoot(cx, "example.com"));
+    dispatch(actions.setProjectDirectoryRoot(cx, "example.com/foo/bar"));
     expect(getProjectDirectoryRoot(getState())).toBe("example.com/foo/bar");
   });
 
   it("should update the child directory when domain name is Webpack://", () => {
-    const { dispatch, getState } = createStore();
-    dispatch(actions.setProjectDirectoryRoot("webpack://"));
-    dispatch(actions.setProjectDirectoryRoot("webpack:///app"));
+    const { dispatch, getState, cx } = createStore();
+    dispatch(actions.setProjectDirectoryRoot(cx, "webpack://"));
+    dispatch(actions.setProjectDirectoryRoot(cx, "webpack:///app"));
     expect(getProjectDirectoryRoot(getState())).toBe("webpack:///app");
   });
 });
--- a/devtools/client/debugger/new/src/actions/types/ASTAction.js
+++ b/devtools/client/debugger/new/src/actions/types/ASTAction.js
@@ -1,40 +1,46 @@
 /* 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 { SymbolDeclarations, AstLocation } from "../../workers/parser";
 import type { PromiseAction } from "../utils/middleware/promise";
+import type { Context } from "../../types";
 
 export type ASTAction =
   | PromiseAction<
       {|
         +type: "SET_SYMBOLS",
+        +cx: Context,
         +sourceId: string
       |},
       SymbolDeclarations
     >
   | {|
       +type: "OUT_OF_SCOPE_LOCATIONS",
+      +cx: Context,
       +locations: ?(AstLocation[])
     |}
   | {|
       +type: "IN_SCOPE_LINES",
+      +cx: Context,
       +lines: number[]
     |}
   | PromiseAction<
       {|
-        +type: "SET_PREVIEW"
+        +type: "SET_PREVIEW",
+        +cx: Context
       |},
       {
         expression: string,
         result: any,
         location: AstLocation,
         tokenPos: any,
         cursorPos: any
       }
     >
   | {|
-      +type: "CLEAR_SELECTION"
+      +type: "CLEAR_SELECTION",
+      +cx: Context
     |};
--- a/devtools/client/debugger/new/src/actions/types/BreakpointAction.js
+++ b/devtools/client/debugger/new/src/actions/types/BreakpointAction.js
@@ -5,17 +5,18 @@
 // @flow
 
 import type {
   Breakpoint,
   SourceLocation,
   XHRBreakpoint,
   Source,
   BreakpointPositions,
-  PendingLocation
+  PendingLocation,
+  Context
 } from "../../types";
 
 import type { PromiseAction } from "../utils/middleware/promise";
 
 export type BreakpointAction =
   | PromiseAction<{|
       +type: "SET_XHR_BREAKPOINT",
       +breakpoint: XHRBreakpoint
@@ -37,23 +38,27 @@ export type BreakpointAction =
     |}>
   | PromiseAction<{|
       +type: "REMOVE_XHR_BREAKPOINT",
       +index: number,
       +breakpoint: XHRBreakpoint
     |}>
   | {|
       +type: "SET_BREAKPOINT",
+      +cx: Context,
       +breakpoint: Breakpoint
     |}
   | {|
       +type: "REMOVE_BREAKPOINT",
+      +cx: Context,
       +location: SourceLocation
     |}
   | {|
       +type: "REMOVE_PENDING_BREAKPOINT",
+      +cx: Context,
       +location: PendingLocation
     |}
   | {|
       type: "ADD_BREAKPOINT_POSITIONS",
+      +cx: Context,
       positions: BreakpointPositions,
       source: Source
     |};
--- a/devtools/client/debugger/new/src/actions/types/PauseAction.js
+++ b/devtools/client/debugger/new/src/actions/types/PauseAction.js
@@ -1,130 +1,151 @@
 /* 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 { Command } from "../../reducers/types";
-import type { Expression, LoadedObject, Frame, Scope, Why } from "../../types";
+import type {
+  Expression,
+  LoadedObject,
+  Frame,
+  Scope,
+  Why,
+  ThreadContext
+} from "../../types";
 
 import type { PromiseAction } from "../utils/middleware/promise";
 
 export type PauseAction =
   | {|
       +type: "BREAK_ON_NEXT",
+      +cx: ThreadContext,
       +thread: string,
       +value: boolean
     |}
   | {|
+      // Note: Do not include cx, as this action is triggered by the server.
       +type: "RESUME",
       +thread: string,
       +value: void,
       +wasStepping: boolean
     |}
   | {|
+      // Note: Do not include cx, as this action is triggered by the server.
       +type: "PAUSED",
       +thread: string,
       +why: Why,
       +scopes: Scope,
       +frames: Frame[],
       +selectedFrameId: string,
       +loadedObjects: LoadedObject[]
     |}
   | {|
       +type: "PAUSE_ON_EXCEPTIONS",
       +shouldPauseOnExceptions: boolean,
       +shouldPauseOnCaughtExceptions: boolean
     |}
   | PromiseAction<{|
       +type: "COMMAND",
+      +cx: ThreadContext,
       +thread: string,
       +command: Command
     |}>
   | {|
       +type: "SELECT_FRAME",
+      +cx: ThreadContext,
       +thread: string,
       +frame: Frame
     |}
   | {|
       +type: "SELECT_COMPONENT",
       +thread: string,
       +componentIndex: number
     |}
   | {|
       +type: "SET_POPUP_OBJECT_PROPERTIES",
+      +cx: ThreadContext,
       +thread: string,
       +objectId: string,
       +properties: Object
     |}
   | {|
       +type: "ADD_EXPRESSION",
+      +cx: ThreadContext,
       +thread: string,
       +id: number,
       +input: string,
       +value: string,
       +expressionError: ?string
     |}
   | PromiseAction<
       {|
         +type: "EVALUATE_EXPRESSION",
+        +cx: ThreadContext,
         +thread: string,
         +input: string
       |},
       Object
     >
   | PromiseAction<{|
       +type: "EVALUATE_EXPRESSIONS",
+      +cx: ThreadContext,
       +results: Expression[],
       +inputs: string[]
     |}>
   | {|
       +type: "UPDATE_EXPRESSION",
+      +cx: ThreadContext,
       +expression: Expression,
       +input: string,
       +expressionError: ?string
     |}
   | {|
       +type: "DELETE_EXPRESSION",
       +input: string
     |}
   | {|
       +type: "CLEAR_AUTOCOMPLETE"
     |}
   | {|
       +type: "CLEAR_EXPRESSION_ERROR"
     |}
   | {|
       +type: "AUTOCOMPLETE",
+      +cx: ThreadContext,
       +input: string,
       +result: Object
     |}
   | PromiseAction<
       {|
         +type: "MAP_SCOPES",
+        +cx: ThreadContext,
         +thread: string,
         +frame: Frame
       |},
       {
         scope: Scope,
         mappings: {
           [string]: string | null
         }
       }
     >
   | {|
       +type: "MAP_FRAMES",
+      +cx: ThreadContext,
       +thread: string,
       +frames: Frame[],
       +selectedFrameId: string
     |}
   | PromiseAction<
       {|
         +type: "ADD_SCOPES",
+        +cx: ThreadContext,
         +thread: string,
         +frame: Frame
       |},
       Scope
     >
   | {|
       +type: "TOGGLE_SKIP_PAUSING",
       +thread: string,
--- a/devtools/client/debugger/new/src/actions/types/SourceAction.js
+++ b/devtools/client/debugger/new/src/actions/types/SourceAction.js
@@ -1,54 +1,61 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
-import type { Source, SourceLocation } from "../../types";
+import type { Source, SourceLocation, Context } from "../../types";
 import type { PromiseAction } from "../utils/middleware/promise";
 
 export type LoadSourceAction = PromiseAction<
   {|
     +type: "LOAD_SOURCE_TEXT",
+    +cx: Context,
     +sourceId: string,
     +epoch: number
   |},
   Source
 >;
 export type SourceAction =
   | LoadSourceAction
   | {|
       +type: "ADD_SOURCE",
+      +cx: Context,
       +source: Source
     |}
   | {|
       +type: "ADD_SOURCES",
+      +cx: Context,
       +sources: Array<Source>
     |}
   | {|
       +type: "UPDATE_SOURCE",
+      +cx: Context,
       +source: Source
     |}
   | {|
       +type: "SET_SELECTED_LOCATION",
+      +cx: Context,
       +source: Source,
       +location?: SourceLocation
     |}
   | {|
       +type: "SET_PENDING_SELECTED_LOCATION",
+      +cx: Context,
       +url: string,
       +line?: number,
       +column?: number
     |}
-  | {| type: "CLEAR_SELECTED_LOCATION" |}
+  | {| type: "CLEAR_SELECTED_LOCATION", +cx: Context |}
   | PromiseAction<
       {|
         +type: "BLACKBOX",
+        +cx: Context,
         +source: Source
       |},
       {|
         +isBlackBoxed: boolean
       |}
     >
   | {|
       +type: "MOVE_TAB",
--- a/devtools/client/debugger/new/src/actions/types/UIAction.js
+++ b/devtools/client/debugger/new/src/actions/types/UIAction.js
@@ -1,15 +1,20 @@
 /* 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 { Source, PartialRange, SourceLocation } from "../../types";
+import type {
+  Source,
+  PartialRange,
+  SourceLocation,
+  Context
+} from "../../types";
 
 import type {
   ActiveSearchType,
   OrientationType,
   SelectedPrimaryPaneTabType
 } from "../../reducers/ui";
 
 export type panelPositionType = "start" | "end";
@@ -59,16 +64,17 @@ export type UIAction =
       +location: SourceLocation,
       +log: boolean
     |}
   | {|
       +type: "CLOSE_CONDITIONAL_PANEL"
     |}
   | {|
       +type: "SET_PROJECT_DIRECTORY_ROOT",
+      +cx: Context,
       +url: string
     |}
   | {|
       +type: "SET_PRIMARY_PANE_TAB",
       +tabName: SelectedPrimaryPaneTabType
     |}
   | {|
       +type: "CLOSE_PROJECT_SEARCH"
--- a/devtools/client/debugger/new/src/actions/types/index.js
+++ b/devtools/client/debugger/new/src/actions/types/index.js
@@ -1,15 +1,15 @@
 /* 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 { WorkerList, MainThread } from "../../types";
+import type { WorkerList, MainThread, Context } from "../../types";
 import type { State } from "../../reducers/types";
 import type { MatchedLocations } from "../../reducers/file-search";
 import type { TreeNode } from "../../utils/sources-tree/types";
 import type { SearchOperation } from "../../reducers/project-text-search";
 
 import type { BreakpointAction } from "./BreakpointAction";
 import type { SourceAction } from "./SourceAction";
 import type { UIAction } from "./UIAction";
@@ -70,66 +70,76 @@ type NavigateAction =
 
 export type FocusItem = {
   thread: string,
   item: TreeNode
 };
 
 export type SourceTreeAction =
   | {| +type: "SET_EXPANDED_STATE", +thread: string, +expanded: any |}
-  | {| +type: "SET_FOCUSED_SOURCE_ITEM", item: FocusItem |};
+  | {| +type: "SET_FOCUSED_SOURCE_ITEM", +cx: Context, item: FocusItem |};
 
 export type ProjectTextSearchAction =
-  | {| +type: "ADD_QUERY", +query: string |}
+  | {| +type: "ADD_QUERY", +cx: Context, +query: string |}
   | {|
       +type: "ADD_SEARCH_RESULT",
+      +cx: Context,
       +result: ProjectTextSearchResult
     |}
-  | {| +type: "UPDATE_STATUS", +status: string |}
-  | {| +type: "CLEAR_SEARCH_RESULTS" |}
-  | {| +type: "ADD_ONGOING_SEARCH", +ongoingSearch: SearchOperation |}
-  | {| +type: "CLEAR_SEARCH" |};
+  | {| +type: "UPDATE_STATUS", +cx: Context, +status: string |}
+  | {| +type: "CLEAR_SEARCH_RESULTS", +cx: Context |}
+  | {|
+      +type: "ADD_ONGOING_SEARCH",
+      +cx: Context,
+      +ongoingSearch: SearchOperation
+    |}
+  | {| +type: "CLEAR_SEARCH", +cx: Context |};
 
 export type FileTextSearchModifier =
   | "caseSensitive"
   | "wholeWord"
   | "regexMatch";
 
 export type FileTextSearchAction =
   | {|
       +type: "TOGGLE_FILE_SEARCH_MODIFIER",
+      +cx: Context,
       +modifier: FileTextSearchModifier
     |}
   | {|
       +type: "UPDATE_FILE_SEARCH_QUERY",
+      +cx: Context,
       +query: string
     |}
   | {|
       +type: "UPDATE_SEARCH_RESULTS",
+      +cx: Context,
       +results: {
         matches: MatchedLocations[],
         matchIndex: number,
         count: number,
         index: number
       }
     |};
 
 export type QuickOpenAction =
   | {| +type: "SET_QUICK_OPEN_QUERY", +query: string |}
   | {| +type: "OPEN_QUICK_OPEN", +query?: string |}
   | {| +type: "CLOSE_QUICK_OPEN" |};
 
-export type DebugeeAction =
+export type DebuggeeAction =
   | {|
       +type: "SET_WORKERS",
+      +cx: Context,
       +workers: WorkerList,
       +mainThread: string
     |}
   | {|
       +type: "SELECT_THREAD",
+      +cx: Context,
       +thread: string
     |};
 
 export type {
   StartPromiseAction,
   DonePromiseAction,
   ErrorPromiseAction
 } from "../utils/middleware/promise";
@@ -151,10 +161,10 @@ export type Action =
   | BreakpointAction
   | PauseAction
   | NavigateAction
   | UIAction
   | ASTAction
   | QuickOpenAction
   | FileTextSearchAction
   | ProjectTextSearchAction
-  | DebugeeAction
+  | DebuggeeAction
   | SourceTreeAction;
--- a/devtools/client/debugger/new/src/actions/ui.js
+++ b/devtools/client/debugger/new/src/actions/ui.js
@@ -12,17 +12,17 @@ import {
   getFileSearchQuery,
   getProjectDirectoryRoot
 } from "../selectors";
 import { selectSource } from "../actions/sources/select";
 import type { ThunkArgs, panelPositionType } from "./types";
 import { getEditor, getLocationsInViewport } from "../utils/editor";
 import { searchContents } from "./file-search";
 
-import type { SourceLocation } from "../types";
+import type { SourceLocation, Context } from "../types";
 import type {
   ActiveSearchType,
   OrientationType,
   SelectedPrimaryPaneTabType
 } from "../reducers/ui";
 
 export function setPrimaryPaneTab(tabName: SelectedPrimaryPaneTabType) {
   return { type: "SET_PRIMARY_PANE_TAB", tabName };
@@ -48,37 +48,37 @@ export function setActiveSearch(activeSe
 
     dispatch({
       type: "TOGGLE_ACTIVE_SEARCH",
       value: activeSearch
     });
   };
 }
 
-export function updateActiveFileSearch() {
+export function updateActiveFileSearch(cx: Context) {
   return ({ dispatch, getState }: ThunkArgs) => {
     const isFileSearchOpen = getActiveSearch(getState()) === "file";
     const fileSearchQuery = getFileSearchQuery(getState());
     if (isFileSearchOpen && fileSearchQuery) {
       const editor = getEditor();
-      dispatch(searchContents(fileSearchQuery, editor));
+      dispatch(searchContents(cx, fileSearchQuery, editor));
     }
   };
 }
 
 export function toggleFrameworkGrouping(toggleValue: boolean) {
   return ({ dispatch, getState }: ThunkArgs) => {
     dispatch({
       type: "TOGGLE_FRAMEWORK_GROUPING",
       value: toggleValue
     });
   };
 }
 
-export function showSource(sourceId: string) {
+export function showSource(cx: Context, sourceId: string) {
   return ({ dispatch, getState }: ThunkArgs) => {
     const source = getSource(getState(), sourceId);
     if (!source) {
       return;
     }
 
     if (getPaneCollapse(getState(), "start")) {
       dispatch({
@@ -86,17 +86,17 @@ export function showSource(sourceId: str
         position: "start",
         paneCollapsed: false
       });
     }
 
     dispatch(setPrimaryPaneTab("sources"));
 
     dispatch({ type: "SHOW_SOURCE", source: null });
-    dispatch(selectSource(source.id));
+    dispatch(selectSource(cx, source.id));
     dispatch({ type: "SHOW_SOURCE", source });
   };
 }
 
 export function togglePaneCollapse(
   position: panelPositionType,
   paneCollapsed: boolean
 ) {
@@ -166,40 +166,42 @@ export function openConditionalPanel(
 }
 
 export function closeConditionalPanel() {
   return {
     type: "CLOSE_CONDITIONAL_PANEL"
   };
 }
 
-export function clearProjectDirectoryRoot() {
+export function clearProjectDirectoryRoot(cx: Context) {
   return {
     type: "SET_PROJECT_DIRECTORY_ROOT",
+    cx,
     url: ""
   };
 }
 
-export function setProjectDirectoryRoot(newRoot: string) {
+export function setProjectDirectoryRoot(cx: Context, newRoot: string) {
   return ({ dispatch, getState }: ThunkArgs) => {
     const curRoot = getProjectDirectoryRoot(getState());
     if (newRoot && curRoot) {
       const newRootArr = newRoot.replace(/\/+/g, "/").split("/");
       const curRootArr = curRoot
         .replace(/^\//, "")
         .replace(/\/+/g, "/")
         .split("/");
       if (newRootArr[0] !== curRootArr[0]) {
         newRootArr.splice(0, 2);
         newRoot = `${curRoot}/${newRootArr.join("/")}`;
       }
     }
 
     dispatch({
       type: "SET_PROJECT_DIRECTORY_ROOT",
+      cx,
       url: newRoot
     });
   };
 }
 
 export function updateViewport() {
   return {
     type: "SET_VIEWPORT",
--- a/devtools/client/debugger/new/src/actions/utils/create-store.js
+++ b/devtools/client/debugger/new/src/actions/utils/create-store.js
@@ -13,16 +13,17 @@
 
 import { createStore, applyMiddleware } from "redux";
 import { waitUntilService } from "./middleware/wait-service";
 import { log } from "./middleware/log";
 import { history } from "./middleware/history";
 import { promise } from "./middleware/promise";
 import { thunk } from "./middleware/thunk";
 import { timing } from "./middleware/timing";
+import { context } from "./middleware/context";
 
 /**
  * @memberof utils/create-store
  * @static
  */
 type ReduxStoreOptions = {
   makeThunkArgs?: Function,
   history?: Array<Object>,
@@ -42,16 +43,17 @@ type ReduxStoreOptions = {
  *                   used in tests.
  *        - middleware: array of middleware to be included in the redux store
  * @memberof utils/create-store
  * @static
  */
 const configureStore = (opts: ReduxStoreOptions = {}) => {
   const middleware = [
     thunk(opts.makeThunkArgs),
+    context,
     promise,
 
     // Order is important: services must go last as they always
     // operate on "already transformed" actions. Actions going through
     // them shouldn't have any special fields like promises, they
     // should just be normal JSON objects.
     waitUntilService
   ];
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/actions/utils/middleware/context.js
@@ -0,0 +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 {
+  validateNavigateContext,
+  validateContext
+} from "../../../utils/context";
+
+import type { ThunkArgs } from "../../types";
+
+function validateActionContext(getState, action) {
+  if (action.type == "COMMAND" && action.status == "done") {
+    // The thread will have resumed execution since the action was initiated,
+    // so just make sure we haven't navigated.
+    validateNavigateContext(getState(), action.cx);
+    return;
+  }
+
+  // Validate using all available information in the context.
+  validateContext(getState(), action.cx);
+}
+
+// Middleware which looks for actions that have a cx property and ignores
+// them if the context is no longer valid.
+function context({ dispatch, getState }: ThunkArgs) {
+  return (next: Function) => (action: Object) => {
+    if ("cx" in action) {
+      validateActionContext(getState, action);
+    }
+    return next(action);
+  };
+}
+
+export { context };
--- a/devtools/client/debugger/new/src/actions/utils/middleware/moz.build
+++ b/devtools/client/debugger/new/src/actions/utils/middleware/moz.build
@@ -3,15 +3,16 @@
 # 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(
+    'context.js',
     'history.js',
     'log.js',
     'promise.js',
     'thunk.js',
     'timing.js',
     'wait-service.js',
 )
--- a/devtools/client/debugger/new/src/components/Editor/Breakpoint.js
+++ b/devtools/client/debugger/new/src/components/Editor/Breakpoint.js
@@ -10,23 +10,28 @@ import classnames from "classnames";
 import { getDocument, toEditorLine } from "../../utils/editor";
 import { getSelectedLocation } from "../../utils/source-maps";
 import { features } from "../../utils/prefs";
 import { showMenu } from "devtools-contextmenu";
 import { breakpointItems } from "./menus/breakpoints";
 import type { BreakpointItemActions } from "./menus/breakpoints";
 import type { EditorItemActions } from "./menus/editor";
 
-import type { Source, Breakpoint as BreakpointType } from "../../types";
+import type {
+  Source,
+  Breakpoint as BreakpointType,
+  ThreadContext
+} from "../../types";
 
 const breakpointSvg = document.createElement("div");
 breakpointSvg.innerHTML =
   '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 15" width="60" height="15"><path d="M53.07.5H1.5c-.54 0-1 .46-1 1v12c0 .54.46 1 1 1h51.57c.58 0 1.15-.26 1.53-.7l4.7-6.3-4.7-6.3c-.38-.44-.95-.7-1.53-.7z"/></svg>';
 
 type Props = {
+  cx: ThreadContext,
   breakpoint: BreakpointType,
   selectedSource: Source,
   editor: Object,
   breakpointActions: BreakpointItemActions,
   editorActions: EditorItemActions
 };
 
 class Breakpoint extends PureComponent<Props> {
@@ -56,57 +61,60 @@ class Breakpoint extends PureComponent<P
     // NOTE: flow does not know about oncontextmenu
     (bp: any).oncontextmenu = this.onContextMenu;
 
     return bp;
   }
 
   onClick = (event: MouseEvent) => {
     const {
+      cx,
       breakpointActions,
       editorActions,
       breakpoint,
       selectedSource
     } = this.props;
 
     // ignore right clicks
     if ((event.ctrlKey && event.button === 0) || event.button === 2) {
       return;
     }
 
     event.stopPropagation();
     event.preventDefault();
 
     const selectedLocation = getSelectedLocation(breakpoint, selectedSource);
     if (event.metaKey) {
-      return editorActions.continueToHere(selectedLocation.line);
+      return editorActions.continueToHere(cx, selectedLocation.line);
     }
 
     if (event.shiftKey) {
       if (features.columnBreakpoints) {
         return breakpointActions.toggleBreakpointsAtLine(
+          cx,
           !breakpoint.disabled,
           selectedLocation.line
         );
       }
 
-      return breakpointActions.toggleDisabledBreakpoint(breakpoint);
+      return breakpointActions.toggleDisabledBreakpoint(cx, breakpoint);
     }
 
     return breakpointActions.removeBreakpointsAtLine(
+      cx,
       selectedLocation.sourceId,
       selectedLocation.line
     );
   };
 
   onContextMenu = (event: MouseEvent) => {
-    const { breakpoint, breakpointActions } = this.props;
+    const { cx, breakpoint, breakpointActions } = this.props;
     event.stopPropagation();
     event.preventDefault();
-    showMenu(event, breakpointItems(breakpoint, breakpointActions));
+    showMenu(event, breakpointItems(cx, breakpoint, breakpointActions));
   };
 
   addBreakpoint(props: Props) {
     const { breakpoint, editor, selectedSource } = props;
     const selectedLocation = getSelectedLocation(breakpoint, selectedSource);
 
     // Hidden Breakpoints are never rendered on the client
     if (breakpoint.options.hidden) {
--- a/devtools/client/debugger/new/src/components/Editor/Breakpoints.js
+++ b/devtools/client/debugger/new/src/components/Editor/Breakpoints.js
@@ -9,45 +9,52 @@ import Breakpoint from "./Breakpoint";
 import { getSelectedSource, getFirstVisibleBreakpoints } from "../../selectors";
 import { makeBreakpointId } from "../../utils/breakpoint";
 import { connect } from "../../utils/connect";
 import { breakpointItemActions } from "./menus/breakpoints";
 import { editorItemActions } from "./menus/editor";
 
 import type { BreakpointItemActions } from "./menus/breakpoints";
 import type { EditorItemActions } from "./menus/editor";
-import type { Breakpoint as BreakpointType, Source } from "../../types";
+import type {
+  Breakpoint as BreakpointType,
+  Source,
+  ThreadContext
+} from "../../types";
 
 type Props = {
+  cx: ThreadContext,
   selectedSource: Source,
   breakpoints: BreakpointType[],
   editor: Object,
   breakpointActions: BreakpointItemActions,
   editorActions: EditorItemActions
 };
 
 class Breakpoints extends Component<Props> {
   render() {
     const {
+      cx,
       breakpoints,
       selectedSource,
       editor,
       breakpointActions,
       editorActions
     } = this.props;
 
     if (!breakpoints || selectedSource.isBlackBoxed) {
       return null;
     }
 
     return (
       <div>
         {breakpoints.map(bp => {
           return (
             <Breakpoint
+              cx={cx}
               key={makeBreakpointId(bp.location)}
               breakpoint={bp}
               selectedSource={selectedSource}
               editor={editor}
               breakpointActions={breakpointActions}
               editorActions={editorActions}
             />
           );
--- a/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoint.js
+++ b/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoint.js
@@ -8,23 +8,24 @@ import classnames from "classnames";
 import { showMenu } from "devtools-contextmenu";
 
 import { getDocument } from "../../utils/editor";
 import { breakpointItems, createBreakpointItems } from "./menus/breakpoints";
 
 // eslint-disable-next-line max-len
 import type { ColumnBreakpoint as ColumnBreakpointType } from "../../selectors/visibleColumnBreakpoints";
 import type { BreakpointItemActions } from "./menus/breakpoints";
-import type { Source } from "../../types";
+import type { Source, Context } from "../../types";
 
 type Bookmark = {
   clear: Function
 };
 
 type Props = {
+  cx: Context,
   editor: Object,
   source: Source,
   columnBreakpoint: ColumnBreakpointType,
   breakpointActions: BreakpointItemActions
 };
 
 const breakpointButton = document.createElement("button");
 breakpointButton.innerHTML =
@@ -83,35 +84,36 @@ export default class ColumnBreakpoint ex
       this.bookmark.clear();
       this.bookmark = null;
     }
   };
 
   onClick = (event: MouseEvent) => {
     event.stopPropagation();
     event.preventDefault();
-    const { columnBreakpoint, breakpointActions } = this.props;
+    const { cx, columnBreakpoint, breakpointActions } = this.props;
     if (columnBreakpoint.breakpoint) {
-      breakpointActions.removeBreakpoint(columnBreakpoint.breakpoint);
+      breakpointActions.removeBreakpoint(cx, columnBreakpoint.breakpoint);
     } else {
-      breakpointActions.addBreakpoint(columnBreakpoint.location);
+      breakpointActions.addBreakpoint(cx, columnBreakpoint.location);
     }
   };
 
   onContextMenu = (event: MouseEvent) => {
     event.stopPropagation();
     event.preventDefault();
     const {
+      cx,
       columnBreakpoint: { breakpoint, location },
       breakpointActions
     } = this.props;
 
     const items = breakpoint
-      ? breakpointItems(breakpoint, breakpointActions)
-      : createBreakpointItems(location, breakpointActions);
+      ? breakpointItems(cx, breakpoint, breakpointActions)
+      : createBreakpointItems(cx, location, breakpointActions);
 
     showMenu(event, items);
   };
 
   componentDidMount() {
     this.addColumnBreakpoint();
   }
 
--- a/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoints.js
+++ b/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoints.js
@@ -3,66 +3,74 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { Component } from "react";
 
 import ColumnBreakpoint from "./ColumnBreakpoint";
 
-import { getSelectedSource, visibleColumnBreakpoints } from "../../selectors";
+import {
+  getSelectedSource,
+  visibleColumnBreakpoints,
+  getContext
+} from "../../selectors";
 import { connect } from "../../utils/connect";
 import { makeBreakpointId } from "../../utils/breakpoint";
 import { breakpointItemActions } from "./menus/breakpoints";
 import type { BreakpointItemActions } from "./menus/breakpoints";
 
-import type { Source } from "../../types";
+import type { Source, Context } from "../../types";
 // eslint-disable-next-line max-len
 import type { ColumnBreakpoint as ColumnBreakpointType } from "../../selectors/visibleColumnBreakpoints";
 
 type Props = {
+  cx: Context,
   editor: Object,
   selectedSource: Source,
   columnBreakpoints: ColumnBreakpointType[],
   breakpointActions: BreakpointItemActions
 };
 
 class ColumnBreakpoints extends Component<Props> {
   props: Props;
 
   render() {
     const {
+      cx,
       editor,
       columnBreakpoints,
       selectedSource,
       breakpointActions
     } = this.props;
 
     if (!selectedSource || selectedSource.isBlackBoxed) {
       return null;
     }
 
     let breakpoints;
     editor.codeMirror.operation(() => {
       breakpoints = columnBreakpoints.map(breakpoint => (
         <ColumnBreakpoint
+          cx={cx}
           key={makeBreakpointId(breakpoint.location)}
           columnBreakpoint={breakpoint}
           editor={editor}
           source={selectedSource}
           breakpointActions={breakpointActions}
         />
       ));
     });
     return <div>{breakpoints}</div>;
   }
 }
 
 const mapStateToProps = state => {
   return {
+    cx: getContext(state),
     selectedSource: getSelectedSource(state),
     columnBreakpoints: visibleColumnBreakpoints(state)
   };
 };
 
 export default connect(
   mapStateToProps,
   dispatch => ({ breakpointActions: breakpointItemActions(dispatch) })
--- a/devtools/client/debugger/new/src/components/Editor/ConditionalPanel.js
+++ b/devtools/client/debugger/new/src/components/Editor/ConditionalPanel.js
@@ -9,24 +9,26 @@ import { connect } from "../../utils/con
 import classNames from "classnames";
 import "./ConditionalPanel.css";
 import { toEditorLine } from "../../utils/editor";
 import actions from "../../actions";
 
 import {
   getBreakpointForLocation,
   getConditionalPanelLocation,
-  getLogPointStatus
+  getLogPointStatus,
+  getContext
 } from "../../selectors";
 
-import type { SourceLocation } from "../../types";
+import type { SourceLocation, Context } from "../../types";
 
 type Props = {
+  cx: Context,
   breakpoint: ?Object,
-  setBreakpointOptions: Function,
+  setBreakpointOptions: typeof actions.setBreakpointOptions,
   location: SourceLocation,
   log: boolean,
   editor: Object,
   openConditionalPanel: typeof actions.openConditionalPanel,
   closeConditionalPanel: typeof actions.closeConditionalPanel
 };
 
 export class ConditionalPanel extends PureComponent<Props> {
@@ -58,20 +60,20 @@ export class ConditionalPanel extends Pu
     if (e.key === "Enter") {
       this.saveAndClose();
     } else if (e.key === "Escape") {
       this.props.closeConditionalPanel();
     }
   };
 
   setBreakpoint(value: string) {
-    const { location, log, breakpoint } = this.props;
+    const { cx, location, log, breakpoint } = this.props;
     const options = breakpoint ? breakpoint.options : {};
     const type = log ? "logValue" : "condition";
-    return this.props.setBreakpointOptions(location, {
+    return this.props.setBreakpointOptions(cx, location, {
       ...options,
       [type]: value
     });
   }
 
   clearConditionalPanel() {
     if (this.cbPanel) {
       this.cbPanel.clear();
@@ -194,16 +196,17 @@ export class ConditionalPanel extends Pu
     return null;
   }
 }
 
 const mapStateToProps = state => {
   const location = getConditionalPanelLocation(state);
   const log = getLogPointStatus(state);
   return {
+    cx: getContext(state),
     breakpoint: getBreakpointForLocation(state, location),
     location,
     log
   };
 };
 
 const {
   setBreakpointOptions,
--- a/devtools/client/debugger/new/src/components/Editor/EditorMenu.js
+++ b/devtools/client/debugger/new/src/components/Editor/EditorMenu.js
@@ -7,26 +7,28 @@
 import { Component } from "react";
 import { connect } from "../../utils/connect";
 import { showMenu } from "devtools-contextmenu";
 
 import { getSourceLocationFromMouseEvent } from "../../utils/editor";
 import {
   getPrettySource,
   getIsPaused,
-  getCurrentThread
+  getCurrentThread,
+  getThreadContext
 } from "../../selectors";
 
 import { editorMenuItems, editorItemActions } from "./menus/editor";
 
-import type { Source } from "../../types";
+import type { Source, ThreadContext } from "../../types";
 import type { EditorItemActions } from "./menus/editor";
 import type SourceEditor from "../../utils/editor/source-editor";
 
 type Props = {
+  cx: ThreadContext,
   contextMenu: ?MouseEvent,
   editorActions: EditorItemActions,
   clearContextMenu: () => void,
   editor: SourceEditor,
   hasPrettySource: boolean,
   isPaused: boolean,
   selectedSource: Source
 };
@@ -38,16 +40,17 @@ class EditorMenu extends Component<Props
     this.props.clearContextMenu();
     if (nextProps.contextMenu) {
       this.showMenu(nextProps);
     }
   }
 
   showMenu(props) {
     const {
+      cx,
       editor,
       selectedSource,
       editorActions,
       hasPrettySource,
       isPaused,
       contextMenu: event
     } = props;
 
@@ -56,16 +59,17 @@ class EditorMenu extends Component<Props
       selectedSource,
       // Use a coercion, as contextMenu is optional
       (event: any)
     );
 
     showMenu(
       event,
       editorMenuItems({
+        cx,
         editorActions,
         selectedSource,
         hasPrettySource,
         location,
         isPaused,
         selectionText: editor.codeMirror.getSelection().trim(),
         isTextSelected: editor.codeMirror.somethingSelected()
       })
@@ -73,16 +77,17 @@ class EditorMenu extends Component<Props
   }
 
   render() {
     return null;
   }
 }
 
 const mapStateToProps = (state, props) => ({
+  cx: getThreadContext(state),
   isPaused: getIsPaused(state, getCurrentThread(state)),
   hasPrettySource: !!getPrettySource(state, props.selectedSource.id)
 });
 
 const mapDispatchToProps = dispatch => ({
   editorActions: editorItemActions(dispatch)
 });
 
--- a/devtools/client/debugger/new/src/components/Editor/Footer.js
+++ b/devtools/client/debugger/new/src/components/Editor/Footer.js
@@ -5,43 +5,45 @@
 // @flow
 import React, { PureComponent } from "react";
 import { connect } from "../../utils/connect";
 import classnames from "classnames";
 import actions from "../../actions";
 import {
   getSelectedSource,
   getPrettySource,
-  getPaneCollapse
+  getPaneCollapse,
+  getContext
 } from "../../selectors";
 
 import {
   isPretty,
   isLoaded,
   getFilename,
   isOriginal,
   isLoading,
   shouldBlackbox
 } from "../../utils/source";
 import { getGeneratedSource } from "../../reducers/sources";
 import { shouldShowPrettyPrint } from "../../utils/editor";
 
 import { PaneToggleButton } from "../shared/Button";
 import AccessibleImage from "../shared/AccessibleImage";
 
-import type { Source } from "../../types";
+import type { Source, Context } from "../../types";
 
 import "./Footer.css";
 
 type CursorPosition = {
   line: number,
   column: number
 };
 
 type Props = {
+  cx: Context,
   selectedSource: Source,
   mappedSource: Source,
   endPanelCollapsed: boolean,
   horizontal: boolean,
   togglePrettyPrint: typeof actions.togglePrettyPrint,
   toggleBlackBox: typeof actions.toggleBlackBox,
   jumpToMappedLocation: typeof actions.jumpToMappedLocation,
   togglePaneCollapse: typeof actions.togglePaneCollapse
@@ -78,17 +80,17 @@ class SourceFooter extends PureComponent
     if (toggle === true) {
       eventDoc.CodeMirror.on("cursorActivity", this.onCursorChange);
     } else {
       eventDoc.CodeMirror.off("cursorActivity", this.onCursorChange);
     }
   }
 
   prettyPrintButton() {
-    const { selectedSource, togglePrettyPrint } = this.props;
+    const { cx, selectedSource, togglePrettyPrint } = this.props;
 
     if (!selectedSource) {
       return;
     }
 
     if (isLoading(selectedSource) && selectedSource.isPrettyPrinted) {
       return (
         <div className="loader" key="pretty-loader">
@@ -102,32 +104,32 @@ class SourceFooter extends PureComponent
     }
 
     const tooltip = L10N.getStr("sourceTabs.prettyPrint");
     const sourceLoaded = selectedSource && isLoaded(selectedSource);
 
     const type = "prettyPrint";
     return (
       <button
-        onClick={() => togglePrettyPrint(selectedSource.id)}
+        onClick={() => togglePrettyPrint(cx, selectedSource.id)}
         className={classnames("action", type, {
           active: sourceLoaded,
           pretty: isPretty(selectedSource)
         })}
         key={type}
         title={tooltip}
         aria-label={tooltip}
       >
         <AccessibleImage className={type} />
       </button>
     );
   }
 
   blackBoxButton() {
-    const { selectedSource, toggleBlackBox } = this.props;
+    const { cx, selectedSource, toggleBlackBox } = this.props;
     const sourceLoaded = selectedSource && isLoaded(selectedSource);
 
     if (!selectedSource) {
       return;
     }
 
     if (!shouldBlackbox(selectedSource)) {
       return;
@@ -138,17 +140,17 @@ class SourceFooter extends PureComponent
     const tooltip = blackboxed
       ? L10N.getStr("sourceFooter.unblackbox")
       : L10N.getStr("sourceFooter.blackbox");
 
     const type = "black-box";
 
     return (
       <button
-        onClick={() => toggleBlackBox(selectedSource)}
+        onClick={() => toggleBlackBox(cx, selectedSource)}
         className={classnames("action", type, {
           active: sourceLoaded,
           blackboxed: blackboxed
         })}
         key={type}
         title={tooltip}
         aria-label={tooltip}
       >
@@ -177,17 +179,22 @@ class SourceFooter extends PureComponent
     const commands = [this.blackBoxButton(), this.prettyPrintButton()].filter(
       Boolean
     );
 
     return commands.length ? <div className="commands">{commands}</div> : null;
   }
 
   renderSourceSummary() {
-    const { mappedSource, jumpToMappedLocation, selectedSource } = this.props;
+    const {
+      cx,
+      mappedSource,
+      jumpToMappedLocation,
+      selectedSource
+    } = this.props;
 
     if (!mappedSource || !isOriginal(selectedSource)) {
       return null;
     }
 
     const filename = getFilename(mappedSource);
     const tooltip = L10N.getFormatStr(
       "sourceFooter.mappedSourceTooltip",
@@ -197,17 +204,17 @@ class SourceFooter extends PureComponent
     const mappedSourceLocation = {
       sourceId: selectedSource.id,
       line: 1,
       column: 1
     };
     return (
       <button
         className="mapped-source"
-        onClick={() => jumpToMappedLocation(mappedSourceLocation)}
+        onClick={() => jumpToMappedLocation(cx, mappedSourceLocation)}
         title={tooltip}
       >
         <span>{title}</span>
       </button>
     );
   }
 
   onCursorChange = event => {
@@ -250,16 +257,17 @@ class SourceFooter extends PureComponent
     );
   }
 }
 
 const mapStateToProps = state => {
   const selectedSource = getSelectedSource(state);
 
   return {
+    cx: getContext(state),
     selectedSource,
     mappedSource: getGeneratedSource(state, selectedSource),
     prettySource: getPrettySource(
       state,
       selectedSource ? selectedSource.id : null
     ),
     endPanelCollapsed: getPaneCollapse(state, "end")
   };
--- a/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js
+++ b/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js
@@ -19,31 +19,34 @@ const { ObjectInspector, utils } = objec
 const {
   node: { createNode, getChildren, getValue, nodeIsPrimitive },
   loadProperties: { loadItemProperties }
 } = utils;
 
 import actions from "../../../actions";
 import {
   getAllPopupObjectProperties,
-  getCurrentThread
+  getCurrentThread,
+  getThreadContext
 } from "../../../selectors";
 import Popover from "../../shared/Popover";
 import PreviewFunction from "../../shared/PreviewFunction";
 
 import AccessibleImage from "../../shared/AccessibleImage";
 import { createObjectClient } from "../../../client/firefox";
 
 import "./Popup.css";
 
 import type { EditorRange } from "../../../utils/editor/types";
 import type { Coords } from "../../shared/Popover";
+import type { ThreadContext } from "../../../types";
 
 type PopupValue = Object | null;
 type Props = {
+  cx: ThreadContext,
   popupObjectProperties: Object,
   popoverPos: Object,
   value: PopupValue,
   expression: string,
   onClose: () => void,
   range: EditorRange,
   editor: any,
   editorRef: ?HTMLDivElement,
@@ -87,33 +90,34 @@ export class Popup extends Component<Pro
     super(props);
     this.state = {
       top: 0
     };
   }
 
   async componentWillMount() {
     const {
+      cx,
       value,
       setPopupObjectProperties,
       popupObjectProperties
     } = this.props;
 
     const root = this.getRoot();
 
     if (
       !nodeIsPrimitive(root) &&
       value &&
       value.actor &&
       !popupObjectProperties[value.actor]
     ) {
       const onLoadItemProperties = loadItemProperties(root, createObjectClient);
       if (onLoadItemProperties !== null) {
         const properties = await onLoadItemProperties;
-        setPopupObjectProperties(root.contents.value, properties);
+        setPopupObjectProperties(cx, root.contents.value, properties);
       }
     }
   }
 
   onMouseLeave = (e: SyntheticMouseEvent<HTMLDivElement>) => {
     const relatedTarget: Element = (e.relatedTarget: any);
 
     if (!relatedTarget) {
@@ -176,27 +180,29 @@ export class Popup extends Component<Pro
     const { editorRef } = this.props;
     if (!editorRef) {
       return "auto";
     }
     return editorRef.getBoundingClientRect().height - this.state.top;
   };
 
   renderFunctionPreview() {
-    const { selectSourceURL, value } = this.props;
+    const { cx, selectSourceURL, value } = this.props;
 
     if (!value) {
       return null;
     }
 
     const { location } = value;
     return (
       <div
         className="preview-popup"
-        onClick={() => selectSourceURL(location.url, { line: location.line })}
+        onClick={() =>
+          selectSourceURL(cx, location.url, { line: location.line })
+        }
       >
         <PreviewFunction func={value} />
       </div>
     );
   }
 
   renderReact(react: Object) {
     const reactHeader = react.displayName || "React Component";
@@ -325,16 +331,17 @@ export class Popup extends Component<Pro
       >
         {this.renderPreview()}
       </Popover>
     );
   }
 }
 
 const mapStateToProps = state => ({
+  cx: getThreadContext(state),
   popupObjectProperties: getAllPopupObjectProperties(
     state,
     getCurrentThread(state)
   )
 });
 
 const {
   addExpression,
--- a/devtools/client/debugger/new/src/components/Editor/Preview/index.js
+++ b/devtools/client/debugger/new/src/components/Editor/Preview/index.js
@@ -7,32 +7,31 @@
 import React, { PureComponent } from "react";
 import { connect } from "../../../utils/connect";
 
 import Popup from "./Popup";
 
 import {
   getPreview,
   getSelectedSource,
-  getIsPaused,
-  getCurrentThread
+  getThreadContext
 } from "../../../selectors";
 import actions from "../../../actions";
 import { toEditorRange } from "../../../utils/editor";
 
-import type { Source } from "../../../types";
+import type { Source, ThreadContext } from "../../../types";
 
 import type { Preview as PreviewType } from "../../../reducers/ast";
 
 type Props = {
+  cx: ThreadContext,
   editor: any,
   editorRef: ?HTMLDivElement,
   selectedSource: Source,
   preview: PreviewType,
-  isPaused: boolean,
   clearPreview: typeof actions.clearPreview,
   setPopupObjectProperties: typeof actions.setPopupObjectProperties,
   addExpression: typeof actions.addExpression,
   updatePreview: typeof actions.updatePreview
 };
 
 type State = {
   selecting: boolean
@@ -108,50 +107,51 @@ class Preview extends PureComponent<Prop
 
     if (prevProps.preview && !prevProps.preview.updating) {
       const target = getElementFromPos(prevProps.preview.cursorPos);
       target && target.classList.remove("preview-selection");
     }
   }
 
   onTokenEnter = ({ target, tokenPos }) => {
-    if (this.props.isPaused) {
-      this.props.updatePreview(target, tokenPos, this.props.editor.codeMirror);
+    const { cx, updatePreview, editor } = this.props;
+    if (cx.isPaused) {
+      updatePreview(cx, target, tokenPos, editor.codeMirror);
     }
   };
 
   onTokenLeave = e => {
-    if (this.props.isPaused && !inPopup(e)) {
-      this.props.clearPreview();
+    if (this.props.cx.isPaused && !inPopup(e)) {
+      this.props.clearPreview(this.props.cx);
     }
   };
 
   onMouseUp = () => {
-    if (this.props.isPaused) {
+    if (this.props.cx.isPaused) {
       this.setState({ selecting: false });
       return true;
     }
   };
 
   onMouseDown = () => {
-    if (this.props.isPaused) {
+    if (this.props.cx.isPaused) {
       this.setState({ selecting: true });
       return true;
     }
   };
 
   onScroll = () => {
-    if (this.props.isPaused) {
-      this.props.clearPreview();
+    if (this.props.cx.isPaused) {
+      this.props.clearPreview(this.props.cx);
     }
   };
 
   onClose = e => {
-    if (this.props.isPaused) {
-      this.props.clearPreview();
+    if (this.props.cx.isPaused) {
+      this.props.clearPreview(this.props.cx);
     }
   };
 
   render() {
     const { selectedSource, preview } = this.props;
     if (!this.props.editor || !selectedSource || this.state.selecting) {
       return null;
     }
@@ -178,18 +178,18 @@ class Preview extends PureComponent<Prop
         popoverPos={cursorPos}
         onClose={this.onClose}
       />
     );
   }
 }
 
 const mapStateToProps = state => ({
+  cx: getThreadContext(state),
   preview: getPreview(state),
-  isPaused: getIsPaused(state, getCurrentThread(state)),
   selectedSource: getSelectedSource(state)
 });
 
 export default connect(
   mapStateToProps,
   {
     clearPreview: actions.clearPreview,
     setPopupObjectProperties: actions.setPopupObjectProperties,
--- a/devtools/client/debugger/new/src/components/Editor/SearchBar.js
+++ b/devtools/client/debugger/new/src/components/Editor/SearchBar.js
@@ -12,25 +12,26 @@ import AccessibleImage from "../shared/A
 import actions from "../../actions";
 import {
   getActiveSearch,
   getSelectedSource,
   getSelectedLocation,
   getFileSearchQuery,
   getFileSearchModifiers,
   getFileSearchResults,
-  getHighlightedLineRange
+  getHighlightedLineRange,
+  getContext
 } from "../../selectors";
 
 import { removeOverlay } from "../../utils/editor";
 
 import { scrollList } from "../../utils/result-list";
 import classnames from "classnames";
 
-import type { Source } from "../../types";
+import type { Source, Context } from "../../types";
 import type { Modifiers, SearchResults } from "../../reducers/file-search";
 
 import SearchInput from "../shared/SearchInput";
 import { debounce } from "lodash";
 import "./SearchBar.css";
 
 import type SourceEditor from "../../utils/editor/source-editor";
 
@@ -50,16 +51,17 @@ type State = {
   query: string,
   selectedResultIndex: number,
   count: number,
   index: number,
   inputFocused: boolean
 };
 
 type Props = {
+  cx: Context,
   editor: SourceEditor,
   selectedSource?: Source,
   searchOn: boolean,
   searchResults: SearchResults,
   modifiers: Modifiers,
   query: string,
   showClose?: boolean,
   size?: string,
@@ -132,20 +134,20 @@ class SearchBar extends Component<Props,
     const { editor: ed, query } = this.props;
     if (ed) {
       const ctx = { ed, cm: ed.codeMirror };
       removeOverlay(ctx, query);
     }
   };
 
   closeSearch = (e: SyntheticEvent<HTMLElement>) => {
-    const { closeFileSearch, editor, searchOn } = this.props;
+    const { cx, closeFileSearch, editor, searchOn } = this.props;
     if (editor && searchOn) {
       this.clearSearch();
-      closeFileSearch(editor);
+      closeFileSearch(cx, editor);
       e.stopPropagation();
       e.preventDefault();
     }
     this.setState({ query: "", inputFocused: false });
   };
 
   toggleSearch = (e: SyntheticKeyboardEvent<HTMLElement>) => {
     e.stopPropagation();
@@ -164,33 +166,33 @@ class SearchBar extends Component<Props,
         this.doSearch(query);
       } else {
         this.setState({ query: "", inputFocused: true });
       }
     }
   };
 
   doSearch = (query: string) => {
-    const { selectedSource } = this.props;
+    const { cx, selectedSource } = this.props;
     if (!selectedSource || !selectedSource.text) {
       return;
     }
 
-    this.props.doSearch(query, this.props.editor);
+    this.props.doSearch(cx, query, this.props.editor);
   };
 
   traverseResults = (e: SyntheticEvent<HTMLElement>, rev: boolean) => {
     e.stopPropagation();
     e.preventDefault();
     const editor = this.props.editor;
 
     if (!editor) {
       return;
     }
-    this.props.traverseResults(rev, editor);
+    this.props.traverseResults(this.props.cx, rev, editor);
   };
 
   // Handlers
 
   onChange = (e: SyntheticInputEvent<HTMLElement>) => {
     this.setState({ query: e.target.value });
 
     return this.doSearch(e.target.value);
@@ -237,33 +239,33 @@ class SearchBar extends Component<Props,
     if (index == -1) {
       return L10N.getFormatStr("sourceSearch.resultsSummary1", count);
     }
 
     return L10N.getFormatStr("editor.searchResults", matchIndex + 1, count);
   }
 
   renderSearchModifiers = () => {
-    const { modifiers, toggleFileSearchModifier, query } = this.props;
+    const { cx, modifiers, toggleFileSearchModifier, query } = this.props;
     const { doSearch } = this;
 
     function SearchModBtn({ modVal, className, svgName, tooltip }) {
       const preppedClass = classnames(className, {
         active: modifiers && modifiers.get(modVal)
       });
       return (
         <button
           className={preppedClass}
           onMouseDown={() => {
-            toggleFileSearchModifier(modVal);
+            toggleFileSearchModifier(cx, modVal);
             doSearch(query);
           }}
           onKeyDown={(e: any) => {
             if (e.key === "Enter") {
-              toggleFileSearchModifier(modVal);
+              toggleFileSearchModifier(cx, modVal);
               doSearch(query);
             }
           }}
           title={tooltip}
         >
           <AccessibleImage className={svgName} />
         </button>
       );
@@ -352,16 +354,17 @@ class SearchBar extends Component<Props,
   }
 }
 
 SearchBar.contextTypes = {
   shortcuts: PropTypes.object
 };
 
 const mapStateToProps = state => ({
+  cx: getContext(state),
   searchOn: getActiveSearch(state) === "file",
   selectedSource: getSelectedSource(state),
   selectedLocation: getSelectedLocation(state),
   query: getFileSearchQuery(state),
   modifiers: getFileSearchModifiers(state),
   highlightedLineRange: getHighlightedLineRange(state),
   searchResults: getFileSearchResults(state)
 });
--- a/devtools/client/debugger/new/src/components/Editor/Tab.js
+++ b/devtools/client/debugger/new/src/components/Editor/Tab.js
@@ -8,17 +8,17 @@ import React, { PureComponent } from "re
 import { connect } from "../../utils/connect";
 
 import { showMenu, buildMenu } from "devtools-contextmenu";
 
 import SourceIcon from "../shared/SourceIcon";
 import { CloseButton } from "../shared/Button";
 
 import type { List } from "immutable";
-import type { Source } from "../../types";
+import type { Source, Context } from "../../types";
 
 import actions from "../../actions";
 
 import {
   getDisplayPath,
   getFileURL,
   getRawSourceURL,
   getSourceQueryString,
@@ -28,25 +28,27 @@ import {
 } from "../../utils/source";
 import { copyToTheClipboard } from "../../utils/clipboard";
 import { getTabMenuItems } from "../../utils/tabs";
 
 import {
   getSelectedSource,
   getActiveSearch,
   getSourcesForTabs,
-  getHasSiblingOfSameName
+  getHasSiblingOfSameName,
+  getContext
 } from "../../selectors";
 import type { ActiveSearchType } from "../../selectors";
 
 import classnames from "classnames";
 
 type SourcesList = List<Source>;
 
 type Props = {
+  cx: Context,
   tabSources: SourcesList,
   selectedSource: Source,
   source: Source,
   activeSearch: ActiveSearchType,
   hasSiblingOfSameName: boolean,
   selectSource: typeof actions.selectSource,
   closeTab: typeof actions.closeTab,
   closeTabs: typeof actions.closeTabs,
@@ -58,16 +60,17 @@ type Props = {
 class Tab extends PureComponent<Props> {
   onTabContextMenu = (event, tab: string) => {
     event.preventDefault();
     this.showContextMenu(event, tab);
   };
 
   showContextMenu(e, tab: string) {
     const {
+      cx,
       closeTab,
       closeTabs,
       tabSources,
       showSource,
       toggleBlackBox,
       togglePrettyPrint,
       selectedSource,
       source
@@ -83,40 +86,43 @@ class Tab extends PureComponent<Props> {
       return;
     }
 
     const tabMenuItems = getTabMenuItems();
     const items = [
       {
         item: {
           ...tabMenuItems.closeTab,
-          click: () => closeTab(sourceTab)
+          click: () => closeTab(cx, sourceTab)
         }
       },
       {
         item: {
           ...tabMenuItems.closeOtherTabs,
-          click: () => closeTabs(otherTabURLs),
+          click: () => closeTabs(cx, otherTabURLs),
           disabled: otherTabURLs.length === 0
         }
       },
       {
         item: {
           ...tabMenuItems.closeTabsToEnd,
           click: () => {
             const tabIndex = tabSources.findIndex(t => t.id == tab);
-            closeTabs(tabURLs.filter((t, i) => i > tabIndex));
+            closeTabs(cx, tabURLs.filter((t, i) => i > tabIndex));
           },
           disabled:
             tabCount === 1 ||
             tabSources.some((t, i) => t === tab && tabCount - 1 === i)
         }
       },
       {
-        item: { ...tabMenuItems.closeAllTabs, click: () => closeTabs(tabURLs) }
+        item: {
+          ...tabMenuItems.closeAllTabs,
+          click: () => closeTabs(cx, tabURLs)
+        }
       },
       { item: { type: "separator" } },
       {
         item: {
           ...tabMenuItems.copyToClipboard,
           disabled: selectedSource.id !== tab,
           click: () => copyToTheClipboard(sourceTab.text)
         }
@@ -127,33 +133,33 @@ class Tab extends PureComponent<Props> {
           disabled: !selectedSource.url,
           click: () => copyToTheClipboard(getRawSourceURL(sourceTab.url))
         }
       },
       {
         item: {
           ...tabMenuItems.showSource,
           disabled: !selectedSource.url,
-          click: () => showSource(tab)
+          click: () => showSource(cx, tab)
         }
       },
       {
         item: {
           ...tabMenuItems.toggleBlackBox,
           label: source.isBlackBoxed
             ? L10N.getStr("sourceFooter.unblackbox")
             : L10N.getStr("sourceFooter.blackbox"),
           disabled: !shouldBlackbox(source),
-          click: () => toggleBlackBox(source)
+          click: () => toggleBlackBox(cx, source)
         }
       },
       {
         item: {
           ...tabMenuItems.prettyPrint,
-          click: () => togglePrettyPrint(tab),
+          click: () => togglePrettyPrint(cx, tab),
           disabled: isPretty(sourceTab)
         }
       }
     ];
 
     showMenu(e, buildMenu(items));
   }
 
@@ -162,56 +168,57 @@ class Tab extends PureComponent<Props> {
   }
 
   isSourceSearchEnabled() {
     return this.props.activeSearch === "source";
   }
 
   render() {
     const {
+      cx,
       selectedSource,
       selectSource,
       closeTab,
       source,
       tabSources,
       hasSiblingOfSameName
     } = this.props;
     const sourceId = source.id;
     const active =
       selectedSource &&
       sourceId == selectedSource.id &&
       (!this.isProjectSearchEnabled() && !this.isSourceSearchEnabled());
     const isPrettyCode = isPretty(source);
 
     function onClickClose(e) {
       e.stopPropagation();
-      closeTab(source);
+      closeTab(cx, source);
     }
 
     function handleTabClick(e) {
       e.preventDefault();
       e.stopPropagation();
-      return selectSource(sourceId);
+      return selectSource(cx, sourceId);
     }
 
     const className = classnames("source-tab", {
       active,
       pretty: isPrettyCode
     });
 
     const path = getDisplayPath(source, tabSources);
     const query = hasSiblingOfSameName ? getSourceQueryString(source) : "";
 
     return (
       <div
         className={className}
         key={sourceId}
         onClick={handleTabClick}
         // Accommodate middle click to close tab
-        onMouseUp={e => e.button === 1 && closeTab(source)}
+        onMouseUp={e => e.button === 1 && closeTab(cx, source)}
         onContextMenu={e => this.onTabContextMenu(e, sourceId)}
         title={getFileURL(source, false)}
       >
         <SourceIcon
           source={source}
           shouldHide={icon => ["file", "javascript"].includes(icon)}
         />
         <div className="filename">
@@ -226,16 +233,17 @@ class Tab extends PureComponent<Props> {
     );
   }
 }
 
 const mapStateToProps = (state, { source }) => {
   const selectedSource = getSelectedSource(state);
 
   return {
+    cx: getContext(state),
     tabSources: getSourcesForTabs(state),
     selectedSource: selectedSource,
     activeSearch: getActiveSearch(state),
     hasSiblingOfSameName: getHasSiblingOfSameName(state, source)
   };
 };
 
 export default connect(
--- a/devtools/client/debugger/new/src/components/Editor/Tabs.js
+++ b/devtools/client/debugger/new/src/components/Editor/Tabs.js
@@ -6,38 +6,40 @@
 
 import React, { PureComponent } from "react";
 import { connect } from "../../utils/connect";
 
 import {
   getSelectedSource,
   getSourcesForTabs,
   getIsPaused,
-  getCurrentThread
+  getCurrentThread,
+  getContext
 } from "../../selectors";
 import { isVisible } from "../../utils/ui";
 
 import { getHiddenTabs } from "../../utils/tabs";
 import { getFilename, isPretty } from "../../utils/source";
 import actions from "../../actions";
 
 import { debounce } from "lodash";
 import "./Tabs.css";
 
 import Tab from "./Tab";
 import { PaneToggleButton } from "../shared/Button";
 import Dropdown from "../shared/Dropdown";
 import AccessibleImage from "../shared/AccessibleImage";
 import CommandBar from "../SecondaryPanes/CommandBar";
 
-import type { Source } from "../../types";
+import type { Source, Context } from "../../types";
 
 type SourcesList = Source[];
 
 type Props = {
+  cx: Context,
   tabSources: SourcesList,
   selectedSource: ?Source,
   horizontal: boolean,
   startPanelCollapsed: boolean,
   endPanelCollapsed: boolean,
   moveTab: typeof actions.moveTab,
   closeTab: typeof actions.closeTab,
   togglePaneCollapse: typeof actions.togglePaneCollapse,
@@ -131,20 +133,20 @@ class Tabs extends PureComponent<Props, 
     }
     if (source.isBlackBoxed) {
       return "blackBox";
     }
     return "file";
   }
 
   renderDropdownSource = (source: Source) => {
-    const { selectSource } = this.props;
+    const { cx, selectSource } = this.props;
     const filename = getFilename(source);
 
-    const onClick = () => selectSource(source.id);
+    const onClick = () => selectSource(cx, source.id);
     return (
       <li key={source.id} onClick={onClick}>
         <AccessibleImage
           className={`dropdown-icon ${this.getIconClass(source)}`}
         />
         <span className="dropdown-label">{filename}</span>
       </li>
     );
@@ -221,16 +223,17 @@ class Tabs extends PureComponent<Props, 
         {this.renderEndPanelToggleButton()}
         {this.renderCommandBar()}
       </div>
     );
   }
 }
 
 const mapStateToProps = state => ({
+  cx: getContext(state),
   selectedSource: getSelectedSource(state),
   tabSources: getSourcesForTabs(state),
   isPaused: getIsPaused(state, getCurrentThread(state))
 });
 
 export default connect(
   mapStateToProps,
   {
--- a/devtools/client/debugger/new/src/components/Editor/index.js
+++ b/devtools/client/debugger/new/src/components/Editor/index.js
@@ -30,17 +30,18 @@ import type { EditorItemActions } from "
 
 import {
   getActiveSearch,
   getSelectedLocation,
   getSelectedSource,
   getConditionalPanelLocation,
   getSymbols,
   getIsPaused,
-  getCurrentThread
+  getCurrentThread,
+  getThreadContext
 } from "../../selectors";
 
 // Redux actions
 import actions from "../../actions";
 
 import SearchBar from "./SearchBar";
 import HighlightLines from "./HighlightLines";
 import Preview from "./Preview";
@@ -74,23 +75,24 @@ import {
 import { resizeToggleButton, resizeBreakpointGutter } from "../../utils/ui";
 
 import "./Editor.css";
 import "./Breakpoints.css";
 import "./Highlight.css";
 
 import type SourceEditor from "../../utils/editor/source-editor";
 import type { SymbolDeclarations } from "../../workers/parser";
-import type { SourceLocation, Source } from "../../types";
+import type { SourceLocation, Source, ThreadContext } from "../../types";
 
 const cssVars = {
   searchbarHeight: "var(--editor-searchbar-height)"
 };
 
 export type Props = {
+  cx: ThreadContext,
   selectedLocation: ?SourceLocation,
   selectedSource: ?Source,
   searchOn: boolean,
   startPanelSize: number,
   endPanelSize: number,
   conditionalPanelLocation: SourceLocation,
   symbols: SymbolDeclarations,
   isPaused: boolean,
@@ -219,21 +221,21 @@ class Editor extends PureComponent<Props
     );
     shortcuts.on(L10N.getStr("sourceTabs.closeTab.key"), this.onClosePress);
     shortcuts.on("Esc", this.onEscape);
     shortcuts.on(searchAgainPrevKey, this.onSearchAgain);
     shortcuts.on(searchAgainKey, this.onSearchAgain);
   }
 
   onClosePress = (key, e: KeyboardEvent) => {
-    const { selectedSource } = this.props;
+    const { cx, selectedSource } = this.props;
     if (selectedSource) {
       e.preventDefault();
       e.stopPropagation();
-      this.props.closeTab(selectedSource);
+      this.props.closeTab(cx, selectedSource);
     }
   };
 
   componentWillUnmount() {
     if (this.state.editor) {
       this.state.editor.destroy();
       this.state.editor.codeMirror.off("scroll", this.onEditorScroll);
       this.setState({ editor: (null: any) });
@@ -267,17 +269,17 @@ class Editor extends PureComponent<Props
     e.preventDefault();
     e.stopPropagation();
 
     const line = this.getCurrentLine();
     if (typeof line !== "number") {
       return;
     }
 
-    this.props.toggleBreakpointAtLine(line);
+    this.props.toggleBreakpointAtLine(this.props.cx, line);
   };
 
   onToggleConditionalPanel = (key, e: KeyboardEvent) => {
     e.stopPropagation();
     e.preventDefault();
     const line = this.getCurrentLine();
 
     if (typeof line !== "number") {
@@ -321,24 +323,25 @@ class Editor extends PureComponent<Props
     const { codeMirror } = this.state.editor;
     if (codeMirror.listSelections().length > 1) {
       codeMirror.execCommand("singleSelection");
       e.preventDefault();
     }
   };
 
   onSearchAgain = (_, e: KeyboardEvent) => {
-    this.props.traverseResults(e.shiftKey, this.state.editor);
+    this.props.traverseResults(this.props.cx, e.shiftKey, this.state.editor);
   };
 
   openMenu(event: MouseEvent) {
     event.stopPropagation();
     event.preventDefault();
 
     const {
+      cx,
       selectedSource,
       breakpointActions,
       editorActions,
       isPaused
     } = this.props;
     const { editor } = this.state;
     if (!selectedSource || !editor) {
       return;
@@ -351,19 +354,19 @@ class Editor extends PureComponent<Props
     if (typeof line != "number") {
       return;
     }
 
     const location = { line, column: undefined, sourceId };
 
     if (target.classList.contains("CodeMirror-linenumber")) {
       return showMenu(event, [
-        ...createBreakpointItems(location, breakpointActions),
+        ...createBreakpointItems(cx, location, breakpointActions),
         { type: "separator" },
-        continueToHereItem(location, isPaused, editorActions)
+        continueToHereItem(cx, location, isPaused, editorActions)
       ]);
     }
 
     if (target.getAttribute("id") === "columnmarker") {
       return;
     }
 
     this.setState({ contextMenu: event });
@@ -375,16 +378,17 @@ class Editor extends PureComponent<Props
 
   onGutterClick = (
     cm: Object,
     line: number,
     gutter: string,
     ev: MouseEvent
   ) => {
     const {
+      cx,
       selectedSource,
       conditionalPanelLocation,
       closeConditionalPanel,
       addBreakpointAtLine,
       continueToHere
     } = this.props;
 
     // ignore right clicks in the gutter
@@ -406,36 +410,36 @@ class Editor extends PureComponent<Props
     }
 
     const sourceLine = toSourceLine(selectedSource.id, line);
     if (typeof sourceLine !== "number") {
       return;
     }
 
     if (ev.metaKey) {
-      return continueToHere(sourceLine);
+      return continueToHere(cx, sourceLine);
     }
 
-    return addBreakpointAtLine(sourceLine);
+    return addBreakpointAtLine(cx, sourceLine);
   };
 
   onGutterContextMenu = (event: MouseEvent) => {
     return this.openMenu(event);
   };
 
   onClick(e: MouseEvent) {
-    const { selectedSource, jumpToMappedLocation } = this.props;
+    const { cx, selectedSource, jumpToMappedLocation } = this.props;
 
     if (selectedSource && e.metaKey && e.altKey) {
       const sourceLocation = getSourceLocationFromMouseEvent(
         this.state.editor,
         selectedSource,
         e
       );
-      jumpToMappedLocation(sourceLocation);
+      jumpToMappedLocation(cx, sourceLocation);
     }
   }
 
   toggleConditionalPanel = (line, log: boolean = false) => {
     const {
       conditionalPanelLocation,
       closeConditionalPanel,
       openConditionalPanel,
@@ -563,29 +567,29 @@ class Editor extends PureComponent<Props
     }
 
     return {
       height: "100%"
     };
   }
 
   renderItems() {
-    const { selectedSource, conditionalPanelLocation } = this.props;
+    const { cx, selectedSource, conditionalPanelLocation } = this.props;
     const { editor, contextMenu } = this.state;
 
     if (!selectedSource || !editor || !getDocument(selectedSource.id)) {
       return null;
     }
 
     return (
       <div>
         <DebugLine editor={editor} />
         <HighlightLine />
         <EmptyLines editor={editor} />
-        <Breakpoints editor={editor} />
+        <Breakpoints editor={editor} cx={cx} />
         <Preview editor={editor} editorRef={this.$editorWrapper} />
         <HighlightLines editor={editor} />
         {
           <EditorMenu
             editor={editor}
             contextMenu={contextMenu}
             clearContextMenu={this.clearContextMenu}
             selectedSource={selectedSource}
@@ -632,16 +636,17 @@ class Editor extends PureComponent<Props
 Editor.contextTypes = {
   shortcuts: PropTypes.object
 };
 
 const mapStateToProps = state => {
   const selectedSource = getSelectedSource(state);
 
   return {
+    cx: getThreadContext(state),
     selectedLocation: getSelectedLocation(state),
     selectedSource,
     searchOn: getActiveSearch(state) === "file",
     conditionalPanelLocation: getConditionalPanelLocation(state),
     symbols: getSymbols(state, selectedSource),
     isPaused: getIsPaused(state, getCurrentThread(state))
   };
 };
--- a/devtools/client/debugger/new/src/components/Editor/menus/breakpoints.js
+++ b/devtools/client/debugger/new/src/components/Editor/menus/breakpoints.js
@@ -1,40 +1,42 @@
 /* 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 actions from "../../../actions";
 import { bindActionCreators } from "redux";
-import type { SourceLocation, Breakpoint } from "../../../types";
+import type { SourceLocation, Breakpoint, Context } from "../../../types";
 import { features } from "../../../utils/prefs";
 
 export const addBreakpointItem = (
+  cx: Context,
   location: SourceLocation,
   breakpointActions: BreakpointItemActions
 ) => ({
   id: "node-menu-add-breakpoint",
   label: L10N.getStr("editor.addBreakpoint"),
   accesskey: L10N.getStr("shortcuts.toggleBreakpoint.accesskey"),
   disabled: false,
-  click: () => breakpointActions.addBreakpoint(location),
+  click: () => breakpointActions.addBreakpoint(cx, location),
   accelerator: L10N.getStr("toggleBreakpoint.key")
 });
 
 export const removeBreakpointItem = (
+  cx: Context,
   breakpoint: Breakpoint,
   breakpointActions: BreakpointItemActions
 ) => ({
   id: "node-menu-remove-breakpoint",
   label: L10N.getStr("editor.removeBreakpoint"),
   accesskey: L10N.getStr("shortcuts.toggleBreakpoint.accesskey"),
   disabled: false,
-  click: () => breakpointActions.removeBreakpoint(breakpoint),
+  click: () => breakpointActions.removeBreakpoint(cx, breakpoint),
   accelerator: L10N.getStr("toggleBreakpoint.key")
 });
 
 export const addConditionalBreakpointItem = (
   location: SourceLocation,
   breakpointActions: BreakpointItemActions
 ) => ({
   id: "node-menu-add-conditional-breakpoint",
@@ -103,114 +105,140 @@ export const logPointItem = (
     location
   } = breakpoint;
   return logValue
     ? editLogPointItem(location, breakpointActions)
     : addLogPointItem(location, breakpointActions);
 };
 
 export const toggleDisabledBreakpointItem = (
+  cx: Context,
   breakpoint: Breakpoint,
   breakpointActions: BreakpointItemActions
 ) => {
   return {
     accesskey: L10N.getStr("editor.disableBreakpoint.accesskey"),
     disabled: false,
-    click: () => breakpointActions.toggleDisabledBreakpoint(breakpoint),
+    click: () => breakpointActions.toggleDisabledBreakpoint(cx, breakpoint),
     ...(breakpoint.disabled
       ? {
           id: "node-menu-enable-breakpoint",
           label: L10N.getStr("editor.enableBreakpoint")
         }
       : {
           id: "node-menu-disable-breakpoint",
           label: L10N.getStr("editor.disableBreakpoint")
         })
   };
 };
 
 export function breakpointItems(
+  cx: Context,
   breakpoint: Breakpoint,
   breakpointActions: BreakpointItemActions
 ) {
   const items = [
-    removeBreakpointItem(breakpoint, breakpointActions),
-    toggleDisabledBreakpointItem(breakpoint, breakpointActions)
+    removeBreakpointItem(cx, breakpoint, breakpointActions),
+    toggleDisabledBreakpointItem(cx, breakpoint, breakpointActions)
   ];
 
   if (features.columnBreakpoints) {
     items.push(
       { type: "separator" },
-      removeBreakpointsOnLineItem(breakpoint.location, breakpointActions),
+      removeBreakpointsOnLineItem(cx, breakpoint.location, breakpointActions),
       breakpoint.disabled
-        ? enableBreakpointsOnLineItem(breakpoint.location, breakpointActions)
-        : disableBreakpointsOnLineItem(breakpoint.location, breakpointActions),
+        ? enableBreakpointsOnLineItem(
+            cx,
+            breakpoint.location,
+            breakpointActions
+          )
+        : disableBreakpointsOnLineItem(
+            cx,
+            breakpoint.location,
+            breakpointActions
+          ),
       { type: "separator" }
     );
   }
 
   items.push(conditionalBreakpointItem(breakpoint, breakpointActions));
 
   if (features.logPoints) {
     items.push(logPointItem(breakpoint, breakpointActions));
   }
 
   return items;
 }
 
 export function createBreakpointItems(
+  cx: Context,
   location: SourceLocation,
   breakpointActions: BreakpointItemActions
 ) {
   const items = [
-    addBreakpointItem(location, breakpointActions),
+    addBreakpointItem(cx, location, breakpointActions),
     addConditionalBreakpointItem(location, breakpointActions)
   ];
 
   if (features.logPoints) {
     items.push(addLogPointItem(location, breakpointActions));
   }
   return items;
 }
 
 // ToDo: Only enable if there are more than one breakpoints on a line?
 export const removeBreakpointsOnLineItem = (
+  cx: Context,
   location: SourceLocation,
   breakpointActions: BreakpointItemActions
 ) => ({
   id: "node-menu-remove-breakpoints-on-line",
   label: L10N.getStr("breakpointMenuItem.removeAllAtLine.label"),
   accesskey: L10N.getStr("breakpointMenuItem.removeAllAtLine.accesskey"),
   disabled: false,
   click: () =>
-    breakpointActions.removeBreakpointsAtLine(location.sourceId, location.line)
+    breakpointActions.removeBreakpointsAtLine(
+      cx,
+      location.sourceId,
+      location.line
+    )
 });
 
 export const enableBreakpointsOnLineItem = (
+  cx: Context,
   location: SourceLocation,
   breakpointActions: BreakpointItemActions
 ) => ({
   id: "node-menu-remove-breakpoints-on-line",
   label: L10N.getStr("breakpointMenuItem.enableAllAtLine.label"),
   accesskey: L10N.getStr("breakpointMenuItem.enableAllAtLine.accesskey"),
   disabled: false,
   click: () =>
-    breakpointActions.enableBreakpointsAtLine(location.sourceId, location.line)
+    breakpointActions.enableBreakpointsAtLine(
+      cx,
+      location.sourceId,
+      location.line
+    )
 });
 
 export const disableBreakpointsOnLineItem = (
+  cx: Context,
   location: SourceLocation,
   breakpointActions: BreakpointItemActions
 ) => ({
   id: "node-menu-remove-breakpoints-on-line",
   label: L10N.getStr("breakpointMenuItem.disableAllAtLine.label"),
   accesskey: L10N.getStr("breakpointMenuItem.disableAllAtLine.accesskey"),
   disabled: false,
   click: () =>
-    breakpointActions.disableBreakpointsAtLine(location.sourceId, location.line)
+    breakpointActions.disableBreakpointsAtLine(
+      cx,
+      location.sourceId,
+      location.line
+    )
 });
 
 export type BreakpointItemActions = {
   addBreakpoint: typeof actions.addBreakpoint,
   removeBreakpoint: typeof actions.removeBreakpoint,
   removeBreakpointsAtLine: typeof actions.removeBreakpointsAtLine,
   enableBreakpointsAtLine: typeof actions.enableBreakpointsAtLine,
   disableBreakpointsAtLine: typeof actions.disableBreakpointsAtLine,
--- a/devtools/client/debugger/new/src/components/Editor/menus/editor.js
+++ b/devtools/client/debugger/new/src/components/Editor/menus/editor.js
@@ -14,30 +14,36 @@ import {
   getFilename,
   shouldBlackbox
 } from "../../../utils/source";
 
 import { downloadFile } from "../../../utils/utils";
 
 import actions from "../../../actions";
 
-import type { Source, SourceLocation } from "../../../types";
+import type {
+  Source,
+  SourceLocation,
+  Context,
+  ThreadContext
+} from "../../../types";
 
 function isMapped(selectedSource) {
   return isOriginalId(selectedSource.id) || !!selectedSource.sourceMapURL;
 }
 
 export const continueToHereItem = (
+  cx: ThreadContext,
   location: SourceLocation,
   isPaused: boolean,
   editorActions: EditorItemActions
 ) => ({
   accesskey: L10N.getStr("editor.continueToHere.accesskey"),
   disabled: !isPaused,
-  click: () => editorActions.continueToHere(location.line, location.column),
+  click: () => editorActions.continueToHere(cx, location.line, location.column),
   id: "node-menu-continue-to-here",
   label: L10N.getStr("editor.continueToHere.label")
 });
 
 // menu items
 
 const copyToClipboardItem = (
   selectedSource: Source,
@@ -80,67 +86,71 @@ const copySourceUri2Item = (
   id: "node-menu-copy-source-url",
   label: L10N.getStr("copySourceUri2"),
   accesskey: L10N.getStr("copySourceUri2.accesskey"),
   disabled: !selectedSource.url,
   click: () => copyToTheClipboard(getRawSourceURL(selectedSource.url))
 });
 
 const jumpToMappedLocationItem = (
+  cx: Context,
   selectedSource: Source,
   location: SourceLocation,
   hasPrettySource: boolean,
   editorActions: EditorItemActions
 ) => ({
   id: "node-menu-jump",
   label: L10N.getFormatStr(
     "editor.jumpToMappedLocation1",
     isOriginalId(selectedSource.id)
       ? L10N.getStr("generated")
       : L10N.getStr("original")
   ),
   accesskey: L10N.getStr("editor.jumpToMappedLocation1.accesskey"),
   disabled:
     (!isMapped(selectedSource) && !isPretty(selectedSource)) || hasPrettySource,
-  click: () => editorActions.jumpToMappedLocation(location)
+  click: () => editorActions.jumpToMappedLocation(cx, location)
 });
 
 const showSourceMenuItem = (
+  cx: Context,
   selectedSource: Source,
   editorActions: EditorItemActions
 ) => ({
   id: "node-menu-show-source",
   label: L10N.getStr("sourceTabs.revealInTree"),
   accesskey: L10N.getStr("sourceTabs.revealInTree.accesskey"),
   disabled: !selectedSource.url,
-  click: () => editorActions.showSource(selectedSource.id)
+  click: () => editorActions.showSource(cx, selectedSource.id)
 });
 
 const blackBoxMenuItem = (
+  cx: Context,
   selectedSource: Source,
   editorActions: EditorItemActions
 ) => ({
   id: "node-menu-blackbox",
   label: selectedSource.isBlackBoxed
     ? L10N.getStr("sourceFooter.unblackbox")
     : L10N.getStr("sourceFooter.blackbox"),
   accesskey: L10N.getStr("sourceFooter.blackbox.accesskey"),
   disabled: !shouldBlackbox(selectedSource),
-  click: () => editorActions.toggleBlackBox(selectedSource)
+  click: () => editorActions.toggleBlackBox(cx, selectedSource)
 });
 
 const watchExpressionItem = (
+  cx: ThreadContext,
   selectedSource: Source,
   selectionText: string,
   editorActions: EditorItemActions
 ) => ({
   id: "node-menu-add-watch-expression",
   label: L10N.getStr("expressions.label"),
   accesskey: L10N.getStr("expressions.accesskey"),
-  click: () => editorActions.addExpression(selectionText)
+  click: () => editorActions.addExpression(cx, selectionText)
 });
 
 const evaluateInConsoleItem = (
   selectedSource: Source,
   selectionText: string,
   editorActions: EditorItemActions
 ) => ({
   id: "node-menu-evaluate-in-console",
@@ -156,56 +166,59 @@ const downloadFileItem = (
     id: "node-menu-download-file",
     label: L10N.getStr("downloadFile.label"),
     accesskey: L10N.getStr("downloadFile.accesskey"),
     click: () => downloadFile(selectedSource, getFilename(selectedSource))
   };
 };
 
 export function editorMenuItems({
+  cx,
   editorActions,
   selectedSource,
   location,
   selectionText,
   hasPrettySource,
   isTextSelected,
   isPaused
 }: {
+  cx: ThreadContext,
   editorActions: EditorItemActions,
   selectedSource: Source,
   location: SourceLocation,
   selectionText: string,
   hasPrettySource: boolean,
   isTextSelected: boolean,
   isPaused: boolean
 }) {
   const items = [];
 
   items.push(
     jumpToMappedLocationItem(
+      cx,
       selectedSource,
       location,
       hasPrettySource,
       editorActions
     ),
-    continueToHereItem(location, isPaused, editorActions),
+    continueToHereItem(cx, location, isPaused, editorActions),
     { type: "separator" },
     copyToClipboardItem(selectedSource, editorActions),
     copySourceItem(selectedSource, selectionText, editorActions),
     copySourceUri2Item(selectedSource, editorActions),
     downloadFileItem(selectedSource, editorActions),
     { type: "separator" },
-    showSourceMenuItem(selectedSource, editorActions),
-    blackBoxMenuItem(selectedSource, editorActions)
+    showSourceMenuItem(cx, selectedSource, editorActions),
+    blackBoxMenuItem(cx, selectedSource, editorActions)
   );
 
   if (isTextSelected) {
     items.push(
       { type: "separator" },
-      watchExpressionItem(selectedSource, selectionText, editorActions),
+      watchExpressionItem(cx, selectedSource, selectionText, editorActions),
       evaluateInConsoleItem(selectedSource, selectionText, editorActions)
     );
   }
 
   return items;
 }
 
 export type EditorItemActions = {
--- a/devtools/client/debugger/new/src/components/Editor/tests/SearchBar.spec.js
+++ b/devtools/client/debugger/new/src/components/Editor/tests/SearchBar.spec.js
@@ -62,17 +62,17 @@ describe("SearchBar", () => {
 
 describe("doSearch", () => {
   it("should complete a search", async () => {
     const { component, props } = render();
     component
       .find("SearchInput")
       .simulate("change", { target: { value: "query" } });
 
-    const doSearchArgs = props.doSearch.mock.calls[0][0];
+    const doSearchArgs = props.doSearch.mock.calls[0][1];
     expect(doSearchArgs).toMatchSnapshot();
   });
 });
 
 describe("showErrorEmoji", () => {
   it("true if query + no results", () => {
     const { component } = render({
       query: "test",
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/Outline.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/Outline.js
@@ -11,33 +11,35 @@ import { score as fuzzaldrinScore } from
 
 import { copyToTheClipboard } from "../../utils/clipboard";
 import { findFunctionText } from "../../utils/function";
 
 import actions from "../../actions";
 import {
   getSelectedSource,
   getSymbols,
-  getSelectedLocation
+  getSelectedLocation,
+  getContext
 } from "../../selectors";
 
 import OutlineFilter from "./OutlineFilter";
 import "./Outline.css";
 import PreviewFunction from "../shared/PreviewFunction";
 import { uniq, sortBy } from "lodash";
 
 import type {
   AstLocation,
   SymbolDeclarations,
   SymbolDeclaration,
   FunctionDeclaration
 } from "../../workers/parser";
-import type { Source } from "../../types";
+import type { Source, Context } from "../../types";
 
 type Props = {
+  cx: Context,
   symbols: SymbolDeclarations,
   selectedSource: ?Source,
   alphabetizeOutline: boolean,
   onAlphabetizeClick: Function,
   selectedLocation: any,
   getFunctionText: Function,
   selectLocation: typeof actions.selectLocation,
   flashLineRange: typeof actions.flashLineRange
@@ -67,22 +69,22 @@ const filterOutlineItem = (name: string,
 export class Outline extends Component<Props, State> {
   constructor(props: Props) {
     super(props);
     (this: any).updateFilter = this.updateFilter.bind(this);
     this.state = { filter: "" };
   }
 
   selectItem(location: AstLocation) {
-    const { selectedSource, selectLocation } = this.props;
+    const { cx, selectedSource, selectLocation } = this.props;
     if (!selectedSource) {
       return;
     }
 
-    selectLocation({
+    selectLocation(cx, {
       sourceId: selectedSource.id,
       line: location.start.line,
       column: location.start.column
     });
   }
 
   onContextMenu(event: SyntheticEvent<HTMLElement>, func: SymbolDeclaration) {
     event.stopPropagation();
@@ -259,16 +261,17 @@ export class Outline extends Component<P
   }
 }
 
 const mapStateToProps = state => {
   const selectedSource = getSelectedSource(state);
   const symbols = selectedSource ? getSymbols(state, selectedSource) : null;
 
   return {
+    cx: getContext(state),
     symbols,
     selectedSource,
     selectedLocation: getSelectedLocation(state),
     getFunctionText: line => {
       if (selectedSource) {
         return findFunctionText(line, selectedSource, symbols);
       }
 
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
@@ -14,17 +14,18 @@ import {
   getShownSource,
   getSelectedSource,
   getDebuggeeUrl,
   getExpandedState,
   getProjectDirectoryRoot,
   getDisplayedSourcesForThread,
   getFocusedSourceItem,
   getWorkerByThread,
-  getWorkerCount
+  getWorkerCount,
+  getContext
 } from "../../selectors";
 
 import { getGeneratedSourceByURL } from "../../reducers/sources";
 
 // Actions
 import actions from "../../actions";
 
 // Components
@@ -46,21 +47,22 @@ import { getRawSourceURL } from "../../u
 import { getDisplayName } from "../../utils/workers";
 import { features } from "../../utils/prefs";
 
 import type {
   TreeNode,
   TreeDirectory,
   ParentMap
 } from "../../utils/sources-tree/types";
-import type { Worker, Source } from "../../types";
+import type { Worker, Source, Context } from "../../types";
 import type { SourcesMap, State as AppState } from "../../reducers/types";
 import type { Item } from "../shared/ManagedTree";
 
 type Props = {
+  cx: Context,
   thread: string,
   worker: Worker,
   sources: SourcesMap,
   sourceCount: number,
   shownSource?: Source,
   selectedSource?: Source,
   debuggeeUrl: string,
   projectRoot: string,
@@ -151,22 +153,22 @@ class SourcesTree extends Component<Prop
           sourceTree
         })
       );
     }
   }
 
   selectItem = (item: TreeNode) => {
     if (item.type == "source" && !Array.isArray(item.contents)) {
-      this.props.selectSource(item.contents.id);
+      this.props.selectSource(this.props.cx, item.contents.id);
     }
   };
 
   onFocus = (item: TreeNode) => {
-    this.props.focusItem({ thread: this.props.thread, item });
+    this.props.focusItem(this.props.cx, { thread: this.props.thread, item });
   };
 
   onActivate = (item: TreeNode) => {
     this.selectItem(item);
   };
 
   // NOTE: we get the source from sources because item.contents is cached
   getSource(item: TreeNode): ?Source {
@@ -366,16 +368,17 @@ function getSourceForTree(
 const mapStateToProps = (state, props) => {
   const selectedSource = getSelectedSource(state);
   const shownSource = getShownSource(state);
   const focused = getFocusedSourceItem(state);
   const thread = props.thread;
   const displayedSources = getDisplayedSourcesForThread(state, thread);
 
   return {
+    cx: getContext(state),
     shownSource: getSourceForTree(state, displayedSources, shownSource, thread),
     selectedSource: getSourceForTree(
       state,
       displayedSources,
       selectedSource,
       thread
     ),
     debuggeeUrl: getDebuggeeUrl(state),
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTreeItem.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTreeItem.js
@@ -10,34 +10,36 @@ import classnames from "classnames";
 import { showMenu } from "devtools-contextmenu";
 
 import SourceIcon from "../shared/SourceIcon";
 import AccessibleImage from "../shared/AccessibleImage";
 
 import {
   getGeneratedSourceByURL,
   getHasSiblingOfSameName,
-  hasPrettySource as checkHasPrettySource
+  hasPrettySource as checkHasPrettySource,
+  getContext
 } from "../../selectors";
 import actions from "../../actions";
 
 import {
   isOriginal as isOriginalSource,
   getSourceQueryString,
   isUrlExtension,
   shouldBlackbox
 } from "../../utils/source";
 import { isDirectory } from "../../utils/sources-tree";
 import { copyToTheClipboard } from "../../utils/clipboard";
 import { features } from "../../utils/prefs";
 
 import type { TreeNode } from "../../utils/sources-tree/types";
-import type { Source } from "../../types";
+import type { Source, Context } from "../../types";
 
 type Props = {
+  cx: Context,
   debuggeeUrl: string,
   projectRoot: string,
   source: ?Source,
   item: TreeNode,
   depth: number,
   focused: boolean,
   expanded: boolean,
   hasMatchingGeneratedSource: boolean,
@@ -129,53 +131,53 @@ class SourceTreeItem extends Component<P
         const copySourceUri2 = {
           id: "node-menu-copy-source",
           label: copySourceUri2Label,
           accesskey: copySourceUri2Key,
           disabled: false,
           click: () => copyToTheClipboard(contents.url)
         };
 
-        const { source } = this.props;
+        const { cx, source } = this.props;
         if (source) {
           const blackBoxMenuItem = {
             id: "node-menu-blackbox",
             label: source.isBlackBoxed
               ? L10N.getStr("sourceFooter.unblackbox")
               : L10N.getStr("sourceFooter.blackbox"),
             accesskey: L10N.getStr("sourceFooter.blackbox.accesskey"),
             disabled: !shouldBlackbox(source),
-            click: () => this.props.toggleBlackBox(source)
+            click: () => this.props.toggleBlackBox(cx, source)
           };
           menuOptions.push(copySourceUri2, blackBoxMenuItem);
         }
       }
     }
 
     if (isDirectory(item)) {
       this.addCollapseExpandAllOptions(menuOptions, item);
 
       if (features.root) {
         const { path } = item;
-        const { projectRoot } = this.props;
+        const { cx, projectRoot } = this.props;
 
         if (projectRoot.endsWith(path)) {
           menuOptions.push({
             id: "node-remove-directory-root",
             label: removeDirectoryRootLabel,
             disabled: false,
-            click: () => this.props.clearProjectDirectoryRoot()
+            click: () => this.props.clearProjectDirectoryRoot(cx)
           });
         } else {
           menuOptions.push({
             id: "node-set-directory-root",
             label: setDirectoryRootLabel,
             accesskey: setDirectoryRootKey,
             disabled: false,
-            click: () => this.props.setProjectDirectoryRoot(path)
+            click: () => this.props.setProjectDirectoryRoot(cx, path)
           });
         }
       }
     }
 
     showMenu(event, menuOptions);
   };
 
@@ -268,16 +270,17 @@ function getHasMatchingGeneratedSource(s
   }
 
   return !!getGeneratedSourceByURL(state, source.url);
 }
 
 const mapStateToProps = (state, props) => {
   const { source } = props;
   return {
+    cx: getContext(state),
     hasMatchingGeneratedSource: getHasMatchingGeneratedSource(state, source),
     hasSiblingOfSameName: getHasSiblingOfSameName(state, source),
     hasPrettySource: source ? checkHasPrettySource(state, source.id) : false
   };
 };
 
 export default connect(
   mapStateToProps,
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/index.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/index.js
@@ -9,37 +9,39 @@ import classnames from "classnames";
 import { Tab, Tabs, TabList, TabPanels } from "react-aria-components/src/tabs";
 
 import actions from "../../actions";
 import {
   getDisplayedSources,
   getActiveSearch,
   getProjectDirectoryRoot,
   getSelectedPrimaryPaneTab,
-  getThreads
+  getThreads,
+  getContext
 } from "../../selectors";
 import { features, prefs } from "../../utils/prefs";
 import { connect } from "../../utils/connect";
 import { formatKeyShortcut } from "../../utils/text";
 
 import Outline from "./Outline";
 import SourcesTree from "./SourcesTree";
 import AccessibleImage from "../shared/AccessibleImage";
 
 import type { SourcesMapByThread } from "../../reducers/types";
 import type { SelectedPrimaryPaneTabType } from "../../selectors";
-import type { Thread } from "../../types";
+import type { Thread, Context } from "../../types";
 
 import "./Sources.css";
 
 type State = {
   alphabetizeOutline: boolean
 };
 
 type Props = {
+  cx: Context,
   selectedTab: SelectedPrimaryPaneTabType,
   sources: SourcesMapByThread,
   horizontal: boolean,
   projectRoot: string,
   sourceSearchOn: boolean,
   setPrimaryPaneTab: typeof actions.setPrimaryPaneTab,
   setActiveSearch: typeof actions.setActiveSearch,
   closeActiveSearch: typeof actions.closeActiveSearch,
@@ -96,29 +98,29 @@ class PrimaryPanes extends Component<Pro
         key="outline-tab"
       >
         {outline}
       </Tab>
     ];
   }
 
   renderProjectRootHeader() {
-    const { projectRoot } = this.props;
+    const { cx, projectRoot } = this.props;
 
     if (!projectRoot) {
       return null;
     }
 
     const rootLabel = projectRoot.split("/").pop();
 
     return (
       <div key="root" className="sources-clear-root-container">
         <button
           className="sources-clear-root"
-          onClick={() => this.props.clearProjectDirectoryRoot()}
+          onClick={() => this.props.clearProjectDirectoryRoot(cx)}
           title={L10N.getStr("removeDirectoryRoot.label")}
         >
           <AccessibleImage className="home" />
           <AccessibleImage className="breadcrumb" />
           <span className="sources-clear-root-label">{rootLabel}</span>
         </button>
       </div>
     );
@@ -159,16 +161,17 @@ class PrimaryPanes extends Component<Pro
           />
         </TabPanels>
       </Tabs>
     );
   }
 }
 
 const mapStateToProps = state => ({
+  cx: getContext(state),
   selectedTab: getSelectedPrimaryPaneTab(state),
   sources: getDisplayedSources(state),
   sourceSearchOn: getActiveSearch(state) === "source",
   threads: getThreads(state),
   projectRoot: getProjectDirectoryRoot(state)
 });
 
 const connector = connect(
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/tests/SourcesTree.spec.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/tests/SourcesTree.spec.js
@@ -4,17 +4,17 @@
 
 // @flow
 
 import React from "react";
 import { shallow } from "enzyme";
 import { showMenu } from "devtools-contextmenu";
 
 import SourcesTree from "../SourcesTree";
-import { makeMockSource } from "../../../utils/test-mockup";
+import { makeMockSource, mockcx } from "../../../utils/test-mockup";
 import { copyToTheClipboard } from "../../../utils/clipboard";
 
 jest.mock("devtools-contextmenu", () => ({ showMenu: jest.fn() }));
 jest.mock("../../../utils/clipboard", () => ({
   copyToTheClipboard: jest.fn()
 }));
 
 describe("SourcesTree", () => {
@@ -189,26 +189,28 @@ describe("SourcesTree", () => {
     it("select activated item", async () => {
       const { instance, props } = render();
       const item = createMockItem();
       const spy = jest.spyOn(instance, "selectItem");
 
       instance.onActivate(item);
       expect(spy).toHaveBeenCalledWith(item);
       expect(props.selectSource).toHaveBeenCalledWith(
+        mockcx,
         "server1.conn13.child1/39"
       );
     });
   });
 
   describe("selectItem", () => {
     it("should select item with no children", async () => {
       const { instance, props } = render();
       instance.selectItem(createMockItem());
       expect(props.selectSource).toHaveBeenCalledWith(
+        mockcx,
         "server1.conn13.child1/39"
       );
     });
 
     it("should not select item with children", async () => {
       const { props, instance } = render();
       instance.selectItem(createMockDirectory());
       expect(props.selectSource).not.toHaveBeenCalled();
@@ -352,16 +354,17 @@ function generateDefaults(overrides: Obj
     "server1.conn13.child1/42": createMockSource(
       "server1.conn13.child1/42",
       "http://mdn.com/four.js",
       false,
       "data:application/json?charset=utf?dsffewrsf"
     )
   };
   return {
+    cx: mockcx,
     thread: "FakeThread",
     autoExpandAll: true,
     selectSource: jest.fn(),
     setExpandedState: jest.fn(),
     sources: defaultSources,
     debuggeeUrl: "http://mdn.com",
     clearProjectDirectoryRoot: jest.fn(),
     setProjectDirectoryRoot: jest.fn(),
--- a/devtools/client/debugger/new/src/components/ProjectSearch.js
+++ b/devtools/client/debugger/new/src/components/ProjectSearch.js
@@ -14,26 +14,28 @@ import { getEditor } from "../utils/edit
 import { highlightMatches } from "../utils/project-search";
 
 import { statusType } from "../reducers/project-text-search";
 import { getRelativePath } from "../utils/sources-tree";
 import {
   getActiveSearch,
   getTextSearchResults,
   getTextSearchStatus,
-  getTextSearchQuery
+  getTextSearchQuery,
+  getContext
 } from "../selectors";
 
 import ManagedTree from "./shared/ManagedTree";
 import SearchInput from "./shared/SearchInput";
 import AccessibleImage from "./shared/AccessibleImage";
 
 import type { List } from "immutable";
 import type { ActiveSearchType } from "../reducers/types";
 import type { StatusType } from "../reducers/project-text-search";
+import type { Context } from "../types";
 
 import "./ProjectSearch.css";
 
 export type Match = {
   type: "MATCH",
   sourceId: string,
   line: number,
   column: number,
@@ -54,16 +56,17 @@ type Item = Result | Match;
 
 type State = {
   inputValue: string,
   inputFocused: boolean,
   focusedItem: ?Item
 };
 
 type Props = {
+  cx: Context,
   query: string,
   results: List<Result>,
   status: StatusType,
   activeSearch: ActiveSearchType,
   closeProjectSearch: typeof actions.closeProjectSearch,
   searchSources: typeof actions.searchSources,
   clearSearch: typeof actions.clearSearch,
   selectSpecificLocation: typeof actions.selectSpecificLocation,
@@ -114,36 +117,36 @@ export class ProjectSearch extends Compo
   componentDidUpdate(prevProps: Props) {
     // If the query changes in redux, also change it in the UI
     if (prevProps.query !== this.props.query) {
       this.setState({ inputValue: this.props.query });
     }
   }
 
   doSearch(searchTerm: string) {
-    this.props.searchSources(searchTerm);
+    this.props.searchSources(this.props.cx, searchTerm);
   }
 
   toggleProjectTextSearch = (key: string, e: KeyboardEvent) => {
-    const { closeProjectSearch, setActiveSearch } = this.props;
+    const { cx, closeProjectSearch, setActiveSearch } = this.props;
     if (e) {
       e.preventDefault();
     }
 
     if (this.isProjectSearchEnabled()) {
-      return closeProjectSearch();
+      return closeProjectSearch(cx);
     }
 
     return setActiveSearch("project");
   };
 
   isProjectSearchEnabled = () => this.props.activeSearch === "project";
 
   selectMatchItem = (matchItem: Match) => {
-    this.props.selectSpecificLocation({
+    this.props.selectSpecificLocation(this.props.cx, {
       sourceId: matchItem.sourceId,
       line: matchItem.line,
       column: matchItem.column
     });
     this.props.doSearchForHighlight(
       this.state.inputValue,
       getEditor(),
       matchItem.line,
@@ -191,20 +194,20 @@ export class ProjectSearch extends Compo
   onFocus = (item: Item) => {
     if (this.state.focusedItem !== item) {
       this.setState({ focusedItem: item });
     }
   };
 
   inputOnChange = (e: SyntheticInputEvent<HTMLInputElement>) => {
     const inputValue = e.target.value;
-    const { clearSearch } = this.props;
+    const { cx, clearSearch } = this.props;
     this.setState({ inputValue });
     if (inputValue === "") {
-      clearSearch();
+      clearSearch(cx);
     }
   };
 
   renderFile = (file: Result, focused: boolean, expanded: boolean) => {
     const matchesLength = file.matches.length;
     const matches = ` (${matchesLength} match${matchesLength > 1 ? "es" : ""})`;
 
     return (
@@ -326,16 +329,17 @@ export class ProjectSearch extends Compo
     );
   }
 }
 ProjectSearch.contextTypes = {
   shortcuts: PropTypes.object
 };
 
 const mapStateToProps = state => ({
+  cx: getContext(state),
   activeSearch: getActiveSearch(state),
   results: getTextSearchResults(state),
   query: getTextSearchQuery(state),
   status: getTextSearchStatus(state)
 });
 
 export default connect(
   mapStateToProps,
--- a/devtools/client/debugger/new/src/components/QuickOpenModal.js
+++ b/devtools/client/debugger/new/src/components/QuickOpenModal.js
@@ -12,17 +12,18 @@ import actions from "../actions";
 import {
   getDisplayedSourcesList,
   getQuickOpenEnabled,
   getQuickOpenQuery,
   getQuickOpenType,
   getSelectedSource,
   getSymbols,
   getTabs,
-  isSymbolsLoading
+  isSymbolsLoading,
+  getContext
 } from "../selectors";
 import { scrollList } from "../utils/result-list";
 import {
   formatSymbols,
   parseLineColumn,
   formatShortcutResults,
   formatSources
 } from "../utils/quick-open";
@@ -30,23 +31,24 @@ import Modal from "./shared/Modal";
 import SearchInput from "./shared/SearchInput";
 import ResultList from "./shared/ResultList";
 
 import type {
   FormattedSymbolDeclarations,
   QuickOpenResult
 } from "../utils/quick-open";
 
-import type { Source } from "../types";
+import type { Source, Context } from "../types";
 import type { QuickOpenType } from "../reducers/quick-open";
 import type { Tab } from "../reducers/tabs";
 
 import "./QuickOpenModal.css";
 
 type Props = {
+  cx: Context,
   enabled: boolean,
   sources: Array<Object>,
   selectedSource?: Source,
   query: string,
   searchType: QuickOpenType,
   symbols: FormattedSymbolDeclarations,
   symbolsLoading: boolean,
   tabs: Tab[],
@@ -240,21 +242,21 @@ export class QuickOpenModal extends Comp
     this.setState({ selectedIndex: nextIndex });
 
     if (results != null) {
       this.onSelectResultItem(results[nextIndex]);
     }
   };
 
   gotoLocation = (location: ?GotoLocationType) => {
-    const { selectSpecificLocation, selectedSource } = this.props;
+    const { cx, selectSpecificLocation, selectedSource } = this.props;
     const selectedSourceId = selectedSource ? selectedSource.id : "";
     if (location != null) {
       const sourceId = location.sourceId ? location.sourceId : selectedSourceId;
-      selectSpecificLocation({
+      selectSpecificLocation(cx, {
         sourceId,
         line: location.line,
         column: location.column
       });
       this.closeModal();
     }
   };
 
@@ -414,16 +416,17 @@ export class QuickOpenModal extends Comp
   }
 }
 
 /* istanbul ignore next: ignoring testing of redux connection stuff */
 function mapStateToProps(state) {
   const selectedSource = getSelectedSource(state);
 
   return {
+    cx: getContext(state),
     enabled: getQuickOpenEnabled(state),
     sources: formatSources(getDisplayedSourcesList(state), getTabs(state)),
     selectedSource,
     symbols: formatSymbols(getSymbols(state, selectedSource)),
     symbolsLoading: isSymbolsLoading(state, selectedSource),
     query: getQuickOpenQuery(state),
     searchType: getQuickOpenType(state),
     tabs: getTabs(state)
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
@@ -22,31 +22,34 @@ import {
 import { getSelectedLocation } from "../../../utils/source-maps";
 import { features } from "../../../utils/prefs";
 import { getEditor } from "../../../utils/editor";
 
 import type {
   Breakpoint as BreakpointType,
   Frame,
   Source,
-  SourceLocation
+  SourceLocation,
+  Context
 } from "../../../types";
 
 type FormattedFrame = Frame & {
   selectedLocation: SourceLocation
 };
 
 import {
   getBreakpointsList,
   getSelectedFrame,
   getSelectedSource,
-  getCurrentThread
+  getCurrentThread,
+  getContext
 } from "../../../selectors";
 
 type Props = {
+  cx: Context,
   breakpoint: BreakpointType,
   breakpoints: BreakpointType[],
   selectedSource: Source,
   source: Source,
   frame: FormattedFrame,
   enableBreakpoint: typeof actions.enableBreakpoint,
   removeBreakpoint: typeof actions.removeBreakpoint,
   removeBreakpoints: typeof actions.removeBreakpoints,
@@ -74,32 +77,32 @@ class Breakpoint extends PureComponent<P
     const { breakpoint, openConditionalPanel } = this.props;
     if (breakpoint.options.condition) {
       openConditionalPanel(this.selectedLocation);
     }
   };
 
   selectBreakpoint = event => {
     event.preventDefault();
-    const { selectSpecificLocation } = this.props;
-    selectSpecificLocation(this.selectedLocation);
+    const { cx, selectSpecificLocation } = this.props;
+    selectSpecificLocation(cx, this.selectedLocation);
   };
 
   removeBreakpoint = event => {
-    const { removeBreakpoint, breakpoint } = this.props;
+    const { cx, removeBreakpoint, breakpoint } = this.props;
     event.stopPropagation();
-    removeBreakpoint(breakpoint);
+    removeBreakpoint(cx, breakpoint);
   };
 
   handleBreakpointCheckbox = () => {
-    const { breakpoint, enableBreakpoint, disableBreakpoint } = this.props;
+    const { cx, breakpoint, enableBreakpoint, disableBreakpoint } = this.props;
     if (breakpoint.disabled) {
-      enableBreakpoint(breakpoint);
+      enableBreakpoint(cx, breakpoint);
     } else {
-      disableBreakpoint(breakpoint);
+      disableBreakpoint(cx, breakpoint);
     }
   };
 
   isCurrentlyPausedAtBreakpoint() {
     const { frame } = this.props;
     if (!frame) {
       return false;
     }
@@ -206,16 +209,17 @@ const getFormattedFrame = createSelector
     return {
       ...frame,
       selectedLocation: getSelectedLocation(frame, selectedSource)
     };
   }
 );
 
 const mapStateToProps = state => ({
+  cx: getContext(state),
   breakpoints: getBreakpointsList(state),
   frame: getFormattedFrame(state, getCurrentThread(state))
 });
 
 export default connect(
   mapStateToProps,
   {
     enableBreakpoint: actions.enableBreakpoint,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointHeading.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointHeading.js
@@ -9,67 +9,76 @@ import actions from "../../../actions";
 import {
   getTruncatedFileName,
   getDisplayPath,
   getSourceQueryString,
   getFileURL
 } from "../../../utils/source";
 import {
   getHasSiblingOfSameName,
-  getBreakpointsForSource
+  getBreakpointsForSource,
+  getContext
 } from "../../../selectors";
 
 import SourceIcon from "../../shared/SourceIcon";
 
-import type { Source, Breakpoint } from "../../../types";
+import type { Source, Breakpoint, Context } from "../../../types";
 import showContextMenu from "./BreakpointHeadingsContextMenu";
 
 type Props = {
+  cx: Context,
   sources: Source[],
   source: Source,
   hasSiblingOfSameName: boolean,
   breakpointsForSource: Breakpoint[],
   disableBreakpointsInSource: typeof actions.disableBreakpointsInSource,
   enableBreakpointsInSource: typeof actions.enableBreakpointsInSource,
   removeBreakpointsInSource: typeof actions.removeBreakpointsInSource,
   selectSource: typeof actions.selectSource
 };
 
 class BreakpointHeading extends PureComponent<Props> {
   onContextMenu = e => {
     showContextMenu({ ...this.props, contextMenuEvent: e });
   };
 
   render() {
-    const { sources, source, hasSiblingOfSameName, selectSource } = this.props;
+    const {
+      cx,
+      sources,
+      source,
+      hasSiblingOfSameName,
+      selectSource
+    } = this.props;
 
     const path = getDisplayPath(source, sources);
     const query = hasSiblingOfSameName ? getSourceQueryString(source) : "";
 
     return (
       <div
         className="breakpoint-heading"
         title={getFileURL(source, false)}
-        onClick={() => selectSource(source.id)}
+        onClick={() => selectSource(cx, source.id)}
         onContextMenu={this.onContextMenu}
       >
         <SourceIcon
           source={source}
           shouldHide={icon => ["file", "javascript"].includes(icon)}
         />
         <div className="filename">
           {getTruncatedFileName(source, query)}
           {path && <span>{`../${path}/..`}</span>}
         </div>
       </div>
     );
   }
 }
 
 const mapStateToProps = (state, { source }) => ({
+  cx: getContext(state),
   hasSiblingOfSameName: getHasSiblingOfSameName(state, source),
   breakpointsForSource: getBreakpointsForSource(state, source.id)
 });
 
 export default connect(
   mapStateToProps,
   {
     selectSource: actions.selectSource,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointHeadingsContextMenu.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointHeadingsContextMenu.js
@@ -2,29 +2,31 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import { buildMenu, showMenu } from "devtools-contextmenu";
 
 import actions from "../../../actions";
-import type { Breakpoint, Source } from "../../../types";
+import type { Breakpoint, Source, Context } from "../../../types";
 
 type Props = {
+  cx: Context,
   source: Source,
   breakpointsForSource: Breakpoint[],
   disableBreakpointsInSource: typeof actions.disableBreakpointsInSource,
   enableBreakpointsInSource: typeof actions.enableBreakpointsInSource,
   removeBreakpointsInSource: typeof actions.removeBreakpointsInSource,
   contextMenuEvent: SyntheticEvent<HTMLElement>
 };
 
 export default function showContextMenu(props: Props) {
   const {
+    cx,
     source,
     breakpointsForSource,
     disableBreakpointsInSource,
     enableBreakpointsInSource,
     removeBreakpointsInSource,
     contextMenuEvent
   } = props;
 
@@ -49,33 +51,33 @@ export default function showContextMenu(
     "breakpointHeadingsMenuItem.removeInSource.accesskey"
   );
 
   const disableInSourceItem = {
     id: "node-menu-disable-in-source",
     label: disableInSourceLabel,
     accesskey: disableInSourceKey,
     disabled: false,
-    click: () => disableBreakpointsInSource(source)
+    click: () => disableBreakpointsInSource(cx, source)
   };
 
   const enableInSourceItem = {
     id: "node-menu-enable-in-source",
     label: enableInSourceLabel,
     accesskey: enableInSourceKey,
     disabled: false,
-    click: () => enableBreakpointsInSource(source)
+    click: () => enableBreakpointsInSource(cx, source)
   };
 
   const removeInSourceItem = {
     id: "node-menu-enable-in-source",
     label: removeInSourceLabel,
     accesskey: removeInSourceKey,
     disabled: false,
-    click: () => removeBreakpointsInSource(source)
+    click: () => removeBreakpointsInSource(cx, source)
   };
 
   const hideDisableInSourceItem = breakpointsForSource.every(
     breakpoint => breakpoint.disabled
   );
   const hideEnableInSourceItem = breakpointsForSource.every(
     breakpoint => !breakpoint.disabled
   );
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointsContextMenu.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointsContextMenu.js
@@ -4,19 +4,20 @@
 
 // @flow
 
 import { buildMenu, showMenu } from "devtools-contextmenu";
 import { getSelectedLocation } from "../../../utils/source-maps";
 import actions from "../../../actions";
 import { features } from "../../../utils/prefs";
 
-import type { Breakpoint, Source } from "../../../types";
+import type { Breakpoint, Source, Context } from "../../../types";
 
 type Props = {
+  cx: Context,
   breakpoint: Breakpoint,
   breakpoints: Breakpoint[],
   selectedSource: Source,
   removeBreakpoint: typeof actions.removeBreakpoint,
   removeBreakpoints: typeof actions.removeBreakpoints,
   removeAllBreakpoints: typeof actions.removeAllBreakpoints,
   toggleBreakpoints: typeof actions.toggleBreakpoints,
   toggleAllBreakpoints: typeof actions.toggleAllBreakpoints,
@@ -24,16 +25,17 @@ type Props = {
   selectSpecificLocation: typeof actions.selectSpecificLocation,
   setBreakpointOptions: typeof actions.setBreakpointOptions,
   openConditionalPanel: typeof actions.openConditionalPanel,
   contextMenuEvent: SyntheticEvent<HTMLElement>
 };
 
 export default function showContextMenu(props: Props) {
   const {
+    cx,
     breakpoint,
     breakpoints,
     selectedSource,
     removeBreakpoint,
     removeBreakpoints,
     removeAllBreakpoints,
     toggleBreakpoints,
     toggleAllBreakpoints,
@@ -110,116 +112,116 @@ export default function showContextMenu(
   );
 
   const deleteSelfItem = {
     id: "node-menu-delete-self",
     label: deleteSelfLabel,
     accesskey: deleteSelfKey,
     disabled: false,
     click: () => {
-      removeBreakpoint(breakpoint);
+      removeBreakpoint(cx, breakpoint);
     }
   };
 
   const deleteAllItem = {
     id: "node-menu-delete-all",
     label: deleteAllLabel,
     accesskey: deleteAllKey,
     disabled: false,
-    click: () => removeAllBreakpoints()
+    click: () => removeAllBreakpoints(cx)
   };
 
   const deleteOthersItem = {
     id: "node-menu-delete-other",
     label: deleteOthersLabel,
     accesskey: deleteOthersKey,
     disabled: false,
-    click: () => removeBreakpoints(otherBreakpoints)
+    click: () => removeBreakpoints(cx, otherBreakpoints)
   };
 
   const enableSelfItem = {
     id: "node-menu-enable-self",
     label: enableSelfLabel,
     accesskey: enableSelfKey,
     disabled: false,
     click: () => {
-      toggleDisabledBreakpoint(breakpoint);
+      toggleDisabledBreakpoint(cx, breakpoint);
     }
   };
 
   const enableAllItem = {
     id: "node-menu-enable-all",
     label: enableAllLabel,
     accesskey: enableAllKey,
     disabled: false,
-    click: () => toggleAllBreakpoints(false)
+    click: () => toggleAllBreakpoints(cx, false)
   };
 
   const enableOthersItem = {
     id: "node-menu-enable-others",
     label: enableOthersLabel,
     accesskey: enableOthersKey,
     disabled: false,
-    click: () => toggleBreakpoints(false, otherDisabledBreakpoints)
+    click: () => toggleBreakpoints(cx, false, otherDisabledBreakpoints)
   };
 
   const disableSelfItem = {
     id: "node-menu-disable-self",
     label: disableSelfLabel,
     accesskey: disableSelfKey,
     disabled: false,
     click: () => {
-      toggleDisabledBreakpoint(breakpoint);
+      toggleDisabledBreakpoint(cx, breakpoint);
     }
   };
 
   const disableAllItem = {
     id: "node-menu-disable-all",
     label: disableAllLabel,
     accesskey: disableAllKey,
     disabled: false,
-    click: () => toggleAllBreakpoints(true)
+    click: () => toggleAllBreakpoints(cx, true)
   };
 
   const disableOthersItem = {
     id: "node-menu-disable-others",
     label: disableOthersLabel,
     accesskey: disableOthersKey,
-    click: () => toggleBreakpoints(true, otherEnabledBreakpoints)
+    click: () => toggleBreakpoints(cx, true, otherEnabledBreakpoints)
   };
 
   const removeConditionItem = {
     id: "node-menu-remove-condition",
     label: removeConditionLabel,
     accesskey: removeConditionKey,
     disabled: false,
     click: () =>
-      setBreakpointOptions(selectedLocation, {
+      setBreakpointOptions(cx, selectedLocation, {
         ...breakpoint.options,
         condition: null
       })
   };
 
   const addConditionItem = {
     id: "node-menu-add-condition",
     label: addConditionLabel,
     accesskey: addConditionKey,
     click: () => {
-      selectSpecificLocation(selectedLocation);
+      selectSpecificLocation(cx, selectedLocation);
       openConditionalPanel(selectedLocation);
     },
     accelerator: L10N.getStr("toggleCondPanel.breakpoint.key")
   };
 
   const editConditionItem = {
     id: "node-menu-edit-condition",
     label: editConditionLabel,
     accesskey: editConditionKey,
     click: () => {
-      selectSpecificLocation(selectedLocation);
+      selectSpecificLocation(cx, selectedLocation);
       openConditionalPanel(selectedLocation);
     },
     accelerator: L10N.getStr("toggleCondPanel.breakpoint.key")
   };
 
   const addLogPointItem = {
     id: "node-menu-add-log-point",
     label: L10N.getStr("editor.addLogPoint"),
@@ -239,17 +241,17 @@ export default function showContextMenu(
   };
 
   const removeLogPointItem = {
     id: "node-menu-remove-log",
     label: L10N.getStr("editor.removeLogPoint.label"),
     accesskey: L10N.getStr("editor.removeLogPoint.accesskey"),
     disabled: false,
     click: () =>
-      setBreakpointOptions(selectedLocation, {
+      setBreakpointOptions(cx, selectedLocation, {
         ...breakpoint.options,
         logValue: null
       })
   };
 
   const logPointItem = breakpoint.options.logValue
     ? editLogPointItem
     : addLogPointItem;
--- 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
@@ -7,17 +7,18 @@
 import React from "react";
 import { shallow } from "enzyme";
 
 import BreakpointsContextMenu from "../BreakpointsContextMenu";
 import { buildMenu } from "devtools-contextmenu";
 
 import {
   makeMockBreakpoint,
-  makeMockSource
+  makeMockSource,
+  mockcx
 } from "../../../../utils/test-mockup";
 
 jest.mock("devtools-contextmenu");
 
 function render(disabled = false) {
   const props = generateDefaults(disabled);
   const component = shallow(<BreakpointsContextMenu {...props} />);
   return { component, props };
@@ -50,16 +51,17 @@ function generateDefaults(disabled) {
     {
       ...makeMockBreakpoint(source, 3),
       id: "https://example.com/main.js:3:",
       disabled: disabled
     }
   ];
 
   const props = {
+    cx: mockcx,
     breakpoints,
     breakpoint: breakpoints[0],
     removeBreakpoint: jest.fn(),
     removeBreakpoints: jest.fn(),
     removeAllBreakpoints: jest.fn(),
     toggleBreakpoints: jest.fn(),
     toggleAllBreakpoints: jest.fn(),
     toggleDisabledBreakpoint: jest.fn(),
@@ -85,49 +87,49 @@ describe("BreakpointsContextMenu", () =>
       const deleteOthers = menuItems.find(
         item => item.item.id === "node-menu-delete-other"
       );
       deleteOthers.item.click();
 
       expect(props.removeBreakpoints).toHaveBeenCalled();
 
       const otherBreakpoints = [props.breakpoints[1], props.breakpoints[2]];
-      expect(props.removeBreakpoints.mock.calls[0][0]).toEqual(
+      expect(props.removeBreakpoints.mock.calls[0][1]).toEqual(
         otherBreakpoints
       );
     });
 
     it("'enable others' calls toggleBreakpoints with proper arguments", () => {
       const { props } = render(true);
       const menuItems = buildMenu.mock.calls[0][0];
       const enableOthers = menuItems.find(
         item => item.item.id === "node-menu-enable-others"
       );
       enableOthers.item.click();
 
       expect(props.toggleBreakpoints).toHaveBeenCalled();
 
-      expect(props.toggleBreakpoints.mock.calls[0][0]).toBe(false);
+      expect(props.toggleBreakpoints.mock.calls[0][1]).toBe(false);
 
       const otherBreakpoints = [props.breakpoints[1], props.breakpoints[2]];
-      expect(props.toggleBreakpoints.mock.calls[0][1]).toEqual(
+      expect(props.toggleBreakpoints.mock.calls[0][2]).toEqual(
         otherBreakpoints
       );
     });
 
     it("'disable others' calls toggleBreakpoints with proper arguments", () => {
       const { props } = render();
       const menuItems = buildMenu.mock.calls[0][0];
       const disableOthers = menuItems.find(
         item => item.item.id === "node-menu-disable-others"
       );
       disableOthers.item.click();
 
       expect(props.toggleBreakpoints).toHaveBeenCalled();
-      expect(props.toggleBreakpoints.mock.calls[0][0]).toBe(true);
+      expect(props.toggleBreakpoints.mock.calls[0][1]).toBe(true);
 
       const otherBreakpoints = [props.breakpoints[1], props.breakpoints[2]];
-      expect(props.toggleBreakpoints.mock.calls[0][1]).toEqual(
+      expect(props.toggleBreakpoints.mock.calls[0][2]).toEqual(
         otherBreakpoints
       );
     });
   });
 });
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/CommandBar.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/CommandBar.js
@@ -7,29 +7,30 @@
 
 import PropTypes from "prop-types";
 import React, { Component } from "react";
 
 import { connect } from "../../utils/connect";
 import classnames from "classnames";
 import { features } from "../../utils/prefs";
 import {
-  getIsPaused,
   getIsWaitingOnBreak,
   getCanRewind,
   getSkipPausing,
-  getCurrentThread
+  getCurrentThread,
+  getThreadContext
 } from "../../selectors";
 import { formatKeyShortcut } from "../../utils/text";
 import actions from "../../actions";
 import { debugBtn } from "../shared/Button/CommandBarButton";
 import AccessibleImage from "../shared/AccessibleImage";
 import "./CommandBar.css";
 
 import { appinfo } from "devtools-services";
+import type { ThreadContext } from "../../types";
 
 const isMacOS = appinfo.OS === "Darwin";
 
 // NOTE: the "resume" command will call either the resume or breakOnNext action
 // depending on whether or not the debugger is paused or running
 const COMMANDS = ["resume", "stepOver", "stepIn", "stepOut"];
 
 const KEYS = {
@@ -70,17 +71,17 @@ function formatKey(action) {
       getKeyForOS("WINNT", `${action}Display`) || getKeyForOS("WINNT", action);
     // display both Windows type and Mac specific keys
     return formatKeyShortcut([key, winKey].join(" "));
   }
   return formatKeyShortcut(key);
 }
 
 type Props = {
-  isPaused: boolean,
+  cx: ThreadContext,
   isWaitingOnBreak: boolean,
   horizontal: boolean,
   canRewind: boolean,
   skipPausing: boolean,
   resume: typeof actions.resume,
   stepIn: typeof actions.stepIn,
   stepOut: typeof actions.stepOut,
   stepOver: typeof actions.stepOver,
@@ -116,67 +117,70 @@ class CommandBar extends Component<Props
         shortcuts.on(getKeyForOS("WINNT", action), (_, e) =>
           this.handleEvent(e, action)
         )
       );
     }
   }
 
   handleEvent(e, action) {
+    const { cx } = this.props;
     e.preventDefault();
     e.stopPropagation();
     if (action === "resume") {
-      this.props.isPaused ? this.props.resume() : this.props.breakOnNext();
+      this.props.cx.isPaused
+        ? this.props.resume(cx)
+        : this.props.breakOnNext(cx);
     } else {
-      this.props[action]();
+      this.props[action](cx);
     }
   }
 
   renderStepButtons() {
-    const { isPaused, canRewind } = this.props;
-    const className = isPaused ? "active" : "disabled";
-    const isDisabled = !isPaused;
+    const { cx, canRewind } = this.props;
+    const className = cx.isPaused ? "active" : "disabled";
+    const isDisabled = !cx.isPaused;
 
-    if (canRewind || (!isPaused && features.removeCommandBarOptions)) {
+    if (canRewind || (!cx.isPaused && features.removeCommandBarOptions)) {
       return;
     }
 
     return [
       debugBtn(
-        this.props.stepOver,
+        () => this.props.stepOver(cx),
         "stepOver",
         className,
         L10N.getFormatStr("stepOverTooltip", formatKey("stepOver")),
         isDisabled
       ),
       debugBtn(
-        this.props.stepIn,
+        () => this.props.stepIn(cx),
         "stepIn",
         className,
         L10N.getFormatStr("stepInTooltip", formatKey("stepIn")),
         isDisabled
       ),
       debugBtn(
-        this.props.stepOut,
+        () => this.props.stepOut(cx),
         "stepOut",
         className,
         L10N.getFormatStr("stepOutTooltip", formatKey("stepOut")),
         isDisabled
       )
     ];
   }
 
   resume() {
-    this.props.resume();
+    this.props.resume(this.props.cx);
   }
 
   renderPauseButton() {
-    const { isPaused, breakOnNext, isWaitingOnBreak, canRewind } = this.props;
+    const { cx, breakOnNext, isWaitingOnBreak, canRewind } = this.props;
 
-    if (isPaused) {
+    if (cx.isPaused) {
       if (canRewind) {
         return null;
       }
       return debugBtn(
         () => this.resume(),
         "resume",
         "active",
         L10N.getFormatStr("resumeButtonTooltip", formatKey("resume"))
@@ -193,66 +197,71 @@ class CommandBar extends Component<Props
         "pause",
         "disabled",
         L10N.getStr("pausePendingButtonTooltip"),
         true
       );
     }
 
     return debugBtn(
-      breakOnNext,
+      () => breakOnNext(cx),
       "pause",
       "active",
       L10N.getFormatStr("pauseButtonTooltip", formatKey("resume"))
     );
   }
 
   renderTimeTravelButtons() {
-    const { isPaused, canRewind } = this.props;
+    const { cx, canRewind } = this.props;
 
-    if (!canRewind || !isPaused) {
+    if (!canRewind || !cx.isPaused) {
       return null;
     }
 
-    const isDisabled = !isPaused;
+    const isDisabled = !cx.isPaused;
 
     return [
-      debugBtn(this.props.rewind, "rewind", "active", "Rewind Execution"),
+      debugBtn(
+        () => this.props.rewind(cx),
+        "rewind",
+        "active",
+        "Rewind Execution"
+      ),
 
       debugBtn(
-        this.props.resume,
+        () => this.props.resume(cx),
         "resume",
         "active",
         L10N.getFormatStr("resumeButtonTooltip", formatKey("resume"))
       ),
       <div key="divider-1" className="divider" />,
       debugBtn(
-        this.props.reverseStepOver,
+        () => this.props.reverseStepOver(cx),
         "reverseStepOver",
         "active",
         "Reverse step over"
       ),
       debugBtn(
-        this.props.stepOver,
+        () => this.props.stepOver(cx),
         "stepOver",
         "active",
         L10N.getFormatStr("stepOverTooltip", formatKey("stepOver")),
         isDisabled
       ),
       <div key="divider-2" className="divider" />,
       debugBtn(
-        this.props.stepOut,
+        () => this.props.stepOut(cx),
         "stepOut",
         "active",
         L10N.getFormatStr("stepOutTooltip", formatKey("stepOut")),
         isDisabled
       ),
 
       debugBtn(
-        this.props.stepIn,
+        () => this.props.stepIn(cx),
         "stepIn",
         "active",
         L10N.getFormatStr("stepInTooltip", formatKey("stepIn")),
         isDisabled
       )
     ];
   }
 
@@ -298,17 +307,17 @@ class CommandBar extends Component<Props
   }
 }
 
 CommandBar.contextTypes = {
   shortcuts: PropTypes.object
 };
 
 const mapStateToProps = state => ({
-  isPaused: getIsPaused(state, getCurrentThread(state)),
+  cx: getThreadContext(state),
   isWaitingOnBreak: getIsWaitingOnBreak(state, getCurrentThread(state)),
   canRewind: getCanRewind(state),
   skipPausing: getSkipPausing(state)
 });
 
 export default connect(
   mapStateToProps,
   {
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Expressions.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Expressions.js
@@ -8,39 +8,41 @@ import { connect } from "../../utils/con
 import classnames from "classnames";
 import { features } from "../../utils/prefs";
 import { objectInspector } from "devtools-reps";
 
 import actions from "../../actions";
 import {
   getExpressions,
   getExpressionError,
-  getAutocompleteMatchset
+  getAutocompleteMatchset,
+  getThreadContext
 } from "../../selectors";
 import { getValue } from "../../utils/expressions";
 import { createObjectClient } from "../../client/firefox";
 
 import { CloseButton } from "../shared/Button";
 import { debounce } from "lodash";
 
 import type { List } from "immutable";
-import type { Expression } from "../../types";
+import type { Expression, ThreadContext } from "../../types";
 
 import "./Expressions.css";
 
 const { ObjectInspector } = objectInspector;
 
 type State = {
   editing: boolean,
   editIndex: number,
   inputValue: string,
   focused: boolean
 };
 
 type Props = {
+  cx: ThreadContext,
   expressions: List<Expression>,
   expressionError: boolean,
   showInput: boolean,
   autocompleteMatches: string[],
   onExpressionAdded: () => void,
   autocomplete: typeof actions.autocomplete,
   clearAutocomplete: typeof actions.clearAutocomplete,
   addExpression: typeof actions.addExpression,
@@ -68,20 +70,20 @@ class Expressions extends Component<Prop
       editing: false,
       editIndex: -1,
       inputValue: "",
       focused: false
     };
   }
 
   componentDidMount() {
-    const { expressions, evaluateExpressions, showInput } = this.props;
+    const { cx, expressions, evaluateExpressions, showInput } = this.props;
 
     if (expressions.size > 0) {
-      evaluateExpressions();
+      evaluateExpressions(cx);
     }
 
     // Ensures that the input is focused when the "+"
     // is clicked while the panel is collapsed
     if (showInput && this._input) {
       this._input.focus();
     }
   }
@@ -156,17 +158,17 @@ class Expressions extends Component<Prop
     if (features.autocompleteExpression) {
       this.findAutocompleteMatches(target.value, target.selectionStart);
     }
     this.setState({ inputValue: target.value });
   };
 
   findAutocompleteMatches = debounce((value, selectionStart) => {
     const { autocomplete } = this.props;
-    autocomplete(value, selectionStart);
+    autocomplete(this.props.cx, value, selectionStart);
   }, 250);
 
   handleKeyDown = (e: SyntheticKeyboardEvent<HTMLInputElement>) => {
     if (e.key === "Escape") {
       this.clear();
     }
   };
 
@@ -187,27 +189,31 @@ class Expressions extends Component<Prop
 
   handleExistingSubmit = async (
     e: SyntheticEvent<HTMLFormElement>,
     expression: Expression
   ) => {
     e.preventDefault();
     e.stopPropagation();
 
-    this.props.updateExpression(this.state.inputValue, expression);
+    this.props.updateExpression(
+      this.props.cx,
+      this.state.inputValue,
+      expression
+    );
     this.hideInput();
   };
 
   handleNewSubmit = async (e: SyntheticEvent<HTMLFormElement>) => {
     const { inputValue } = this.state;
     e.preventDefault();
     e.stopPropagation();
 
     this.props.clearExpressionError();
-    await this.props.addExpression(this.state.inputValue);
+    await this.props.addExpression(this.props.cx, this.state.inputValue);
     this.setState({
       editing: false,
       editIndex: -1,
       inputValue: this.props.expressionError ? inputValue : ""
     });
 
     if (!this.props.expressionError) {
       this.hideInput();
@@ -372,16 +378,17 @@ class Expressions extends Component<Prop
         {(showInput || !expressions.size) && this.renderNewExpressionInput()}
       </ul>
     );
   }
 }
 
 const mapStateToProps = state => {
   return {
+    cx: getThreadContext(state),
     autocompleteMatches: getAutocompleteMatchset(state),
     expressions: getExpressions(state),
     expressionError: getExpressionError(state)
   };
 };
 
 export default connect(
   mapStateToProps,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Frame.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Frame.js
@@ -8,18 +8,19 @@ import PropTypes from "prop-types";
 
 import classNames from "classnames";
 
 import AccessibleImage from "../../shared/AccessibleImage";
 import { formatDisplayName } from "../../../utils/pause/frames";
 import { getFilename, getFileURL } from "../../../utils/source";
 import FrameMenu from "./FrameMenu";
 import FrameIndent from "./FrameIndent";
+import actions from "../../../actions";
 
-import type { Frame } from "../../../types";
+import type { Frame, ThreadContext } from "../../../types";
 
 type FrameTitleProps = {
   frame: Frame,
   options: Object,
   l10n: Object
 };
 
 function FrameTitle({ frame, options = {}, l10n }: FrameTitleProps) {
@@ -56,21 +57,22 @@ function FrameLocation({ frame, displayF
       <span className="line">{location.line}</span>
     </span>
   );
 }
 
 FrameLocation.displayName = "FrameLocation";
 
 type FrameComponentProps = {
+  cx: ThreadContext,
   frame: Frame,
   selectedFrame: Frame,
   copyStackTrace: Function,
   toggleFrameworkGrouping: Function,
-  selectFrame: Function,
+  selectFrame: typeof actions.selectFrame,
   frameworkGroupingOn: boolean,
   hideLocation: boolean,
   shouldMapDisplayName: boolean,
   toggleBlackBox: Function,
   displayFullUrl: boolean,
   getFrameTitle?: string => string,
   disableContextMenu: boolean,
   selectable: boolean
@@ -102,28 +104,28 @@ export default class FrameComponent exte
   onMouseDown(
     e: SyntheticMouseEvent<HTMLElement>,
     frame: Frame,
     selectedFrame: Frame
   ) {
     if (e.button !== 0) {
       return;
     }
-    this.props.selectFrame(frame);
+    this.props.selectFrame(this.props.cx, frame);
   }
 
   onKeyUp(
     event: SyntheticKeyboardEvent<HTMLElement>,
     frame: Frame,
     selectedFrame: Frame
   ) {
     if (event.key != "Enter") {
       return;
     }
-    this.props.selectFrame(frame);
+    this.props.selectFrame(this.props.cx, frame);
   }
 
   render() {
     const {
       frame,
       selectedFrame,
       hideLocation,
       shouldMapDisplayName,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
@@ -10,17 +10,18 @@ import classNames from "classnames";
 import { getLibraryFromUrl } from "../../../utils/pause/frames";
 
 import FrameMenu from "./FrameMenu";
 import AccessibleImage from "../../shared/AccessibleImage";
 import FrameComponent from "./Frame";
 
 import "./Group.css";
 
-import type { Frame } from "../../../types";
+import actions from "../../../actions";
+import type { Frame, ThreadContext } from "../../../types";
 import Badge from "../../shared/Badge";
 import FrameIndent from "./FrameIndent";
 
 type FrameLocationProps = { frame: Frame, expanded: boolean };
 function FrameLocation({ frame, expanded }: FrameLocationProps) {
   const library = frame.library || getLibraryFromUrl(frame);
   if (!library) {
     return null;
@@ -34,19 +35,20 @@ function FrameLocation({ frame, expanded
       <span className="group-description-name">{library}</span>
     </span>
   );
 }
 
 FrameLocation.displayName = "FrameLocation";
 
 type Props = {
+  cx: ThreadContext,
   group: Frame[],
   selectedFrame: Frame,
-  selectFrame: Function,
+  selectFrame: typeof actions.selectFrame,
   toggleFrameworkGrouping: Function,
   copyStackTrace: Function,
   toggleBlackBox: Function,
   frameworkGroupingOn: boolean,
   displayFullUrl: boolean,
   getFrameTitle?: string => string,
   disableContextMenu: boolean,
   selectable: boolean
@@ -83,16 +85,17 @@ export default class Group extends Compo
 
   toggleFrames = (event: SyntheticMouseEvent<HTMLElement>) => {
     event.stopPropagation();
     this.setState(prevState => ({ expanded: !prevState.expanded }));
   };
 
   renderFrames() {
     const {
+      cx,
       group,
       selectFrame,
       selectedFrame,
       toggleFrameworkGrouping,
       frameworkGroupingOn,
       toggleBlackBox,
       copyStackTrace,
       displayFullUrl,
@@ -109,16 +112,17 @@ export default class Group extends Compo
     return (
       <div className="frames-list">
         {group.reduce((acc, frame, i) => {
           if (selectable) {
             acc.push(<FrameIndent key={`frame-indent-${i}`} />);
           }
           return acc.concat(
             <FrameComponent
+              cx={cx}
               copyStackTrace={copyStackTrace}
               frame={frame}
               frameworkGroupingOn={frameworkGroupingOn}
               hideLocation={true}
               key={frame.id}
               selectedFrame={selectedFrame}
               selectFrame={selectFrame}
               shouldMapDisplayName={false}
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/index.js
@@ -3,45 +3,47 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { Component } from "react";
 import { connect } from "../../../utils/connect";
 import PropTypes from "prop-types";
 
-import type { Frame, Why } from "../../../types";
+import type { Frame, Why, ThreadContext } from "../../../types";
 
 import FrameComponent from "./Frame";
 import Group from "./Group";
 
 import renderWhyPaused from "./WhyPaused";
 
 import actions from "../../../actions";
 import { collapseFrames, formatCopyName } from "../../../utils/pause/frames";
 import { copyToTheClipboard } from "../../../utils/clipboard";
 
 import {
   getFrameworkGroupingState,
   getSelectedFrame,
   getCallStackFrames,
   getPauseReason,
-  getCurrentThread
+  getCurrentThread,
+  getThreadContext
 } from "../../../selectors";
 
 import "./Frames.css";
 
 const NUM_FRAMES_SHOWN = 7;
 
 type Props = {
+  cx: ThreadContext,
   frames: Array<Frame>,
   frameworkGroupingOn: boolean,
   selectedFrame: Object,
   why: Why,
-  selectFrame: Function,
+  selectFrame: typeof actions.selectFrame,
   toggleBlackBox: Function,
   toggleFrameworkGrouping: Function,
   disableFrameTruncate: boolean,
   disableContextMenu: boolean,
   displayFullUrl: boolean,
   getFrameTitle?: string => string,
   selectable?: boolean
 };
@@ -109,16 +111,17 @@ class Frames extends Component<Props, St
 
   toggleFrameworkGrouping = () => {
     const { toggleFrameworkGrouping, frameworkGroupingOn } = this.props;
     toggleFrameworkGrouping(!frameworkGroupingOn);
   };
 
   renderFrames(frames: Frame[]) {
     const {
+      cx,
       selectFrame,
       selectedFrame,
       toggleBlackBox,
       frameworkGroupingOn,
       displayFullUrl,
       getFrameTitle,
       disableContextMenu,
       selectable = false
@@ -131,31 +134,33 @@ class Frames extends Component<Props, St
     // the user copies the trace. Needed for the console which has several
     // places where we don't want to have those new lines.
     return (
       <div role="list">
         {framesOrGroups.map(
           (frameOrGroup: FrameOrGroup) =>
             frameOrGroup.id ? (
               <FrameComponent
+                cx={cx}
                 frame={(frameOrGroup: any)}
                 toggleFrameworkGrouping={this.toggleFrameworkGrouping}
                 copyStackTrace={this.copyStackTrace}
                 frameworkGroupingOn={frameworkGroupingOn}
                 selectFrame={selectFrame}
                 selectedFrame={selectedFrame}
                 toggleBlackBox={toggleBlackBox}
                 key={String(frameOrGroup.id)}
                 displayFullUrl={displayFullUrl}
                 getFrameTitle={getFrameTitle}
                 disableContextMenu={disableContextMenu}
                 selectable={selectable}
               />
             ) : (
               <Group
+                cx={cx}
                 group={(frameOrGroup: any)}
                 toggleFrameworkGrouping={this.toggleFrameworkGrouping}
                 copyStackTrace={this.copyStackTrace}
                 frameworkGroupingOn={frameworkGroupingOn}
                 selectFrame={selectFrame}
                 selectedFrame={selectedFrame}
                 toggleBlackBox={toggleBlackBox}
                 key={frameOrGroup[0].id}
@@ -211,16 +216,17 @@ class Frames extends Component<Props, St
       </div>
     );
   }
 }
 
 Frames.contextTypes = { l10n: PropTypes.object };
 
 const mapStateToProps = state => ({
+  cx: getThreadContext(state),
   frames: getCallStackFrames(state),
   why: getPauseReason(state, getCurrentThread(state)),
   frameworkGroupingOn: getFrameworkGroupingState(state),
   selectedFrame: getSelectedFrame(state, getCurrentThread(state))
 });
 
 export default connect(
   mapStateToProps,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/Frame.spec.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/Frame.spec.js
@@ -2,23 +2,28 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React from "react";
 import { shallow, mount } from "enzyme";
 import Frame from "../Frame.js";
-import { makeMockFrame, makeMockSource } from "../../../../utils/test-mockup";
+import {
+  makeMockFrame,
+  makeMockSource,
+  mockthreadcx
+} from "../../../../utils/test-mockup";
 
 import FrameMenu from "../FrameMenu";
 jest.mock("../FrameMenu", () => jest.fn());
 
 function frameProperties(frame, selectedFrame: any, overrides = {}) {
   return {
+    cx: mockthreadcx,
     frame,
     selectedFrame,
     copyStackTrace: jest.fn(),
     contextTypes: {},
     selectFrame: jest.fn(),
     toggleBlackBox: jest.fn(),
     displayFullUrl: false,
     frameworkGroupingOn: false,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/Group.spec.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/Group.spec.js
@@ -2,24 +2,29 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React from "react";
 import { shallow } from "enzyme";
 import Group from "../Group.js";
-import { makeMockFrame, makeMockSource } from "../../../../utils/test-mockup";
+import {
+  makeMockFrame,
+  makeMockSource,
+  mockthreadcx
+} from "../../../../utils/test-mockup";
 
 import FrameMenu from "../FrameMenu";
 jest.mock("../FrameMenu", () => jest.fn());
 
 function render(overrides = {}) {
   const frame = { ...makeMockFrame(), displayName: "foo", library: "Back" };
   const defaultProps = {
+    cx: mockthreadcx,
     group: [frame],
     selectedFrame: frame,
     frameworkGroupingOn: true,
     toggleFrameworkGrouping: jest.fn(),
     selectFrame: jest.fn(),
     copyStackTrace: jest.fn(),
     toggleBlackBox: jest.fn(),
     disableContextMenu: false,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/__snapshots__/Group.spec.js.snap
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/__snapshots__/Group.spec.js.snap
@@ -150,16 +150,24 @@ exports[`Group passes the getFrameTitle 
   <div
     className="frames-list"
   >
     <FrameIndent
       key="frame-indent-0"
     />
     <Frame
       copyStackTrace={[MockFunction]}
+      cx={
+        Object {
+          "isPaused": false,
+          "navigateCounter": 0,
+          "pauseCounter": 0,
+          "thread": "FakeThread",
+        }
+      }
       disableContextMenu={false}
       displayFullUrl={false}
       frame={
         Object {
           "displayName": "renderFoo",
           "generatedLocation": Object {
             "line": 55,
             "sourceId": "source",
@@ -253,16 +261,24 @@ exports[`Group passes the getFrameTitle 
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[MockFunction]}
     />
     <FrameIndent
       key="frame-indent-1"
     />
     <Frame
       copyStackTrace={[MockFunction]}
+      cx={
+        Object {
+          "isPaused": false,
+          "navigateCounter": 0,
+          "pauseCounter": 0,
+          "thread": "FakeThread",
+        }
+      }
       disableContextMenu={false}
       displayFullUrl={false}
       frame={
         Object {
           "displayName": "a",
           "generatedLocation": Object {
             "line": 55,
             "sourceId": "source",
@@ -356,16 +372,24 @@ exports[`Group passes the getFrameTitle 
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[MockFunction]}
     />
     <FrameIndent
       key="frame-indent-2"
     />
     <Frame
       copyStackTrace={[MockFunction]}
+      cx={
+        Object {
+          "isPaused": false,
+          "navigateCounter": 0,
+          "pauseCounter": 0,
+          "thread": "FakeThread",
+        }
+      }
       disableContextMenu={false}
       displayFullUrl={false}
       frame={
         Object {
           "displayName": "b",
           "generatedLocation": Object {
             "line": 55,
             "sourceId": "source",
@@ -613,16 +637,24 @@ exports[`Group renders group with anonym
   <div
     className="frames-list"
   >
     <FrameIndent
       key="frame-indent-0"
     />
     <Frame
       copyStackTrace={[MockFunction]}
+      cx={
+        Object {
+          "isPaused": false,
+          "navigateCounter": 0,
+          "pauseCounter": 0,
+          "thread": "FakeThread",
+        }
+      }
       disableContextMenu={false}
       displayFullUrl={false}
       frame={
         Object {
           "displayName": "display-1",
           "generatedLocation": Object {
             "line": 55,
             "sourceId": "source",
@@ -715,16 +747,24 @@ exports[`Group renders group with anonym
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[MockFunction]}
     />
     <FrameIndent
       key="frame-indent-1"
     />
     <Frame
       copyStackTrace={[MockFunction]}
+      cx={
+        Object {
+          "isPaused": false,
+          "navigateCounter": 0,
+          "pauseCounter": 0,
+          "thread": "FakeThread",
+        }
+      }
       disableContextMenu={false}
       displayFullUrl={false}
       frame={
         Object {
           "displayName": "display-2",
           "generatedLocation": Object {
             "line": 55,
             "sourceId": "source",
@@ -817,16 +857,24 @@ exports[`Group renders group with anonym
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[MockFunction]}
     />
     <FrameIndent
       key="frame-indent-2"
     />
     <Frame
       copyStackTrace={[MockFunction]}
+      cx={
+        Object {
+          "isPaused": false,
+          "navigateCounter": 0,
+          "pauseCounter": 0,
+          "thread": "FakeThread",
+        }
+      }
       disableContextMenu={false}
       displayFullUrl={false}
       frame={
         Object {
           "displayName": "display-3",
           "generatedLocation": Object {
             "line": 55,
             "sourceId": "source",
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Worker.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Worker.js
@@ -4,33 +4,34 @@
 
 // @flow
 
 import React, { Component } from "react";
 import { connect } from "../../utils/connect";
 import classnames from "classnames";
 
 import actions from "../../actions";
-import { getCurrentThread, getIsPaused } from "../../selectors";
+import { getCurrentThread, getIsPaused, getContext } from "../../selectors";
 import { getDisplayName, isWorker } from "../../utils/workers";
 import AccessibleImage from "../shared/AccessibleImage";
 
-import type { Thread } from "../../types";
+import type { Context, Thread } from "../../types";
 
 type Props = {
+  cx: Context,
   selectThread: typeof actions.selectThread,
   isPaused: boolean,
   thread: Thread,
   currentThread: string
 };
 
 export class Worker extends Component<Props> {
   onSelectThread = () => {
     const { thread } = this.props;
-    this.props.selectThread(thread.actor);
+    this.props.selectThread(this.props.cx, thread.actor);
   };
 
   render() {
     const { currentThread, isPaused, thread } = this.props;
 
     const worker = isWorker(thread);
     const label = worker ? getDisplayName(thread) : L10N.getStr("mainThread");
 
@@ -52,16 +53,17 @@ export class Worker extends Component<Pr
           </div>
         ) : null}
       </div>
     );
   }
 }
 
 const mapStateToProps = (state, props: Props) => ({
+  cx: getContext(state),
   currentThread: getCurrentThread(state),
   isPaused: getIsPaused(state, props.thread.actor)
 });
 
 export default connect(
   mapStateToProps,
   {
     selectThread: actions.selectThread
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/index.js
@@ -16,17 +16,18 @@ import {
   getBreakpointsDisabled,
   getExpressions,
   getIsWaitingOnBreak,
   getMapScopes,
   getSelectedFrame,
   getShouldPauseOnExceptions,
   getShouldPauseOnCaughtExceptions,
   getWorkers,
-  getCurrentThread
+  getCurrentThread,
+  getThreadContext
 } from "../../selectors";
 
 import AccessibleImage from "../shared/AccessibleImage";
 import { prefs, features } from "../../utils/prefs";
 
 import Breakpoints from "./Breakpoints";
 import Expressions from "./Expressions";
 import SplitBox from "devtools-splitter";
@@ -37,17 +38,17 @@ import CommandBar from "./CommandBar";
 import UtilsBar from "./UtilsBar";
 import XHRBreakpoints from "./XHRBreakpoints";
 import EventListeners from "./EventListeners";
 
 import Scopes from "./Scopes";
 
 import "./SecondaryPanes.css";
 
-import type { Expression, Frame, WorkerList } from "../../types";
+import type { Expression, Frame, WorkerList, ThreadContext } from "../../types";
 
 type AccordionPaneItem = {
   header: string,
   component: any,
   opened?: boolean,
   onToggle?: () => void,
   shouldOpen?: () => boolean,
   buttons?: any
@@ -67,16 +68,17 @@ function debugBtn(onClick, type, classNa
 }
 
 type State = {
   showExpressionsInput: boolean,
   showXHRInput: boolean
 };
 
 type Props = {
+  cx: ThreadContext,
   expressions: List<Expression>,
   hasFrames: boolean,
   horizontal: boolean,
   breakpoints: Object,
   selectedFrame: ?Frame,
   breakpointsDisabled: boolean,
   isWaitingOnBreak: boolean,
   shouldMapScopes: boolean,
@@ -109,16 +111,17 @@ class SecondaryPanes extends Component<P
   };
 
   onXHRAdded = () => {
     this.setState({ showXHRInput: false });
   };
 
   renderBreakpointsToggle() {
     const {
+      cx,
       toggleAllBreakpoints,
       breakpoints,
       breakpointsDisabled
     } = this.props;
     const isIndeterminate =
       !breakpointsDisabled && breakpoints.some(x => x.disabled);
 
     if (features.skipPausing || breakpoints.length === 0) {
@@ -130,17 +133,17 @@ class SecondaryPanes extends Component<P
       "aria-label": breakpointsDisabled
         ? L10N.getStr("breakpoints.enable")
         : L10N.getStr("breakpoints.disable"),
       className: "breakpoints-toggle",
       disabled: false,
       key: "breakpoints-toggle",
       onChange: e => {
         e.stopPropagation();
-        toggleAllBreakpoints(!breakpointsDisabled);
+        toggleAllBreakpoints(cx, !breakpointsDisabled);
       },
       onClick: e => e.stopPropagation(),
       checked: !breakpointsDisabled && !isIndeterminate,
       ref: input => {
         if (input) {
           input.indeterminate = isIndeterminate;
         }
       },
@@ -157,17 +160,17 @@ class SecondaryPanes extends Component<P
 
     const buttons = [];
 
     if (expressions.size) {
       buttons.push(
         debugBtn(
           evt => {
             evt.stopPropagation();
-            this.props.evaluateExpressions();
+            this.props.evaluateExpressions(this.props.cx);
           },
           "refresh",
           "refresh",
           L10N.getStr("watchExpressions.refreshButton")
         )
       );
     }
 
@@ -457,16 +460,17 @@ class SecondaryPanes extends Component<P
     );
   }
 }
 
 const mapStateToProps = state => {
   const thread = getCurrentThread(state);
 
   return {
+    cx: getThreadContext(state),
     expressions: getExpressions(state),
     hasFrames: !!getTopFrame(state, thread),
     breakpoints: getBreakpointsList(state),
     breakpointsDisabled: getBreakpointsDisabled(state),
     isWaitingOnBreak: getIsWaitingOnBreak(state, thread),
     selectedFrame: getSelectedFrame(state, thread),
     shouldMapScopes: getMapScopes(state),
     shouldPauseOnExceptions: getShouldPauseOnExceptions(state),
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/tests/CommandBar.spec.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/tests/CommandBar.spec.js
@@ -2,20 +2,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React from "react";
 import { shallow } from "enzyme";
 import CommandBar from "../CommandBar";
+import { mockthreadcx } from "../../../utils/test-mockup";
 
 describe("CommandBar", () => {
   it("f8 key command calls props.breakOnNext when not in paused state", () => {
     const props = {
+      cx: mockthreadcx,
       breakOnNext: jest.fn(),
       resume: jest.fn(),
       isPaused: false
     };
     const mockEvent = {
       preventDefault: jest.fn(),
       stopPropagation: jest.fn()
     };
@@ -38,16 +40,17 @@ describe("CommandBar", () => {
     });
 
     expect(props.breakOnNext).toBeCalled();
     expect(props.resume).not.toBeCalled();
   });
 
   it("f8 key command calls props.resume when in paused state", () => {
     const props = {
+      cx: { ...mockthreadcx, isPaused: true },
       breakOnNext: jest.fn(),
       resume: jest.fn(),
       isPaused: true
     };
     const mockEvent = {
       preventDefault: jest.fn(),
       stopPropagation: jest.fn()
     };
--- a/devtools/client/debugger/new/src/components/test/Outline.spec.js
+++ b/devtools/client/debugger/new/src/components/test/Outline.spec.js
@@ -3,27 +3,29 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React from "react";
 import { shallow } from "enzyme";
 import Outline from "../../components/PrimaryPanes/Outline";
 import { makeSymbolDeclaration } from "../../utils/test-head";
+import { mockcx } from "../../utils/test-mockup";
 import { showMenu } from "devtools-contextmenu";
 import { copyToTheClipboard } from "../../utils/clipboard";
 
 jest.mock("devtools-contextmenu", () => ({ showMenu: jest.fn() }));
 jest.mock("../../utils/clipboard", () => ({ copyToTheClipboard: jest.fn() }));
 
 const sourceId = "id";
 const mockFunctionText = "mock function text";
 
 function generateDefaults(overrides) {
   return {
+    cx: mockcx,
     selectLocation: jest.fn(),
     selectedSource: { id: sourceId },
     getFunctionText: jest.fn().mockReturnValue(mockFunctionText),
     flashLineRange: jest.fn(),
     isHidden: false,
     symbols: {},
     selectedLocation: {
       sourceId: sourceId
@@ -65,17 +67,20 @@ describe("Outline", () => {
       functions: [makeSymbolDeclaration("my_example_function", startLine)]
     };
 
     const { component, props } = render({ symbols });
 
     const { selectLocation } = props;
     const listItem = component.find("li").first();
     listItem.simulate("click");
-    expect(selectLocation).toHaveBeenCalledWith({ line: startLine, sourceId });
+    expect(selectLocation).toHaveBeenCalledWith(mockcx, {
+      line: startLine,
+      sourceId
+    });
   });
 
   describe("renders outline", () => {
     describe("renders loading", () => {
       it("if symbols is not defined", () => {
         const { component } = render({
           symbols: (null: any)
         });
@@ -205,17 +210,17 @@ describe("Outline", () => {
         functions: [makeSymbolDeclaration("x_function", 25, 26, "x_klass")],
         classes: [makeSymbolDeclaration("x_klass", 24, 27)]
       };
 
       const { component, props } = render({ symbols: symbols });
 
       await component.find("h2").simulate("click", {});
 
-      expect(props.selectLocation).toHaveBeenCalledWith({
+      expect(props.selectLocation).toHaveBeenCalledWith(mockcx, {
         line: 24,
         sourceId: sourceId
       });
     });
 
     it("does not select an item if selectedSource is not defined", async () => {
       const { instance, props } = render({ selectedSource: (null: any) });
       await instance.selectItem({});
--- a/devtools/client/debugger/new/src/components/test/ProjectSearch.spec.js
+++ b/devtools/client/debugger/new/src/components/test/ProjectSearch.spec.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React from "react";
 import { mount, shallow } from "enzyme";
 import { ProjectSearch } from "../ProjectSearch";
 import { statusType } from "../../reducers/project-text-search";
+import { mockcx } from "../../utils/test-mockup";
 
 const hooks = { on: [], off: [] };
 const shortcuts = {
   dispatch(eventName) {
     hooks.on.forEach(hook => {
       if (hook.event === eventName) {
         hook.cb();
       }
@@ -70,16 +71,17 @@ const testMatch = {
   value: "some thing match1",
   sourceId: "some-target/source42",
   line: 3,
   column: 30
 };
 
 function render(overrides = {}, mounted = false) {
   const props = {
+    cx: mockcx,
     status: "DONE",
     sources: {},
     results: [],
     query: "foo",
     activeSearch: "project",
     closeProjectSearch: jest.fn(),
     searchSources: jest.fn(),
     clearSearch: jest.fn(),
@@ -182,17 +184,17 @@ describe("ProjectSearch", () => {
         results: testResults,
         searchSources
       },
       true
     );
     component
       .find("SearchInput input")
       .simulate("keydown", { key: "Enter", stopPropagation: jest.fn() });
-    expect(searchSources).toHaveBeenCalledWith("foo");
+    expect(searchSources).toHaveBeenCalledWith(mockcx, "foo");
   });
 
   it("onEnterPress shortcut no match or setExpanded", () => {
     const selectSpecificLocation = jest.fn();
     const component = render(
       {
         results: testResults,
         selectSpecificLocation
@@ -210,17 +212,17 @@ describe("ProjectSearch", () => {
       {
         results: testResults,
         selectSpecificLocation
       },
       true
     );
     component.instance().state.focusedItem = { ...testMatch };
     shortcuts.dispatch("Enter");
-    expect(selectSpecificLocation).toHaveBeenCalledWith({
+    expect(selectSpecificLocation).toHaveBeenCalledWith(mockcx, {
       sourceId: "some-target/source42",
       line: 3,
       column: 30
     });
   });
 
   it("state.inputValue responds to prop.query changes", () => {
     const component = render({ query: "foo" });
--- a/devtools/client/debugger/new/src/components/test/QuickOpenModal.spec.js
+++ b/devtools/client/debugger/new/src/components/test/QuickOpenModal.spec.js
@@ -3,23 +3,25 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React from "react";
 import { shallow, mount } from "enzyme";
 import { QuickOpenModal } from "../QuickOpenModal";
+import { mockcx } from "../../utils/test-mockup";
 
 jest.mock("fuzzaldrin-plus");
 
 import { filter } from "fuzzaldrin-plus";
 
 function generateModal(propOverrides, renderType = "shallow") {
   const props = {
+    cx: mockcx,
     enabled: false,
     query: "",
     searchType: "sources",
     sources: [],
     tabs: [],
     selectSpecificLocation: jest.fn(),
     setQuickOpenQuery: jest.fn(),
     highlightLineRange: jest.fn(),
@@ -309,17 +311,17 @@ describe("QuickOpenModal", () => {
           searchType: "goto"
         },
         "shallow"
       );
       const event = {
         key: "Enter"
       };
       wrapper.find("SearchInput").simulate("keydown", event);
-      expect(props.selectSpecificLocation).toHaveBeenCalledWith({
+      expect(props.selectSpecificLocation).toHaveBeenCalledWith(mockcx, {
         column: 12,
         line: 34,
         sourceId: ""
       });
     });
 
     it("on Enter go to location with sourceId", () => {
       const sourceId = "source_id";
@@ -331,17 +333,17 @@ describe("QuickOpenModal", () => {
           selectedSource: { id: sourceId }
         },
         "shallow"
       );
       const event = {
         key: "Enter"
       };
       wrapper.find("SearchInput").simulate("keydown", event);
-      expect(props.selectSpecificLocation).toHaveBeenCalledWith({
+      expect(props.selectSpecificLocation).toHaveBeenCalledWith(mockcx, {
         column: 12,
         line: 34,
         sourceId: sourceId
       });
     });
 
     it("on Enter with no location, does no action", () => {
       const { wrapper, props } = generateModal(
@@ -442,17 +444,17 @@ describe("QuickOpenModal", () => {
       wrapper.setState(() => ({
         results: [{}, { id }],
         selectedIndex: 1
       }));
       const event = {
         key: "Enter"
       };
       wrapper.find("SearchInput").simulate("keydown", event);
-      expect(props.selectSpecificLocation).toHaveBeenCalledWith({
+      expect(props.selectSpecificLocation).toHaveBeenCalledWith(mockcx, {
         column: undefined,
         sourceId: id,
         line: 0
       });
       expect(props.setQuickOpenQuery).not.toHaveBeenCalled();
     });
 
     it("on Enter with results, handle functions result item", () => {
@@ -472,17 +474,17 @@ describe("QuickOpenModal", () => {
       wrapper.setState(() => ({
         results: [{}, { id }],
         selectedIndex: 1
       }));
       const event = {
         key: "Enter"
       };
       wrapper.find("SearchInput").simulate("keydown", event);
-      expect(props.selectSpecificLocation).toHaveBeenCalledWith({
+      expect(props.selectSpecificLocation).toHaveBeenCalledWith(mockcx, {
         column: undefined,
         line: 0,
         sourceId: ""
       });
       expect(props.setQuickOpenQuery).not.toHaveBeenCalled();
     });
 
     it("on Enter with results, handle gotoSource search", () => {
@@ -502,17 +504,17 @@ describe("QuickOpenModal", () => {
       wrapper.setState(() => ({
         results: [{}, { id }],
         selectedIndex: 1
       }));
       const event = {
         key: "Enter"
       };
       wrapper.find("SearchInput").simulate("keydown", event);
-      expect(props.selectSpecificLocation).toHaveBeenCalledWith({
+      expect(props.selectSpecificLocation).toHaveBeenCalledWith(mockcx, {
         column: 4,
         line: 3,
         sourceId: id
       });
       expect(props.setQuickOpenQuery).not.toHaveBeenCalled();
     });
 
     it("on Enter with results, handle shortcuts search", () => {
--- a/devtools/client/debugger/new/src/components/test/__snapshots__/ProjectSearch.spec.js.snap
+++ b/devtools/client/debugger/new/src/components/test/__snapshots__/ProjectSearch.spec.js.snap
@@ -39,16 +39,21 @@ exports[`ProjectSearch found no search r
 </div>
 `;
 
 exports[`ProjectSearch found search results 1`] = `
 <ProjectSearch
   activeSearch="project"
   clearSearch={[MockFunction]}
   closeProjectSearch={[MockFunction]}
+  cx={
+    Object {
+      "navigateCounter": 0,
+    }
+  }
   doSearchForHighlight={[MockFunction]}
   query="match"
   results={
     Array [
       Object {
         "filepath": "testFilePath1",
         "matches": Array [
           Object {
--- a/devtools/client/debugger/new/src/components/test/__snapshots__/QuickOpenModal.spec.js.snap
+++ b/devtools/client/debugger/new/src/components/test/__snapshots__/QuickOpenModal.spec.js.snap
@@ -1,14 +1,19 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`QuickOpenModal Basic render with mount & searchType = functions 1`] = `
 <QuickOpenModal
   clearHighlightLineRange={[MockFunction]}
   closeQuickOpen={[MockFunction]}
+  cx={
+    Object {
+      "navigateCounter": 0,
+    }
+  }
   enabled={true}
   highlightLineRange={[MockFunction]}
   isOriginal={false}
   query="@"
   searchType="functions"
   selectSpecificLocation={[MockFunction]}
   setQuickOpenQuery={[MockFunction]}
   shortcutsModalEnabled={false}
@@ -127,16 +132,21 @@ exports[`QuickOpenModal Basic render wit
   </Slide>
 </QuickOpenModal>
 `;
 
 exports[`QuickOpenModal Basic render with mount & searchType = variables 1`] = `
 <QuickOpenModal
   clearHighlightLineRange={[MockFunction]}
   closeQuickOpen={[MockFunction]}
+  cx={
+    Object {
+      "navigateCounter": 0,
+    }
+  }
   enabled={true}
   highlightLineRange={[MockFunction]}
   isOriginal={false}
   query="#"
   searchType="variables"
   selectSpecificLocation={[MockFunction]}
   setQuickOpenQuery={[MockFunction]}
   shortcutsModalEnabled={false}
@@ -239,16 +249,21 @@ exports[`QuickOpenModal Basic render wit
   </Slide>
 </QuickOpenModal>
 `;
 
 exports[`QuickOpenModal Basic render with mount 1`] = `
 <QuickOpenModal
   clearHighlightLineRange={[MockFunction]}
   closeQuickOpen={[MockFunction]}
+  cx={
+    Object {
+      "navigateCounter": 0,
+    }
+  }
   enabled={true}
   highlightLineRange={[MockFunction]}
   isOriginal={false}
   query=""
   searchType="sources"
   selectSpecificLocation={[MockFunction]}
   setQuickOpenQuery={[MockFunction]}
   shortcutsModalEnabled={false}
@@ -401,16 +416,21 @@ exports[`QuickOpenModal Renders when ena
   />
 </Slide>
 `;
 
 exports[`QuickOpenModal Simple goto search query = :abc & searchType = goto 1`] = `
 <QuickOpenModal
   clearHighlightLineRange={[MockFunction]}
   closeQuickOpen={[MockFunction]}
+  cx={
+    Object {
+      "navigateCounter": 0,
+    }
+  }
   enabled={true}
   highlightLineRange={[MockFunction]}
   isOriginal={false}
   query=":abc"
   searchType="goto"
   selectSpecificLocation={[MockFunction]}
   setQuickOpenQuery={[MockFunction]}
   shortcutsModalEnabled={false}
@@ -518,16 +538,21 @@ exports[`QuickOpenModal Simple goto sear
   </Slide>
 </QuickOpenModal>
 `;
 
 exports[`QuickOpenModal showErrorEmoji false when count + query 1`] = `
 <QuickOpenModal
   clearHighlightLineRange={[MockFunction]}
   closeQuickOpen={[MockFunction]}
+  cx={
+    Object {
+      "navigateCounter": 0,
+    }
+  }
   enabled={true}
   highlightLineRange={[MockFunction]}
   isOriginal={false}
   query="dasdasdas"
   searchType="sources"
   selectSpecificLocation={[MockFunction]}
   setQuickOpenQuery={[MockFunction]}
   shortcutsModalEnabled={false}
@@ -677,16 +702,21 @@ exports[`QuickOpenModal showErrorEmoji f
   </Slide>
 </QuickOpenModal>
 `;
 
 exports[`QuickOpenModal showErrorEmoji false when goto numeric ':2222' 1`] = `
 <QuickOpenModal
   clearHighlightLineRange={[MockFunction]}
   closeQuickOpen={[MockFunction]}
+  cx={
+    Object {
+      "navigateCounter": 0,
+    }
+  }
   enabled={true}
   highlightLineRange={[MockFunction]}
   isOriginal={false}
   query=":2222"
   searchType="goto"
   selectSpecificLocation={[MockFunction]}
   setQuickOpenQuery={[MockFunction]}
   shortcutsModalEnabled={false}
@@ -793,16 +823,21 @@ exports[`QuickOpenModal showErrorEmoji f
   </Slide>
 </QuickOpenModal>
 `;
 
 exports[`QuickOpenModal showErrorEmoji false when no query 1`] = `
 <QuickOpenModal
   clearHighlightLineRange={[MockFunction]}
   closeQuickOpen={[MockFunction]}
+  cx={
+    Object {
+      "navigateCounter": 0,
+    }
+  }
   enabled={true}
   highlightLineRange={[MockFunction]}
   isOriginal={false}
   query=""
   searchType=""
   selectSpecificLocation={[MockFunction]}
   setQuickOpenQuery={[MockFunction]}
   shortcutsModalEnabled={false}
@@ -920,16 +955,21 @@ exports[`QuickOpenModal showErrorEmoji f
   </Slide>
 </QuickOpenModal>
 `;
 
 exports[`QuickOpenModal showErrorEmoji true when goto not numeric ':22k22' 1`] = `
 <QuickOpenModal
   clearHighlightLineRange={[MockFunction]}
   closeQuickOpen={[MockFunction]}
+  cx={
+    Object {
+      "navigateCounter": 0,
+    }
+  }
   enabled={true}
   highlightLineRange={[MockFunction]}
   isOriginal={false}
   query=":22k22"
   searchType="goto"
   selectSpecificLocation={[MockFunction]}
   setQuickOpenQuery={[MockFunction]}
   shortcutsModalEnabled={false}
@@ -1036,16 +1076,21 @@ exports[`QuickOpenModal showErrorEmoji t
   </Slide>
 </QuickOpenModal>
 `;
 
 exports[`QuickOpenModal showErrorEmoji true when no count + query 1`] = `
 <QuickOpenModal
   clearHighlightLineRange={[MockFunction]}
   closeQuickOpen={[MockFunction]}
+  cx={
+    Object {
+      "navigateCounter": 0,
+    }
+  }
   enabled={true}
   highlightLineRange={[MockFunction]}
   isOriginal={false}
   query="test"
   searchType=""
   selectSpecificLocation={[MockFunction]}
   setQuickOpenQuery={[MockFunction]}
   shortcutsModalEnabled={false}
@@ -1180,16 +1225,21 @@ exports[`QuickOpenModal shows loading lo
   />
 </Slide>
 `;
 
 exports[`QuickOpenModal updateResults on enable 1`] = `
 <QuickOpenModal
   clearHighlightLineRange={[MockFunction]}
   closeQuickOpen={[MockFunction]}
+  cx={
+    Object {
+      "navigateCounter": 0,
+    }
+  }
   enabled={false}
   highlightLineRange={[MockFunction]}
   isOriginal={false}
   query=""
   searchType="sources"
   selectSpecificLocation={[MockFunction]}
   setQuickOpenQuery={[MockFunction]}
   shortcutsModalEnabled={false}
@@ -1205,16 +1255,21 @@ exports[`QuickOpenModal updateResults on
   toggleShortcutsModal={[MockFunction]}
 />
 `;
 
 exports[`QuickOpenModal updateResults on enable 2`] = `
 <QuickOpenModal
   clearHighlightLineRange={[MockFunction]}
   closeQuickOpen={[MockFunction]}
+  cx={
+    Object {
+      "navigateCounter": 0,
+    }
+  }
   enabled={true}
   highlightLineRange={[MockFunction]}
   isOriginal={false}
   query=""
   searchType="sources"
   selectSpecificLocation={[MockFunction]}
   setQuickOpenQuery={[MockFunction]}
   shortcutsModalEnabled={false}
--- a/devtools/client/debugger/new/src/reducers/pause.js
+++ b/devtools/client/debugger/new/src/reducers/pause.js
@@ -20,17 +20,19 @@ import type { Action } from "../actions/
 import type { State } from "./types";
 import type {
   Why,
   Scope,
   SourceId,
   ChromeFrame,
   FrameId,
   MappedLocation,
-  ThreadId
+  ThreadId,
+  Context,
+  ThreadContext
 } from "../types";
 
 export type Command =
   | null
   | "stepOver"
   | "stepIn"
   | "stepOut"
   | "resume"
@@ -68,34 +70,45 @@ type ThreadPauseState = {
   command: Command,
   lastCommand: Command,
   wasStepping: boolean,
   previousLocation: ?MappedLocation
 };
 
 // Pause state describing all threads.
 export type PauseState = {
-  currentThread: ThreadId,
+  cx: Context,
+  threadcx: ThreadContext,
   canRewind: boolean,
   threads: { [ThreadId]: ThreadPauseState },
   skipPausing: boolean,
   mapScopes: boolean,
   shouldPauseOnExceptions: boolean,
   shouldPauseOnCaughtExceptions: boolean
 };
 
-export const createPauseState = (): PauseState => ({
-  currentThread: "UnknownThread",
-  threads: {},
-  canRewind: false,
-  skipPausing: prefs.skipPausing,
-  mapScopes: prefs.mapScopes,
-  shouldPauseOnExceptions: prefs.pauseOnExceptions,
-  shouldPauseOnCaughtExceptions: prefs.pauseOnCaughtExceptions
-});
+function createPauseState(thread: ThreadId = "UnknownThread") {
+  return {
+    cx: {
+      navigateCounter: 0
+    },
+    threadcx: {
+      navigateCounter: 0,
+      thread,
+      isPaused: false,
+      pauseCounter: 0
+    },
+    threads: {},
+    canRewind: false,
+    skipPausing: prefs.skipPausing,
+    mapScopes: prefs.mapScopes,
+    shouldPauseOnExceptions: prefs.pauseOnExceptions,
+    shouldPauseOnCaughtExceptions: prefs.pauseOnCaughtExceptions
+  };
+}
 
 const resumedPauseState = {
   frames: null,
   frameScopes: {
     generated: {},
     original: {},
     mappings: {}
   },
@@ -141,29 +154,46 @@ function update(
       threads: {
         ...state.threads,
         [action.thread]: { ...threadState(), ...newThreadState }
       }
     };
   };
 
   switch (action.type) {
-    case "SELECT_THREAD":
-      return { ...state, currentThread: action.thread };
+    case "SELECT_THREAD": {
+      return {
+        ...state,
+        threadcx: {
+          ...state.threadcx,
+          thread: action.thread,
+          isPaused: !!threadState().frames,
+          pauseCounter: state.threadcx.pauseCounter + 1
+        }
+      };
+    }
 
     case "PAUSED": {
       const { thread, selectedFrameId, frames, loadedObjects, why } = action;
 
       // turn this into an object keyed by object id
       const objectMap = {};
       loadedObjects.forEach(obj => {
         objectMap[obj.value.objectId] = obj;
       });
 
-      state = { ...state, currentThread: thread };
+      state = {
+        ...state,
+        threadcx: {
+          ...state.threadcx,
+          pauseCounter: state.threadcx.pauseCounter + 1,
+          thread,
+          isPaused: true
+        }
+      };
       return updateThreadState({
         isWaitingOnBreak: false,
         selectedFrameId,
         frames,
         frameScopes: { ...resumedPauseState.frameScopes },
         loadedObjects: objectMap,
         why
       });
@@ -236,18 +266,17 @@ function update(
           ...threadState().loadedObjects,
           [action.objectId]: action.properties
         }
       });
     }
 
     case "CONNECT":
       return {
-        ...createPauseState(),
-        currentThread: action.mainThread.actor,
+        ...createPauseState(action.mainThread.actor),
         canRewind: action.canRewind
       };
 
     case "PAUSE_ON_EXCEPTIONS": {
       const { shouldPauseOnExceptions, shouldPauseOnCaughtExceptions } = action;
 
       prefs.pauseOnExceptions = shouldPauseOnExceptions;
       prefs.pauseOnCaughtExceptions = shouldPauseOnCaughtExceptions;
@@ -268,42 +297,59 @@ function update(
           ...resumedPauseState,
           command: action.command,
           lastCommand: action.command,
           previousLocation: getPauseLocation(threadState(), action)
         });
       }
       return updateThreadState({ command: null });
 
-    case "RESUME":
-      // Workaround for threads resuming before the initial connection.
-      if (!action.thread && !state.currentThread) {
-        return state;
+    case "RESUME": {
+      if (action.thread == state.threadcx.thread) {
+        state = {
+          ...state,
+          threadcx: {
+            ...state.threadcx,
+            pauseCounter: state.threadcx.pauseCounter + 1,
+            isPaused: false
+          }
+        };
       }
       return updateThreadState({
         ...resumedPauseState,
         wasStepping: !!action.wasStepping
       });
+    }
 
     case "EVALUATE_EXPRESSION":
       return updateThreadState({
         command: action.status === "start" ? "expression" : null
       });
 
-    case "NAVIGATE":
+    case "NAVIGATE": {
+      const navigateCounter = state.cx.navigateCounter + 1;
       return {
         ...state,
-        currentThread: action.mainThread.actor,
+        cx: {
+          navigateCounter
+        },
+        threadcx: {
+          navigateCounter,
+          thread: action.mainThread.actor,
+          pauseCounter: 0,
+          isPaused: false
+        },
         threads: {
           [action.mainThread.actor]: {
             ...state.threads[action.mainThread.actor],
             ...resumedPauseState
           }
         }
       };
+    }
 
     case "TOGGLE_SKIP_PAUSING": {
       const { skipPausing } = action;
       prefs.skipPausing = skipPausing;
 
       return { ...state, skipPausing };
     }
 
@@ -343,16 +389,24 @@ function getPauseLocation(state, action)
 // the state that we care about and still type it with Flow. The
 // problem is that we want to re-export all selectors from a single
 // module for the UI, and all of those selectors should take the
 // top-level app state, so we'd have to "wrap" them to automatically
 // pick off the piece of state we're interested in. It's impossible
 // (right now) to type those wrapped functions.
 type OuterState = State;
 
+export function getContext(state: OuterState) {
+  return state.pause.cx;
+}
+
+export function getThreadContext(state: OuterState) {
+  return state.pause.threadcx;
+}
+
 export function getAllPopupObjectProperties(
   state: OuterState,
   thread: ThreadId
 ) {
   return getThreadPauseState(state.pause, thread).loadedObjects;
 }
 
 export function getPauseReason(state: OuterState, thread: ThreadId): ?Why {
@@ -369,17 +423,17 @@ export function wasStepping(state: Outer
 
 export function isStepping(state: OuterState, thread: ThreadId) {
   return ["stepIn", "stepOver", "stepOut"].includes(
     getPauseCommand(state, thread)
   );
 }
 
 export function getCurrentThread(state: OuterState) {
-  return state.pause.currentThread;
+  return getThreadContext(state).thread;
 }
 
 export function getIsPaused(state: OuterState, thread: ThreadId) {
   return !!getThreadPauseState(state.pause, thread).frames;
 }
 
 export function getPreviousPauseFrameLocation(
   state: OuterState,
--- a/devtools/client/debugger/new/src/reducers/sources.js
+++ b/devtools/client/debugger/new/src/reducers/sources.js
@@ -22,17 +22,17 @@ import {
 
 import { originalToGeneratedId } from "devtools-source-map";
 import { prefs } from "../utils/prefs";
 
 import type { Source, SourceId, SourceLocation, ThreadId } from "../types";
 import type { PendingSelectedLocation, Selector } from "./types";
 import type { Action, DonePromiseAction, FocusItem } from "../actions/types";
 import type { LoadSourceAction } from "../actions/types/SourceAction";
-import { mapValues, uniqBy, uniq } from "lodash";
+import { mapValues, uniqBy } from "lodash";
 
 export type SourcesMap = { [SourceId]: Source };
 export type SourcesMapByThread = { [ThreadId]: SourcesMap };
 
 type UrlsMap = { [string]: SourceId[] };
 type DisplayedSources = { [ThreadId]: { [SourceId]: boolean } };
 type GetDisplayedSourcesSelector = OuterState => { [ThreadId]: SourcesMap };
 type PlainUrlsMap = { [string]: string[] };
@@ -384,25 +384,16 @@ function getSourceActors(state, source) 
     return source.actors;
   }
 
   // Original sources do not have actors, so use the generated source.
   const generatedSource = state.sources[originalToGeneratedId(source.id)];
   return generatedSource ? generatedSource.actors : [];
 }
 
-export function getSourceThreads(
-  state: OuterState,
-  source: Source
-): ThreadId[] {
-  return uniq(
-    getSourceActors(state.sources, source).map(actor => actor.thread)
-  );
-}
-
 export function getSourceInSources(sources: SourcesMap, id: string): ?Source {
   return sources[id];
 }
 
 export function getSource(state: OuterState, id: SourceId): ?Source {
   return getSourceInSources(getSources(state), id);
 }
 
--- a/devtools/client/debugger/new/src/reducers/tests/sources.spec.js
+++ b/devtools/client/debugger/new/src/reducers/tests/sources.spec.js
@@ -5,17 +5,17 @@
 // @flow
 declare var describe: (name: string, func: () => void) => void;
 declare var it: (desc: string, func: () => void) => void;
 declare var expect: (value: any) => any;
 
 import update, { initialSourcesState, getDisplayedSources } from "../sources";
 import type { Source } from "../../types";
 import { prefs } from "../../utils/prefs";
-import { makeMockSource } from "../../utils/test-mockup";
+import { makeMockSource, mockcx } from "../../utils/test-mockup";
 
 const extensionSource = {
   ...makeMockSource(),
   id: "extensionId",
   url: "http://example.com/script.js",
   actors: [{ actor: "extensionId-actor", source: "extensionId", thread: "foo" }]
 };
 
@@ -49,44 +49,47 @@ const mockedSources = [
   chromeExtensionSource
 ];
 
 describe("sources reducer", () => {
   it("should work", () => {
     let state = initialSourcesState();
     state = update(state, {
       type: "ADD_SOURCE",
+      cx: mockcx,
       source: makeMockSource()
     });
     expect(Object.keys(state.sources)).toHaveLength(1);
   });
 });
 
 describe("sources selectors", () => {
   it("should return all extensions when chrome preference enabled", () => {
     prefs.chromeAndExtenstionsEnabled = true;
     let state = initialSourcesState();
     state = {
       sources: update(state, {
         type: "ADD_SOURCES",
+        cx: mockcx,
         // coercing to a Source for the purpose of this test
         sources: ((mockedSources: any): Source[])
       })
     };
     const selectedDisplayedSources = getDisplayedSources(state);
     const threadSources = selectedDisplayedSources.foo;
     expect(Object.values(threadSources)).toHaveLength(3);
   });
 
   it("should omit all extensions when chrome preference enabled", () => {
     prefs.chromeAndExtenstionsEnabled = false;
     let state = initialSourcesState();
     state = {
       sources: update(state, {
         type: "ADD_SOURCES",
+        cx: mockcx,
         // coercing to a Source for the purpose of this test
         sources: ((mockedSources: any): Source[])
       })
     };
     const selectedDisplayedSources = getDisplayedSources(state);
     const threadSources = selectedDisplayedSources.foo;
     expect(Object.values(threadSources)).toHaveLength(1);
   });
--- a/devtools/client/debugger/new/src/types.js
+++ b/devtools/client/debugger/new/src/types.js
@@ -452,8 +452,10 @@ export type Cancellable = {
 };
 
 export type EventListenerBreakpoints = string[];
 
 export type SourceDocuments = { [string]: Object };
 
 export type BreakpointPosition = MappedLocation;
 export type BreakpointPositions = BreakpointPosition[];
+
+export type { Context, ThreadContext } from "./utils/context";
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/utils/context.js
@@ -0,0 +1,81 @@
+/* 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 { ThreadId } from "../types";
+import type { State } from "../reducers/types";
+
+import { getThreadContext } from "../selectors";
+
+// Context encapsulates the main parameters of the current redux state, which
+// impact most other information tracked by the debugger.
+//
+// The main use of Context is to control when asynchronous operations are
+// allowed to make changes to the program state. Such operations might be
+// invalidated as the state changes from the time the operation was originally
+// initiated. For example, operations on pause state might still continue even
+// after the thread unpauses.
+//
+// The methods below can be used to compare an old context with the current one
+// and see if the operation is now invalid and should be abandoned. Actions can
+// also include a 'cx' Context property, which will be checked by the context
+// middleware. If the action fails validateContextAction() then it will not be
+// dispatched.
+//
+// Context can additionally be used as a shortcut to access the main properties
+// of the pause state.
+
+// A normal Context is invalidated if the target navigates.
+export type NavigateContext = {|
+  // Counter reflecting how many times the debugger has navigated to a new page
+  // and reset most of its state.
+  +navigateCounter: number
+|};
+
+// A ThreadContext is invalidated if the target navigates, or if the current
+// thread changes, pauses, or resumes.
+export type ThreadContext = {|
+  +navigateCounter: number,
+
+  // The currently selected thread.
+  +thread: ThreadId,
+
+  // Counter reflecting how many times the selected thread has paused or
+  // resumed.
+  +pauseCounter: number,
+
+  // Whether the current thread is paused. This is determined from the other
+  // Context properties and is here for convenient access.
+  +isPaused: boolean
+|};
+
+export type Context = NavigateContext | ThreadContext;
+
+export function validateNavigateContext(state: State, cx: Context) {
+  const newcx = getThreadContext(state);
+
+  if (newcx.navigateCounter != cx.navigateCounter) {
+    throw new Error("Page has navigated");
+  }
+}
+
+function validateThreadContext(state: State, cx: ThreadContext) {
+  const newcx = getThreadContext(state);
+
+  if (cx.thread != newcx.thread) {
+    throw new Error("Current thread has changed");
+  }
+
+  if (cx.pauseCounter != newcx.pauseCounter) {
+    throw new Error("Current thread has paused or resumed");
+  }
+}
+
+export function validateContext(state: State, cx: Context) {
+  validateNavigateContext(state, cx);
+
+  if ("thread" in cx) {
+    validateThreadContext(state, (cx: any));
+  }
+}
--- a/devtools/client/debugger/new/src/utils/moz.build
+++ b/devtools/client/debugger/new/src/utils/moz.build
@@ -13,16 +13,17 @@ DIRS += [
 CompiledModules(
     'assert.js',
     'ast.js',
     'asyncStoreHelper.js',
     'bootstrap.js',
     'build-query.js',
     'clipboard.js',
     'connect.js',
+    'context.js',
     'dbg.js',
     'defer.js',
     'DevToolsUtils.js',
     'empty-lines.js',
     'expressions.js',
     'fromJS.js',
     'function.js',
     'indentation.js',
--- a/devtools/client/debugger/new/src/utils/test-head.js
+++ b/devtools/client/debugger/new/src/utils/test-head.js
@@ -48,16 +48,19 @@ function createStore(client: any, initia
   store.thunkArgs = () => ({
     dispatch: store.dispatch,
     getState: store.getState,
     client,
     sourceMaps,
     panel: {}
   });
 
+  // Put the initial context in the store, for convenience to unit tests.
+  store.cx = selectors.getThreadContext(store.getState());
+
   return store;
 }
 
 /**
  * @memberof utils/test-head
  * @static
  */
 function commonLog(msg: string, data: any = {}) {
--- a/devtools/client/debugger/new/src/utils/test-mockup.js
+++ b/devtools/client/debugger/new/src/utils/test-mockup.js
@@ -149,20 +149,31 @@ function makeMockExpression(value: Objec
   return {
     input: "input",
     value,
     from: "from",
     updating: false
   };
 }
 
+// Mock contexts for use in tests that do not create a redux store.
+const mockcx = { navigateCounter: 0 };
+const mockthreadcx = {
+  navigateCounter: 0,
+  thread: "FakeThread",
+  pauseCounter: 0,
+  isPaused: false
+};
+
 export {
   makeMockSource,
   makeMockWasmSource,
   makeMockScope,
   mockScopeAddVariable,
   makeMockBreakpoint,
   makeMockFrame,
   makeMockFrameWithURL,
   makeWhyNormal,