Merge inbound to mozilla-central a=merge
authorCoroiu Cristina <ccoroiu@mozilla.com>
Fri, 21 Dec 2018 23:55:45 +0200
changeset 508841 b23630094b9c
parent 508809 74101900e7d4 (current diff)
parent 508840 21c1ab065df1 (diff)
child 508872 2071f03d7e9e
child 508899 f1ce520b1d0d
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
b23630094b9c / 66.0a1 / 20181221215622 / files
nightly linux64
b23630094b9c / 66.0a1 / 20181221215622 / files
nightly mac
b23630094b9c / 66.0a1 / 20181221215622 / files
nightly win32
b23630094b9c / 66.0a1 / 20181221215622 / files
nightly win64
b23630094b9c / 66.0a1 / 20181221215622 / 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
dom/svg/nsSVGFilters.cpp
dom/svg/nsSVGFilters.h
netwerk/cookie/nsCookieService.cpp
toolkit/components/antitracking/test/browser/browser.ini
--- a/browser/components/search/content/search-one-offs.js
+++ b/browser/components/search/content/search-one-offs.js
@@ -553,18 +553,18 @@ class SearchOneOffs {
 
     let engines = gBrowser.selectedBrowser.engines;
     let tooManyEngines = engines.length > this._addEngineMenuThreshold;
 
     if (tooManyEngines) {
       // Make the top-level menu button.
       let button = document.createXULElement("toolbarbutton");
       list.appendChild(button);
-      button.classList.add("addengine-item", "badged-button");
-      button.setAttribute("class", "addengine-menu-button");
+      button.classList.add("addengine-menu-button", "addengine-item",
+                           "badged-button");
       button.setAttribute("type", "menu");
       button.setAttribute("label",
         this.bundle.GetStringFromName("cmd_addFoundEngineMenu"));
       button.setAttribute("crop", "end");
       button.setAttribute("pack", "start");
 
       // Set the menu button's image to the image of the first engine.  The
       // offered engines may have differing images, so there's no perfect
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 110
+Version 111
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-109...release-110
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-110...release-111
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.2
 - babel-preset-react @6.24.1
 - react @16.4.1
 - react-dom @16.4.1
 - webpack @3.12.0
--- a/devtools/client/debugger/new/dist/debugger.css
+++ b/devtools/client/debugger/new/dist/debugger.css
@@ -1782,18 +1782,18 @@ html .toggle-button.end.vertical svg {
   padding: 0 10px 0 3px;
   width: 100%;
 }
 
 .sources-list .tree .img.arrow {
   margin-right: 5px;
 }
 
-.sources-list .tree .focused img:not(.vue):not(.angular),
-.sources-list .managed-tree .tree .node.focused img.blackBox {
+.sources-list .tree .focused .img:not(.vue):not(.angular),
+.sources-list .managed-tree .tree .node.focused .img.blackBox {
   background: #ffffff;
 }
 
 .sources-list .tree .label .suffix {
   font-style: italic;
   font-size: 0.9em;
   color: var(--theme-comment);
 }
@@ -1914,20 +1914,16 @@ html .toggle-button.end.vertical svg {
   width: 13px;
   height: 13px;
   display: inline-block;
   margin-inline-end: 6px;
   margin-inline-start: 1px;
   margin-top: 2px;
 }
 
-.theme-dark .sources-list .managed-tree .tree .node:not(.focused) .img.blackBox {
-  background-color: var(--theme-comment);
-}
-
 .theme-dark .sources-list .managed-tree .tree .node .img.blackBox {
   background-color: var(--theme-body-color);
 }
 
 /*
   Custom root styles
 */
 .sources-pane.sources-list-custom-root {
@@ -3695,16 +3691,20 @@ html[dir="rtl"] .breakpoints-list .break
   position: relative;
   transition: all 0.25s ease;
   cursor: pointer;
 }
 
 .worker-list .worker:hover {
   background-color: var(--search-overlays-semitransparent);
 }
+
+.worker-list .worker.selected {
+  background-color: var(--tab-line-selected-color);
+}
 /* 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/>. */
 
 :root {
   --accordion-header-background: var(--theme-toolbar-background);
   --disclosure-arrow: #b2b2b2;
 }
--- a/devtools/client/debugger/new/dist/parser-worker.js
+++ b/devtools/client/debugger/new/dist/parser-worker.js
@@ -1010,16 +1010,19 @@ exports.getObjectExpressionValue = getOb
 exports.getCode = getCode;
 exports.getComments = getComments;
 exports.getSpecifiers = getSpecifiers;
 exports.isComputedExpression = isComputedExpression;
 exports.getMemberExpression = getMemberExpression;
 exports.getVariables = getVariables;
 exports.getPatternIdentifiers = getPatternIdentifiers;
 exports.isTopLevel = isTopLevel;
+exports.nodeHasSameLocation = nodeHasSameLocation;
+exports.sameLocation = sameLocation;
+exports.getFunctionParameterNames = getFunctionParameterNames;
 
 var _types = __webpack_require__(2268);
 
 var t = _interopRequireWildcard(_types);
 
 var _generator = __webpack_require__(2365);
 
 var _generator2 = _interopRequireDefault(_generator);
@@ -1052,17 +1055,17 @@ function isObjectShorthand(parent) {
 
 function getObjectExpressionValue(node) {
   const { value } = node;
 
   if (t.isIdentifier(value)) {
     return value.name;
   }
 
-  if (t.isCallExpression(value)) {
+  if (t.isCallExpression(value) || t.isFunctionExpression(value)) {
     return "";
   }
   const code = (0, _generator2.default)(value).code;
 
   const shouldWrap = t.isObjectExpression(value);
   return shouldWrap ? `(${code})` : code;
 }
 
@@ -1173,16 +1176,48 @@ function getIdentifiers(items) {
 }
 
 // Top Level checks the number of "body" nodes in the ancestor chain
 // if the node is top-level, then it shoul only have one body.
 function isTopLevel(ancestors) {
   return ancestors.filter(ancestor => ancestor.key == "body").length == 1;
 }
 
+function nodeHasSameLocation(a, b) {
+  return sameLocation(a.location, b.location);
+}
+
+function sameLocation(a, b) {
+  return a.start.line == b.start.line && a.start.column == b.start.column && a.end.line == b.end.line && a.end.column == b.end.column;
+}
+
+function getFunctionParameterNames(path) {
+  if (path.node.params != null) {
+    return path.node.params.map(param => {
+      if (param.type !== "AssignmentPattern") {
+        return param.name;
+      }
+
+      // Parameter with default value
+      if (param.left.type === "Identifier" && param.right.type === "Identifier") {
+        return `${param.left.name} = ${param.right.name}`;
+      } else if (param.left.type === "Identifier" && param.right.type === "StringLiteral") {
+        return `${param.left.name} = ${param.right.value}`;
+      } else if (param.left.type === "Identifier" && param.right.type === "ObjectExpression") {
+        return `${param.left.name} = {}`;
+      } else if (param.left.type === "Identifier" && param.right.type === "ArrayExpression") {
+        return `${param.left.name} = []`;
+      } else if (param.left.type === "Identifier" && param.right.type === "NullLiteral") {
+        return `${param.left.name} = null`;
+      }
+    });
+  }
+  return [];
+}
+
 /***/ }),
 
 /***/ 1455:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
@@ -1308,49 +1343,36 @@ function _interopRequireDefault(obj) { r
 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 /* 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/>. */
 
 let symbolDeclarations = new Map();
 
-function getFunctionParameterNames(path) {
-  if (path.node.params != null) {
-    return path.node.params.map(param => {
-      if (param.type !== "AssignmentPattern") {
-        return param.name;
-      }
-
-      // Parameter with default value
-      if (param.left.type === "Identifier" && param.right.type === "Identifier") {
-        return `${param.left.name} = ${param.right.name}`;
-      } else if (param.left.type === "Identifier" && param.right.type === "StringLiteral") {
-        return `${param.left.name} = ${param.right.value}`;
-      } else if (param.left.type === "Identifier" && param.right.type === "ObjectExpression") {
-        return `${param.left.name} = {}`;
-      } else if (param.left.type === "Identifier" && param.right.type === "ArrayExpression") {
-        return `${param.left.name} = []`;
-      } else if (param.left.type === "Identifier" && param.right.type === "NullLiteral") {
-        return `${param.left.name} = null`;
-      }
-    });
-  }
-  return [];
+function getUniqueIdentifiers(identifiers) {
+  const newIdentifiers = [];
+  for (const newId of identifiers) {
+    if (!newIdentifiers.find(id => (0, _helpers.nodeHasSameLocation)(id, newId))) {
+      newIdentifiers.push(newId);
+    }
+  }
+
+  return newIdentifiers;
 }
 
 /* eslint-disable complexity */
 function extractSymbol(path, symbols) {
   if ((0, _helpers.isFunction)(path)) {
     const name = (0, _getFunctionName2.default)(path.node, path.parent);
     symbols.functions.push({
       name,
       klass: (0, _inferClassName.inferClassName)(path),
       location: path.node.loc,
-      parameterNames: getFunctionParameterNames(path),
+      parameterNames: (0, _helpers.getFunctionParameterNames)(path),
       identifier: path.node.id,
       // indicates the occurence of the function in a file
       // e.g { name: foo, ... index: 4 } is the 4th foo function
       // in the file
       index: symbols.functions.filter(f => f.name === name).length
     });
   }
 
@@ -1437,17 +1459,17 @@ function extractSymbol(path, symbols) {
     const { start, end } = path.node.loc;
     return symbols.identifiers.push({
       name: path.node.value,
       expression: (0, _helpers.getObjectExpressionValue)(path.parent),
       location: { start, end }
     });
   }
 
-  if (t.isIdentifier(path) && !t.isGenericTypeAnnotation(path.parent) && !t.isObjectProperty(path.parent) && !t.isArrayPattern(path.parent)) {
+  if (t.isIdentifier(path) && !t.isGenericTypeAnnotation(path.parent)) {
     let { start, end } = path.node.loc;
 
     // We want to include function params, but exclude the function name
     if (t.isClassMethod(path.parent) && !path.inList) {
       return;
     }
 
     if (t.isProperty(path.parentPath) && !(0, _helpers.isObjectShorthand)(path.parent)) {
@@ -1515,16 +1537,17 @@ function extractSymbols(sourceId) {
       } catch (e) {
         console.error(e);
       }
     }
   });
 
   // comments are extracted separately from the AST
   symbols.comments = (0, _helpers.getComments)(ast);
+  symbols.identifiers = getUniqueIdentifiers(symbols.identifiers);
 
   return symbols;
 }
 
 function extendSnippet(name, expression, path, prevPath) {
   const computed = path && path.node.computed;
   const prevComputed = prevPath && prevPath.node.computed;
   const prevArray = t.isArrayExpression(prevPath);
@@ -23055,16 +23078,23 @@ function mapOriginalExpression(expressio
     return expression;
   }
 
   t.traverse(ast, (node, ancestors) => {
     if (!t.isIdentifier(node) && !t.isThisExpression(node)) {
       return;
     }
 
+    const ancestor = ancestors[ancestors.length - 1];
+    // Shorthand properties can have a key and value with `node.loc.start` value
+    // and we only want to replace the value.
+    if (t.isObjectProperty(ancestor.node) && ancestor.key !== "value") {
+      return;
+    }
+
     const replacement = replacements.get(locationKey(node.loc.start));
     if (replacement) {
       replaceNode(ancestors, t.cloneNode(replacement));
       shouldUpdate = true;
     }
   });
 
   if (shouldUpdate) {
--- a/devtools/client/debugger/new/src/actions/ast.js
+++ b/devtools/client/debugger/new/src/actions/ast.js
@@ -7,17 +7,17 @@
 import {
   getSource,
   getSourceFromId,
   getSymbols,
   getSelectedLocation,
   isPaused
 } from "../selectors";
 
-import { mapFrames, fetchExtra } from "./pause";
+import { mapFrames } from "./pause";
 import { updateTab } from "./tabs";
 
 import { PROMISE } from "./utils/middleware/promise";
 
 import { setInScopeLines } from "./ast/setInScopeLines";
 import { setPausePoints } from "./ast/setPausePoints";
 export { setPausePoints };
 
@@ -62,17 +62,16 @@ export function setSymbols(sourceId: Sou
 
     await dispatch({
       type: "SET_SYMBOLS",
       sourceId,
       [PROMISE]: parser.getSymbols(sourceId)
     });
 
     if (isPaused(getState())) {
-      await dispatch(fetchExtra());
       await dispatch(mapFrames());
     }
 
     await dispatch(setPausePoints(sourceId));
     await dispatch(setSourceMetaData(sourceId));
   };
 }
 
--- a/devtools/client/debugger/new/src/actions/ast/setPausePoints.js
+++ b/devtools/client/debugger/new/src/actions/ast/setPausePoints.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import { getSourceFromId } from "../../selectors";
 import * as parser from "../../workers/parser";
 import { isGenerated } from "../../utils/source";
-import { mapPausePoints } from "../../utils/pause/pausePoints";
+import { convertToList } from "../../utils/pause/pausePoints";
 import { features } from "../../utils/prefs";
 import { getGeneratedLocation } from "../../utils/source-maps";
 
 import type { SourceId } from "../../types";
 import type { ThunkArgs, Action } from "../types";
 
 function compressPausePoints(pausePoints) {
   const compressed = {};
@@ -23,56 +23,60 @@ function compressPausePoints(pausePoints
       compressed[line][col] = (types.break ? 1 : 0) | (types.step ? 2 : 0);
     }
   }
 
   return compressed;
 }
 
 async function mapLocations(pausePoints, state, source, sourceMaps) {
+  const pausePointList = convertToList(pausePoints);
   const sourceId = source.id;
-  return mapPausePoints(pausePoints, async ({ types, location }) => {
-    const generatedLocation = await getGeneratedLocation(
-      state,
-      source,
-      {
-        ...location,
-        sourceId
-      },
-      sourceMaps
-    );
 
-    return { types, location, generatedLocation };
-  });
+  return Promise.all(
+    pausePointList.map(async ({ types, location }) => {
+      const generatedLocation = await getGeneratedLocation(
+        state,
+        source,
+        {
+          ...location,
+          sourceId
+        },
+        sourceMaps
+      );
+
+      return { types, location, generatedLocation };
+    })
+  );
 }
 export function setPausePoints(sourceId: SourceId) {
   return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const source = getSourceFromId(getState(), sourceId);
     if (!features.pausePoints || !source || !source.text) {
       return;
     }
 
     if (source.isWasm) {
       return;
     }
 
     let pausePoints = await parser.getPausePoints(sourceId);
 
+    if (isGenerated(source)) {
+      const compressed = compressPausePoints(pausePoints);
+      await client.setPausePoints(sourceId, compressed);
+    }
+
     pausePoints = await mapLocations(
       pausePoints,
       getState(),
       source,
       sourceMaps
     );
 
-    if (isGenerated(source)) {
-      const compressed = compressPausePoints(pausePoints);
-      await client.setPausePoints(sourceId, compressed);
-    }
-
     dispatch(
       ({
         type: "SET_PAUSE_POINTS",
         sourceText: source.text || "",
         sourceId,
         pausePoints
       }: Action)
     );
--- a/devtools/client/debugger/new/src/actions/breakpoints/index.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/index.js
@@ -12,32 +12,38 @@
 import { isOriginalId } from "devtools-source-map";
 import { PROMISE } from "../utils/middleware/promise";
 import {
   getBreakpoint,
   getBreakpointsList,
   getXHRBreakpoints,
   getSelectedSource,
   getBreakpointAtLocation,
-  getBreakpointsAtLine
+  getBreakpointsAtLine,
+  getBreakpointsForSource
 } from "../../selectors";
 import { assertBreakpoint, createXHRBreakpoint } from "../../utils/breakpoint";
 import {
   addBreakpoint,
   addHiddenBreakpoint,
   enableBreakpoint
 } from "./addBreakpoint";
 import remapLocations from "./remapLocations";
 import { syncBreakpoint } from "./syncBreakpoint";
 import { isEmptyLineInSource } from "../../reducers/ast";
 
 // this will need to be changed so that addCLientBreakpoint is removed
 
 import type { ThunkArgs, Action } from "../types";
-import type { Breakpoint, SourceLocation, XHRBreakpoint } from "../../types";
+import type {
+  Breakpoint,
+  Source,
+  SourceLocation,
+  XHRBreakpoint
+} from "../../types";
 
 import { recordEvent } from "../../utils/telemetry";
 
 export type addBreakpointOptions = {
   condition?: string,
   hidden?: boolean
 };
 
@@ -100,16 +106,50 @@ export function disableBreakpoint(locati
         type: "DISABLE_BREAKPOINT",
         breakpoint: newBreakpoint
       }: Action)
     );
   };
 }
 
 /**
+ * Disable all breakpoints in a source
+ *
+ * @memberof actions/breakpoints
+ * @static
+ */
+export function disableBreakpointsInSource(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.generatedLocation));
+      }
+    }
+  };
+}
+
+/**
+ * Enable all breakpoints in a source
+ *
+ * @memberof actions/breakpoints
+ * @static
+ */
+export function enableBreakpointsInSource(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.generatedLocation));
+      }
+    }
+  };
+}
+
+/**
  * Toggle All Breakpoints
  *
  * @memberof actions/breakpoints
  * @static
  */
 export function toggleAllBreakpoints(shouldDisableBreakpoints: boolean) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
     const breakpoints = getBreakpointsList(getState());
@@ -191,16 +231,31 @@ export function removeAllBreakpoints() {
 export function removeBreakpoints(breakpoints: Breakpoint[]) {
   return async ({ dispatch }: ThunkArgs) => {
     return Promise.all(
       breakpoints.map(bp => dispatch(removeBreakpoint(bp.location)))
     );
   };
 }
 
+/**
+ * Removes all breakpoints in a source
+ *
+ * @memberof actions/breakpoints
+ * @static
+ */
+export function removeBreakpointsInSource(source: Source) {
+  return async ({ dispatch, getState, client }: ThunkArgs) => {
+    const breakpoints = getBreakpointsForSource(getState(), source.id);
+    for (const breakpoint of breakpoints) {
+      dispatch(removeBreakpoint(breakpoint.generatedLocation));
+    }
+  };
+}
+
 export function remapBreakpoints(sourceId: string) {
   return async ({ dispatch, getState, sourceMaps }: ThunkArgs) => {
     const breakpoints = getBreakpointsList(getState());
     const newBreakpoints = await remapLocations(
       breakpoints,
       sourceId,
       sourceMaps
     );
--- a/devtools/client/debugger/new/src/actions/expressions.js
+++ b/devtools/client/debugger/new/src/actions/expressions.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import {
+  getCurrentThread,
   getExpression,
   getExpressions,
   getSelectedFrame,
   getSelectedFrameId,
   getSourceFromId,
   getSelectedSource,
   getSelectedScopeMappings,
   getSelectedFrameBindings
@@ -112,17 +113,21 @@ export function deleteExpression(express
  * @param {number} selectedFrameId
  * @static
  */
 export function evaluateExpressions() {
   return async function({ dispatch, getState, client }: ThunkArgs) {
     const expressions = getExpressions(getState()).toJS();
     const inputs = expressions.map(({ input }) => input);
     const frameId = getSelectedFrameId(getState());
-    const results = await client.evaluateExpressions(inputs, frameId);
+    const thread = getCurrentThread(getState());
+    const results = await client.evaluateExpressions(inputs, {
+      frameId,
+      thread
+    });
     dispatch({ type: "EVALUATE_EXPRESSIONS", inputs, results });
   };
 }
 
 function evaluateExpression(expression: Expression) {
   return async function({ dispatch, getState, client, sourceMaps }: ThunkArgs) {
     if (!expression.input) {
       console.warn("Expressions should not be empty");
@@ -142,21 +147,26 @@ function evaluateExpression(expression: 
         const mapResult = await dispatch(getMappedExpression(input));
         if (mapResult) {
           input = mapResult.expression;
         }
       }
     }
 
     const frameId = getSelectedFrameId(getState());
+    const thread = getCurrentThread(getState());
 
     return dispatch({
       type: "EVALUATE_EXPRESSION",
+      thread,
       input: expression.input,
-      [PROMISE]: client.evaluateInFrame(wrapExpression(input), frameId)
+      [PROMISE]: client.evaluateInFrame(wrapExpression(input), {
+        frameId,
+        thread
+      })
     });
   };
 }
 
 /**
  * Gets information about original variable names from the source map
  * and replaces all posible generated names.
  */
--- a/devtools/client/debugger/new/src/actions/navigation.js
+++ b/devtools/client/debugger/new/src/actions/navigation.js
@@ -49,20 +49,20 @@ export function navigate(url: string): A
   sourceQueue.clear();
 
   return {
     type: "NAVIGATE",
     url
   };
 }
 
-export function connect(url: string, canRewind: boolean) {
+export function connect(url: string, thread: string, canRewind: boolean) {
   return async function({ dispatch }: ThunkArgs) {
     await dispatch(updateWorkers());
-    dispatch(({ type: "CONNECT", url, canRewind }: Action));
+    dispatch(({ type: "CONNECT", url, thread, canRewind }: Action));
   };
 }
 
 /**
  * @memberof actions/navigation
  * @static
  */
 export function navigated() {
--- a/devtools/client/debugger/new/src/actions/pause/breakOnNext.js
+++ b/devtools/client/debugger/new/src/actions/pause/breakOnNext.js
@@ -1,22 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import type { ThunkArgs } from "../types";
+import { getCurrentThread } from "../../selectors";
 
 /**
  * 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() {
-  return async ({ dispatch, client }: ThunkArgs) => {
-    await client.breakOnNext();
-    return dispatch({ type: "BREAK_ON_NEXT" });
+  return async ({ dispatch, getState, client }: ThunkArgs) => {
+    const thread = getCurrentThread(getState());
+    await client.breakOnNext(thread);
+    return dispatch({ type: "BREAK_ON_NEXT", thread });
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/commands.js
+++ b/devtools/client/debugger/new/src/actions/pause/commands.js
@@ -1,39 +1,55 @@
 /* -*- 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 { isPaused, getSource, getTopFrame } from "../../selectors";
+import {
+  isPaused,
+  getCurrentThread,
+  getSource,
+  getTopFrame
+} from "../../selectors";
 import { PROMISE } from "../utils/middleware/promise";
 import { getNextStep } from "../../workers/parser";
 import { addHiddenBreakpoint } from "../breakpoints";
 import { features } from "../../utils/prefs";
 import { recordEvent } from "../../utils/telemetry";
 
 import type { Source } from "../../types";
 import type { ThunkArgs } from "../types";
 import type { Command } from "../../reducers/types";
 
+export function selectThread(thread: string) {
+  return async ({ dispatch, client }: ThunkArgs) => {
+    return dispatch({
+      type: "SELECT_THREAD",
+      thread
+    });
+  };
+}
+
 /**
  * Debugger commands like stepOver, stepIn, stepUp
  *
  * @param string $0.type
  * @memberof actions/pause
  * @static
  */
 export function command(type: Command) {
-  return async ({ dispatch, client }: ThunkArgs) => {
+  return async ({ dispatch, getState, client }: ThunkArgs) => {
+    const thread = getCurrentThread(getState());
     return dispatch({
       type: "COMMAND",
       command: type,
-      [PROMISE]: client[type]()
+      thread,
+      [PROMISE]: client[type](thread)
     });
   };
 }
 
 /**
  * StepIn
  * @memberof actions/pause
  * @static
--- a/devtools/client/debugger/new/src/actions/pause/extra.js
+++ b/devtools/client/debugger/new/src/actions/pause/extra.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 { inComponent, getSelectedFrame } from "../../selectors";
+import {
+  getCurrentThread,
+  getSource,
+  inComponent,
+  getSelectedFrame
+} from "../../selectors";
 import { isImmutablePreview } from "../../utils/preview";
 
 import type { ThunkArgs } from "../types";
 
 async function getReactProps(evaluate, displayName) {
   const componentNames = await evaluate(
     `
     if(this.hasOwnProperty('_reactInternalFiber')) {
@@ -72,27 +77,36 @@ export function fetchExtra() {
     const frame = getSelectedFrame(getState());
     if (!frame) {
       return;
     }
 
     const extra = await dispatch(getExtra("this;", frame.this));
     dispatch({
       type: "ADD_EXTRA",
+      thread: getCurrentThread(getState()),
       extra: extra
     });
   };
 }
 
 export function getExtra(expression: string, result: Object) {
   return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const selectedFrame = getSelectedFrame(getState());
     if (!selectedFrame) {
       return {};
     }
 
+    const source = getSource(getState(), selectedFrame.location.sourceId);
+    if (!source) {
+      return {};
+    }
+
     const extra = await getExtraProps(getState, expression, result, expr =>
-      client.evaluateInFrame(expr, selectedFrame.id)
+      client.evaluateInFrame(expr, {
+        frameId: selectedFrame.id,
+        thread: source.thread
+      })
     );
 
     return extra;
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/fetchScopes.js
+++ b/devtools/client/debugger/new/src/actions/pause/fetchScopes.js
@@ -1,29 +1,32 @@
 /* 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 { getSelectedFrame, getGeneratedFrameScope } from "../../selectors";
+import {
+  getCurrentThread,
+  getSelectedFrame,
+  getGeneratedFrameScope
+} from "../../selectors";
 import { mapScopes } from "./mapScopes";
 import { PROMISE } from "../utils/middleware/promise";
-import { fetchExtra } from "./extra";
 import type { ThunkArgs } from "../types";
 
 export function fetchScopes() {
   return async function({ dispatch, getState, client, sourceMaps }: ThunkArgs) {
     const frame = getSelectedFrame(getState());
     if (!frame || getGeneratedFrameScope(getState(), frame.id)) {
       return;
     }
 
     const scopes = dispatch({
       type: "ADD_SCOPES",
+      thread: getCurrentThread(getState()),
       frame,
       [PROMISE]: client.getFrameScopes(frame)
     });
 
-    await dispatch(fetchExtra());
     await dispatch(mapScopes(scopes, frame));
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/index.js
+++ b/devtools/client/debugger/new/src/actions/pause/index.js
@@ -5,28 +5,28 @@
 // @flow
 
 /**
  * Redux actions for the pause state
  * @module actions/pause
  */
 
 export {
+  selectThread,
   stepIn,
   stepOver,
   stepOut,
   resume,
   rewind,
   reverseStepIn,
   reverseStepOver,
   reverseStepOut
 } from "./commands";
 export { fetchScopes } from "./fetchScopes";
 export { paused } from "./paused";
 export { resumed } from "./resumed";
 export { continueToHere } from "./continueToHere";
 export { breakOnNext } from "./breakOnNext";
 export { mapFrames } from "./mapFrames";
-export { fetchExtra, getExtra } from "./extra";
 export { setPopupObjectProperties } from "./setPopupObjectProperties";
 export { pauseOnExceptions } from "./pauseOnExceptions";
 export { selectFrame } from "./selectFrame";
 export { toggleSkipPausing } from "./skipPausing";
--- a/devtools/client/debugger/new/src/actions/pause/mapFrames.js
+++ b/devtools/client/debugger/new/src/actions/pause/mapFrames.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import {
+  getCurrentThread,
   getFrames,
   getSymbols,
   getSource,
   getSelectedFrame
 } from "../../selectors";
 
 import assert from "../../utils/assert";
 import { findClosestFunction } from "../../utils/ast";
@@ -165,16 +166,18 @@ export function mapFrames() {
     if (!frames) {
       return;
     }
 
     let mappedFrames = await updateFrameLocations(frames, sourceMaps);
     mappedFrames = await expandFrames(mappedFrames, sourceMaps, getState);
     mappedFrames = mapDisplayNames(mappedFrames, getState);
 
+    const thread = getCurrentThread(getState());
     const selectedFrameId = getSelectedFrameId(getState(), mappedFrames);
     dispatch({
       type: "MAP_FRAMES",
+      thread,
       frames: mappedFrames,
       selectedFrameId
     });
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/mapScopes.js
+++ b/devtools/client/debugger/new/src/actions/pause/mapScopes.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 { getSource } from "../../selectors";
+import { getCurrentThread, getSource } from "../../selectors";
 import { loadSourceText } from "../sources/loadSourceText";
 import { PROMISE } from "../utils/middleware/promise";
 
 import { features } from "../../utils/prefs";
 import { log } from "../../utils/log";
 import { isGenerated } from "../../utils/source";
 import type { Frame, Scope } from "../../types";
 
@@ -23,16 +23,17 @@ export function mapScopes(scopes: Promis
       getState(),
       frame.generatedLocation.sourceId
     );
 
     const source = getSource(getState(), frame.location.sourceId);
 
     await dispatch({
       type: "MAP_SCOPES",
+      thread: getCurrentThread(getState()),
       frame,
       [PROMISE]: (async function() {
         if (
           !features.mapScopes ||
           !source ||
           !generatedSource ||
           generatedSource.isWasm ||
           source.isPrettyPrinted ||
--- a/devtools/client/debugger/new/src/actions/pause/moz.build
+++ b/devtools/client/debugger/new/src/actions/pause/moz.build
@@ -6,17 +6,16 @@
 DIRS += [
 
 ]
 
 DebuggerModules(
     'breakOnNext.js',
     'commands.js',
     'continueToHere.js',
-    'extra.js',
     'fetchScopes.js',
     'index.js',
     'mapFrames.js',
     'mapScopes.js',
     'paused.js',
     'pauseOnExceptions.js',
     'resumed.js',
     'selectFrame.js',
--- a/devtools/client/debugger/new/src/actions/pause/pauseOnExceptions.js
+++ b/devtools/client/debugger/new/src/actions/pause/pauseOnExceptions.js
@@ -2,38 +2,42 @@
  * 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 { PROMISE } from "../utils/middleware/promise";
 import { recordEvent } from "../../utils/telemetry";
 import type { ThunkArgs } from "../types";
+import { getCurrentThread } from "../../selectors";
 
 /**
  *
  * @memberof actions/pause
  * @static
  */
 export function pauseOnExceptions(
   shouldPauseOnExceptions: boolean,
   shouldPauseOnCaughtExceptions: boolean
 ) {
-  return ({ dispatch, client }: ThunkArgs) => {
+  return ({ dispatch, getState, client }: ThunkArgs) => {
     /* eslint-disable camelcase */
     recordEvent("pause_on_exceptions", {
       exceptions: shouldPauseOnExceptions,
       // There's no "n" in the key below (#1463117)
       caught_exceptio: shouldPauseOnCaughtExceptions
     });
     /* eslint-enable camelcase */
 
+    const thread = getCurrentThread(getState());
     return dispatch({
       type: "PAUSE_ON_EXCEPTIONS",
+      thread,
       shouldPauseOnExceptions,
       shouldPauseOnCaughtExceptions,
       [PROMISE]: client.pauseOnExceptions(
+        thread,
         shouldPauseOnExceptions,
         shouldPauseOnCaughtExceptions
       )
     });
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/paused.js
+++ b/devtools/client/debugger/new/src/actions/pause/paused.js
@@ -33,17 +33,17 @@ async function getOriginalSourceForFrame
  * Debugger has just paused
  *
  * @param {object} pauseInfo
  * @memberof actions/pause
  * @static
  */
 export function paused(pauseInfo: Pause) {
   return async function({ dispatch, getState, client, sourceMaps }: ThunkArgs) {
-    const { frames, why, loadedObjects } = pauseInfo;
+    const { thread, frames, why, loadedObjects } = pauseInfo;
     const topFrame = frames.length > 0 ? frames[0] : null;
 
     // NOTE: do not step when leaving a frame or paused at a debugger statement
     if (topFrame && !why.frameFinished && why.type == "resumeLimit") {
       const mappedFrame = await updateFrameLocation(topFrame, sourceMaps);
       const source = await getOriginalSourceForFrame(getState(), mappedFrame);
 
       // Ensure that the original file has loaded if there is one.
@@ -52,16 +52,17 @@ export function paused(pauseInfo: Pause)
       if (shouldStep(mappedFrame, getState(), sourceMaps)) {
         dispatch(command("stepOver"));
         return;
       }
     }
 
     dispatch({
       type: "PAUSED",
+      thread,
       why,
       frames,
       selectedFrameId: topFrame ? topFrame.id : undefined,
       loadedObjects: loadedObjects || []
     });
 
     const hiddenBreakpointLocation = getHiddenBreakpointLocation(getState());
     if (hiddenBreakpointLocation) {
--- a/devtools/client/debugger/new/src/actions/pause/resumed.js
+++ b/devtools/client/debugger/new/src/actions/pause/resumed.js
@@ -1,31 +1,32 @@
 /* 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 { getCurrentThread, isStepping, getPauseReason } from "../../selectors";
 import { evaluateExpressions } from "../expressions";
 import { inDebuggerEval } from "../../utils/pause";
 
 import type { ThunkArgs } from "../types";
 
 /**
  * Debugger has just resumed
  *
  * @memberof actions/pause
  * @static
  */
 export function resumed() {
   return async ({ dispatch, client, getState }: ThunkArgs) => {
     const why = getPauseReason(getState());
     const wasPausedInEval = inDebuggerEval(why);
     const wasStepping = isStepping(getState());
+    const thread = getCurrentThread(getState());
 
-    dispatch({ type: "RESUME" });
+    dispatch({ type: "RESUME", thread });
 
     if (!wasStepping && !wasPausedInEval) {
       await dispatch(evaluateExpressions());
     }
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/selectFrame.js
+++ b/devtools/client/debugger/new/src/actions/pause/selectFrame.js
@@ -2,28 +2,30 @@
  * 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 { getCurrentThread } from "../../selectors";
 
 import type { Frame } from "../../types";
 import type { ThunkArgs } from "../types";
 
 /**
  * @memberof actions/pause
  * @static
  */
 export function selectFrame(frame: Frame) {
   return async ({ dispatch, client, getState, sourceMaps }: ThunkArgs) => {
     dispatch({
       type: "SELECT_FRAME",
+      thread: getCurrentThread(getState()),
       frame
     });
 
     dispatch(selectLocation(frame.location));
 
     dispatch(evaluateExpressions());
     dispatch(fetchScopes());
   };
--- a/devtools/client/debugger/new/src/actions/pause/setPopupObjectProperties.js
+++ b/devtools/client/debugger/new/src/actions/pause/setPopupObjectProperties.js
@@ -1,28 +1,30 @@
 /* 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 } from "../../selectors";
+import { getCurrentThread, getPopupObjectProperties } from "../../selectors";
 import type { ThunkArgs } from "../types";
 
 /**
  * @memberof actions/pause
  * @static
  */
 export function setPopupObjectProperties(object: any, properties: Object) {
   return ({ dispatch, client, getState }: ThunkArgs) => {
     const objectId = object.actor || object.objectId;
 
     if (getPopupObjectProperties(getState(), object.actor)) {
       return;
     }
 
+    const thread = getCurrentThread(getState());
     dispatch({
       type: "SET_POPUP_OBJECT_PROPERTIES",
+      thread,
       objectId,
       properties
     });
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/skipPausing.js
+++ b/devtools/client/debugger/new/src/actions/pause/skipPausing.js
@@ -1,20 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import type { ThunkArgs } from "../types";
-import { getSkipPausing } from "../../selectors";
+import { getSkipPausing, getCurrentThread } from "../../selectors";
 
 /**
  * @memberof actions/pause
  * @static
  */
 export function toggleSkipPausing() {
   return async ({ dispatch, client, getState, sourceMaps }: ThunkArgs) => {
+    const thread = getCurrentThread(getState());
     const skipPausing = !getSkipPausing(getState());
-    await client.setSkipPausing(skipPausing);
-    dispatch({ type: "TOGGLE_SKIP_PAUSING", skipPausing });
+    await client.setSkipPausing(thread, skipPausing);
+    dispatch({ type: "TOGGLE_SKIP_PAUSING", thread, skipPausing });
   };
 }
--- a/devtools/client/debugger/new/src/actions/preview.js
+++ b/devtools/client/debugger/new/src/actions/preview.js
@@ -14,17 +14,16 @@ import {
   isLineInScope,
   isSelectedFrameVisible,
   getSelectedSource,
   getSelectedFrame,
   getSymbols
 } from "../selectors";
 
 import { getMappedExpression } from "./expressions";
-import { getExtra } from "./pause";
 
 import type { Action, ThunkArgs } from "./types";
 import type { Position } from "../types";
 import type { AstLocation } from "../workers/parser";
 
 function findExpressionMatch(state, codeMirror, tokenPos) {
   const source = getSelectedSource(state);
   if (!source) {
@@ -95,34 +94,31 @@ export function setPreview(
             expression = mapResult.expression;
           }
         }
 
         if (!selectedFrame) {
           return;
         }
 
-        const { result } = await client.evaluateInFrame(
-          expression,
-          selectedFrame.id
-        );
+        const { result } = await client.evaluateInFrame(expression, {
+          frameId: selectedFrame.id,
+          thread: source.thread
+        });
 
         if (result === undefined) {
           return;
         }
 
-        const extra = await dispatch(getExtra(expression, result));
-
         return {
           expression,
           result,
           location,
           tokenPos,
-          cursorPos,
-          extra
+          cursorPos
         };
       })()
     });
   };
 }
 
 export function clearPreview() {
   return ({ dispatch, getState, client }: ThunkArgs) => {
--- a/devtools/client/debugger/new/src/actions/sources/newSources.js
+++ b/devtools/client/debugger/new/src/actions/sources/newSources.js
@@ -35,16 +35,17 @@ function createOriginalSource(
   originalUrl,
   generatedSource,
   sourceMaps
 ): Source {
   return {
     url: originalUrl,
     relativeUrl: originalUrl,
     id: generatedToOriginalId(generatedSource.id, originalUrl),
+    thread: "",
     isPrettyPrinted: false,
     isWasm: false,
     isBlackBoxed: false,
     loadedState: "unloaded"
   };
 }
 
 function loadSourceMaps(sources: Source[]) {
--- a/devtools/client/debugger/new/src/actions/sources/prettyPrint.js
+++ b/devtools/client/debugger/new/src/actions/sources/prettyPrint.js
@@ -32,16 +32,17 @@ export function createPrettySource(sourc
     const source = getSourceFromId(getState(), sourceId);
     const url = getPrettySourceURL(source.url);
     const id = await sourceMaps.generatedToOriginalId(sourceId, url);
 
     const prettySource: JsSource = {
       url,
       relativeUrl: url,
       id,
+      thread: "",
       isBlackBoxed: false,
       isPrettyPrinted: true,
       isWasm: false,
       contentType: "text/javascript",
       loadedState: "loading"
     };
 
     dispatch(({ type: "ADD_SOURCE", source: prettySource }: Action));
--- a/devtools/client/debugger/new/src/actions/sources/select.js
+++ b/devtools/client/debugger/new/src/actions/sources/select.js
@@ -37,16 +37,17 @@ import {
 import type { SourceLocation, PartialPosition, Source } from "../../types";
 import type { ThunkArgs } from "../types";
 
 export const setSelectedLocation = (
   source: Source,
   location: SourceLocation
 ) => ({
   type: "SET_SELECTED_LOCATION",
+  thread: source.thread,
   source,
   location
 });
 
 export const setPendingSelectedLocation = (url: string, options: Object) => ({
   type: "SET_PENDING_SELECTED_LOCATION",
   url: url,
   line: options.location ? options.location.line : null
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/client/chrome.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 { setupCommands, clientCommands } from "./chrome/commands";
+import { setupEvents, clientEvents, pageEvents } from "./chrome/events";
+
+export async function onConnect(connection: any, actions: Object): Object {
+  const {
+    tabConnection,
+    connTarget: { type }
+  } = connection;
+  const { Debugger, Runtime, Page } = tabConnection;
+
+  Debugger.enable();
+  Debugger.setPauseOnExceptions({ state: "none" });
+  Debugger.setAsyncCallStackDepth({ maxDepth: 0 });
+
+  if (type == "chrome") {
+    Page.frameNavigated(pageEvents.frameNavigated);
+    Page.frameStartedLoading(pageEvents.frameStartedLoading);
+    Page.frameStoppedLoading(pageEvents.frameStoppedLoading);
+  }
+
+  Debugger.scriptParsed(clientEvents.scriptParsed);
+  Debugger.scriptFailedToParse(clientEvents.scriptFailedToParse);
+  Debugger.paused(clientEvents.paused);
+  Debugger.resumed(clientEvents.resumed);
+
+  setupCommands({ Debugger, Runtime, Page });
+  setupEvents({ actions, Page, type, Runtime });
+  return {};
+}
+
+export { clientCommands, clientEvents };
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/client/chrome/commands.js
@@ -0,0 +1,144 @@
+/* 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 {
+  toServerLocation,
+  fromServerLocation,
+  createLoadedObject
+} from "./create";
+
+import type { SourceLocation } from "../../types";
+import type { ServerLocation, Agents } from "./types";
+
+type setBreakpointResponseType = {
+  breakpointId: string,
+  serverLocation?: ServerLocation
+};
+
+let debuggerAgent;
+let runtimeAgent;
+let pageAgent;
+
+function setupCommands({ Debugger, Runtime, Page }: Agents) {
+  debuggerAgent = Debugger;
+  runtimeAgent = Runtime;
+  pageAgent = Page;
+}
+
+function resume() {
+  return debuggerAgent.resume();
+}
+
+function stepIn() {
+  return debuggerAgent.stepInto();
+}
+
+function stepOver() {
+  return debuggerAgent.stepOver();
+}
+
+function stepOut() {
+  return debuggerAgent.stepOut();
+}
+
+function pauseOnExceptions(
+  shouldPauseOnExceptions: boolean,
+  shouldIgnoreCaughtExceptions: boolean
+) {
+  if (!shouldPauseOnExceptions) {
+    return debuggerAgent.setPauseOnExceptions({ state: "none" });
+  }
+  const state = shouldIgnoreCaughtExceptions ? "uncaught" : "all";
+  return debuggerAgent.setPauseOnExceptions({ state });
+}
+
+function breakOnNext() {
+  return debuggerAgent.pause();
+}
+
+function sourceContents(sourceId: string) {
+  return debuggerAgent
+    .getScriptSource({ scriptId: sourceId })
+    .then(({ scriptSource }) => ({
+      source: scriptSource,
+      contentType: null
+    }));
+}
+
+async function setBreakpoint(location: SourceLocation, condition: string) {
+  const {
+    breakpointId,
+    serverLocation
+  }: setBreakpointResponseType = await debuggerAgent.setBreakpoint({
+    location: toServerLocation(location),
+    columnNumber: location.column
+  });
+
+  const actualLocation = fromServerLocation(serverLocation) || location;
+
+  return {
+    id: breakpointId,
+    actualLocation: actualLocation
+  };
+}
+
+function removeBreakpoint(breakpointId: string) {
+  return debuggerAgent.removeBreakpoint({ breakpointId });
+}
+
+async function getProperties(object: any) {
+  const { result } = await runtimeAgent.getProperties({
+    objectId: object.objectId
+  });
+
+  const loadedObjects = result.map(createLoadedObject);
+
+  return { loadedObjects };
+}
+
+function evaluate(script: string) {
+  return runtimeAgent.evaluate({ expression: script });
+}
+
+function debuggeeCommand(script: string): Promise<void> {
+  evaluate(script);
+  return Promise.resolve();
+}
+
+function navigate(url: string) {
+  return pageAgent.navigate({ url });
+}
+
+function getBreakpointByLocation(location: SourceLocation) {}
+
+function setPausePoints() {}
+
+function getFrameScopes() {}
+function evaluateInFrame() {}
+function evaluateExpressions() {}
+
+const clientCommands = {
+  resume,
+  stepIn,
+  stepOut,
+  stepOver,
+  pauseOnExceptions,
+  breakOnNext,
+  sourceContents,
+  setBreakpoint,
+  removeBreakpoint,
+  evaluate,
+  debuggeeCommand,
+  navigate,
+  getProperties,
+  getBreakpointByLocation,
+  setPausePoints,
+  getFrameScopes,
+  evaluateInFrame,
+  evaluateExpressions
+};
+
+export { setupCommands, clientCommands };
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/client/chrome/create.js
@@ -0,0 +1,52 @@
+/* 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 { SourceLocation, LoadedObject } from "../../types";
+import type { ServerLocation } from "./types";
+
+export function fromServerLocation(
+  serverLocation?: ServerLocation
+): ?SourceLocation {
+  if (serverLocation) {
+    return {
+      sourceId: serverLocation.scriptId,
+      line: serverLocation.lineNumber + 1,
+      column: serverLocation.columnNumber,
+      sourceUrl: ""
+    };
+  }
+}
+
+export function toServerLocation(location: SourceLocation): ServerLocation {
+  return {
+    scriptId: location.sourceId,
+    lineNumber: location.line - 1
+  };
+}
+
+export function createFrame(frame: any) {
+  return {
+    id: frame.callFrameId,
+    displayName: frame.functionName,
+    scopeChain: frame.scopeChain,
+    generatedLocation: frame.location,
+    location: fromServerLocation(frame.location)
+  };
+}
+
+export function createLoadedObject(
+  serverObject: any,
+  parentId: string
+): LoadedObject {
+  const { value, name } = serverObject;
+
+  return {
+    objectId: value.objectId,
+    parentId,
+    name,
+    value
+  };
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/client/chrome/events.js
@@ -0,0 +1,122 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+// @flow
+
+import { createFrame, createLoadedObject } from "./create";
+
+let actions;
+let pageAgent;
+let clientType;
+let runtimeAgent;
+
+function setupEvents(dependencies: any) {
+  actions = dependencies.actions;
+  pageAgent = dependencies.Page;
+  clientType = dependencies.clientType;
+  runtimeAgent = dependencies.Runtime;
+}
+
+// Debugger Events
+function scriptParsed({
+  scriptId,
+  url,
+  startLine,
+  startColumn,
+  endLine,
+  endColumn,
+  executionContextId,
+  hash,
+  isContentScript,
+  isInternalScript,
+  isLiveEdit,
+  sourceMapURL,
+  hasSourceURL,
+  deprecatedCommentWasUsed
+}: any) {
+  if (isContentScript) {
+    return;
+  }
+
+  if (clientType == "node") {
+    sourceMapURL = undefined;
+  }
+
+  actions.newSource({
+    id: scriptId,
+    url,
+    sourceMapURL,
+    isPrettyPrinted: false
+  });
+}
+
+function scriptFailedToParse() {}
+
+async function paused({
+  callFrames,
+  reason,
+  data,
+  hitBreakpoints,
+  asyncStackTrace
+}: any) {
+  const frames = callFrames.map(createFrame);
+  const frame = frames[0];
+  const why = { type: reason, ...data };
+
+  const objectId = frame.scopeChain[0].object.objectId;
+  const { result } = await runtimeAgent.getProperties({
+    objectId
+  });
+
+  const loadedObjects = result.map(createLoadedObject);
+
+  if (clientType == "chrome") {
+    pageAgent.configureOverlay({ message: "Paused in debugger.html" });
+  }
+
+  await actions.paused({ frame, why, frames, loadedObjects });
+}
+
+function resumed() {
+  if (clientType == "chrome") {
+    pageAgent.configureOverlay({ suspended: false });
+  }
+
+  actions.resumed();
+}
+
+function globalObjectCleared() {}
+
+// Page Events
+function frameNavigated(frame: any) {
+  actions.navigated();
+}
+
+function frameStartedLoading() {
+  actions.willNavigate();
+}
+
+function domContentEventFired() {}
+
+function loadEventFired() {}
+
+function frameStoppedLoading() {}
+
+const clientEvents = {
+  scriptParsed,
+  scriptFailedToParse,
+  paused,
+  resumed,
+  globalObjectCleared
+};
+
+const pageEvents = {
+  frameNavigated,
+  frameStartedLoading,
+  domContentEventFired,
+  loadEventFired,
+  frameStoppedLoading
+};
+
+export { setupEvents, pageEvents, clientEvents };
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/client/chrome/moz.build
@@ -0,0 +1,14 @@
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+
+]
+
+DebuggerModules(
+    'commands.js',
+    'create.js',
+    'events.js',
+)
--- a/devtools/client/debugger/new/src/client/firefox.js
+++ b/devtools/client/debugger/new/src/client/firefox.js
@@ -53,23 +53,24 @@ export async function onConnect(connecti
   // debugger finds them, but there may be existing sources already in
   // the debugger (if it's paused already, or if loading the page from
   // bfcache) so explicity fire `newSource` events for all returned
   // sources.
   const sources = await clientCommands.fetchSources();
   const traits = tabTarget.activeTab ? tabTarget.activeTab.traits : null;
   await actions.connect(
     tabTarget.url,
+    threadClient.actor,
     traits && traits.canRewind
   );
   await actions.newSources(sources);
 
   // If the threadClient is already paused, make sure to show a
   // paused state.
   const pausedPacket = threadClient.getLastPausePacket();
   if (pausedPacket) {
-    clientEvents.paused("paused", pausedPacket);
+    clientEvents.paused(threadClient, "paused", pausedPacket);
   }
 
   return { bpClients };
 }
 
 export { createObjectClient, clientCommands, clientEvents };
--- a/devtools/client/debugger/new/src/client/firefox/commands.js
+++ b/devtools/client/debugger/new/src/client/firefox/commands.js
@@ -20,25 +20,29 @@ import type {
   TabTarget,
   DebuggerClient,
   Grip,
   ThreadClient,
   ObjectClient,
   BPClients
 } from "./types";
 
-import type { PausePoints } from "../../workers/parser";
+import type { PausePointsMap } from "../../workers/parser";
 
 import { makePendingLocationId } from "../../utils/breakpoint";
 
-import { createSource, createBreakpointLocation } from "./create";
+import { createSource, createBreakpointLocation, createWorker } from "./create";
+import { originalToGeneratedId, isOriginalId } from "devtools-source-map";
+import { supportsWorkers, updateWorkerClients } from "./workers";
 
-import Services from "devtools-services";
+import { features } from "../../utils/prefs";
 
 let bpClients: BPClients;
+let workerClients: Object;
+let sourceThreads: Object;
 let threadClient: ThreadClient;
 let tabTarget: TabTarget;
 let debuggerClient: DebuggerClient;
 let supportsWasm: boolean;
 
 type Dependencies = {
   threadClient: ThreadClient,
   tabTarget: TabTarget,
@@ -47,16 +51,18 @@ type Dependencies = {
 };
 
 function setupCommands(dependencies: Dependencies): { bpClients: BPClients } {
   threadClient = dependencies.threadClient;
   tabTarget = dependencies.tabTarget;
   debuggerClient = dependencies.debuggerClient;
   supportsWasm = dependencies.supportsWasm;
   bpClients = {};
+  workerClients = {};
+  sourceThreads = {};
 
   return { bpClients };
 }
 
 function createObjectClient(grip: Grip) {
   return debuggerClient.createObjectClient(grip);
 }
 
@@ -67,70 +73,88 @@ function releaseActor(actor: String) {
 
   return debuggerClient.release(actor);
 }
 
 function sendPacket(packet: Object, callback?: Function = r => r) {
   return debuggerClient.request(packet).then(callback);
 }
 
-function resume(): Promise<*> {
+function lookupThreadClient(thread: string) {
+  if (thread == threadClient.actor) {
+    return threadClient;
+  }
+  if (!workerClients[thread]) {
+    throw new Error(`Unknown thread client: ${thread}`);
+  }
+  return workerClients[thread].thread;
+}
+
+function lookupConsoleClient(thread: string) {
+  if (thread == threadClient.actor) {
+    return tabTarget.activeConsole;
+  }
+  return workerClients[thread].console;
+}
+
+function resume(thread: string): Promise<*> {
   return new Promise(resolve => {
-    threadClient.resume(resolve);
+    lookupThreadClient(thread).resume(resolve);
   });
 }
 
-function stepIn(): Promise<*> {
+function stepIn(thread: string): Promise<*> {
   return new Promise(resolve => {
-    threadClient.stepIn(resolve);
+    lookupThreadClient(thread).stepIn(resolve);
   });
 }
 
-function stepOver(): Promise<*> {
+function stepOver(thread: string): Promise<*> {
   return new Promise(resolve => {
-    threadClient.stepOver(resolve);
-  });
-}
-
-function stepOut(): Promise<*> {
-  return new Promise(resolve => {
-    threadClient.stepOut(resolve);
+    lookupThreadClient(thread).stepOver(resolve);
   });
 }
 
-function rewind(): Promise<*> {
+function stepOut(thread: string): Promise<*> {
   return new Promise(resolve => {
-    threadClient.rewind(resolve);
+    lookupThreadClient(thread).stepOut(resolve);
   });
 }
 
-function reverseStepIn(): Promise<*> {
+function rewind(thread: string): Promise<*> {
   return new Promise(resolve => {
-    threadClient.reverseStepIn(resolve);
+    lookupThreadClient(thread).rewind(resolve);
+  });
+}
+
+function reverseStepIn(thread: string): Promise<*> {
+  return new Promise(resolve => {
+    lookupThreadClient(thread).reverseStepIn(resolve);
   });
 }
 
-function reverseStepOver(): Promise<*> {
+function reverseStepOver(thread: string): Promise<*> {
   return new Promise(resolve => {
-    threadClient.reverseStepOver(resolve);
+    lookupThreadClient(thread).reverseStepOver(resolve);
   });
 }
 
-function reverseStepOut(): Promise<*> {
+function reverseStepOut(thread: string): Promise<*> {
   return new Promise(resolve => {
-    threadClient.reverseStepOut(resolve);
+    lookupThreadClient(thread).reverseStepOut(resolve);
   });
 }
 
-function breakOnNext(): Promise<*> {
-  return threadClient.breakOnNext();
+function breakOnNext(thread: string): Promise<*> {
+  return lookupThreadClient(thread).breakOnNext();
 }
 
 function sourceContents(sourceId: SourceId): Source {
-  const sourceClient = threadClient.source({ actor: sourceId });
+  const sourceThreadClient = sourceThreads[sourceId];
+  const sourceClient = sourceThreadClient.source({ actor: sourceId });
   return sourceClient.source();
 }
 
 function getBreakpointByLocation(location: SourceLocation) {
   const id = makePendingLocationId(location);
   const bpClient = bpClients[id];
 
   if (bpClient) {
@@ -157,17 +181,18 @@ function removeXHRBreakpoint(path: strin
   return threadClient.removeXHRBreakpoint(path, method);
 }
 
 function setBreakpoint(
   location: SourceLocation,
   condition: boolean,
   noSliding: boolean
 ): Promise<BreakpointResult> {
-  const sourceClient = threadClient.source({ actor: location.sourceId });
+  const sourceThreadClient = sourceThreads[location.sourceId];
+  const sourceClient = sourceThreadClient.source({ actor: location.sourceId });
 
   return sourceClient
     .setBreakpoint({
       line: location.line,
       column: location.column,
       condition,
       noSliding
     })
@@ -204,49 +229,53 @@ function setBreakpointCondition(
   breakpointId: BreakpointId,
   location: SourceLocation,
   condition: boolean,
   noSliding: boolean
 ) {
   const bpClient = bpClients[breakpointId];
   delete bpClients[breakpointId];
 
+  const sourceThreadClient = sourceThreads[bpClient.source.actor];
   return bpClient
-    .setCondition(threadClient, condition, noSliding)
+    .setCondition(sourceThreadClient, condition, noSliding)
     .then(_bpClient => {
       bpClients[breakpointId] = _bpClient;
       return { id: breakpointId };
     });
 }
 
-async function evaluateInFrame(script: Script, frameId: string) {
-  return evaluate(script, { frameId });
+async function evaluateInFrame(script: Script, options: EvaluateParam) {
+  return evaluate(script, options);
 }
 
-async function evaluateExpressions(scripts: Script[], frameId?: string) {
-  return Promise.all(scripts.map(script => evaluate(script, { frameId })));
+async function evaluateExpressions(scripts: Script[], options: EvaluateParam) {
+  return Promise.all(scripts.map(script => evaluate(script, options)));
 }
 
-type EvaluateParam = { frameId?: FrameId };
+type EvaluateParam = { thread?: string, frameId?: FrameId };
 
 function evaluate(
   script: ?Script,
-  { frameId }: EvaluateParam = {}
+  { thread, frameId }: EvaluateParam = {}
 ): Promise<mixed> {
-  const params = frameId ? { frameActor: frameId } : {};
-  if (!tabTarget || !tabTarget.activeConsole || !script) {
+  const params = { thread, frameActor: frameId };
+  if (!tabTarget || !script) {
+    return Promise.resolve({});
+  }
+
+  const console = thread
+    ? lookupConsoleClient(thread)
+    : tabTarget.activeConsole;
+  if (!console) {
     return Promise.resolve({});
   }
 
   return new Promise(resolve => {
-    tabTarget.activeConsole.evaluateJSAsync(
-      script,
-      result => resolve(result),
-      params
-    );
+    console.evaluateJSAsync(script, result => resolve(result), params);
   });
 }
 
 function autocomplete(
   input: string,
   cursor: number,
   frameId: string
 ): Promise<mixed> {
@@ -266,42 +295,49 @@ function autocomplete(
 function navigate(url: string): Promise<*> {
   return tabTarget.activeTab.navigateTo({ url });
 }
 
 function reload(): Promise<*> {
   return tabTarget.activeTab.reload();
 }
 
-function getProperties(grip: Grip): Promise<*> {
-  const objClient = threadClient.pauseGrip(grip);
+function getProperties(thread: string, grip: Grip): Promise<*> {
+  const objClient = lookupThreadClient(thread).pauseGrip(grip);
 
   return objClient.getPrototypeAndProperties().then(resp => {
     const { ownProperties, safeGetterValues } = resp;
     for (const name in safeGetterValues) {
       const { enumerable, writable, getterValue } = safeGetterValues[name];
       ownProperties[name] = { enumerable, writable, value: getterValue };
     }
     return resp;
   });
 }
 
 async function getFrameScopes(frame: Frame): Promise<*> {
   if (frame.scope) {
     return frame.scope;
   }
 
-  return threadClient.getEnvironment(frame.id);
+  let sourceId = frame.location.sourceId;
+  if (isOriginalId(sourceId)) {
+    sourceId = originalToGeneratedId(sourceId);
+  }
+
+  const sourceThreadClient = sourceThreads[sourceId];
+  return sourceThreadClient.getEnvironment(frame.id);
 }
 
 function pauseOnExceptions(
+  thread: string,
   shouldPauseOnExceptions: boolean,
   shouldPauseOnCaughtExceptions: boolean
 ): Promise<*> {
-  return threadClient.pauseOnExceptions(
+  return lookupThreadClient(thread).pauseOnExceptions(
     shouldPauseOnExceptions,
     // Providing opposite value because server
     // uses "shouldIgnoreCaughtExceptions"
     !shouldPauseOnCaughtExceptions
   );
 }
 
 function prettyPrint(sourceId: SourceId, indentSize: number): Promise<*> {
@@ -320,101 +356,107 @@ async function blackBox(sourceId: Source
   return { isBlackBoxed: !isBlackBoxed };
 }
 
 function disablePrettyPrint(sourceId: SourceId): Promise<*> {
   const sourceClient = threadClient.source({ actor: sourceId });
   return sourceClient.disablePrettyPrint();
 }
 
-async function setPausePoints(sourceId: SourceId, pausePoints: PausePoints) {
+async function setPausePoints(sourceId: SourceId, pausePoints: PausePointsMap) {
   return sendPacket({ to: sourceId, type: "setPausePoints", pausePoints });
 }
 
-async function setSkipPausing(shouldSkip: boolean) {
-  return threadClient.request({
+async function setSkipPausing(thread: string, shouldSkip: boolean) {
+  const client = lookupThreadClient(thread);
+  return client.request({
     skip: shouldSkip,
-    to: threadClient.actor,
+    to: client.actor,
     type: "skipBreakpoints"
   });
 }
 
-function interrupt(): Promise<*> {
-  return threadClient.interrupt();
+function interrupt(thread: string): Promise<*> {
+  return lookupThreadClient(thread).interrupt();
 }
 
 function eventListeners(): Promise<*> {
   return threadClient.eventListeners();
 }
 
-function pauseGrip(func: Function): ObjectClient {
-  return threadClient.pauseGrip(func);
+function pauseGrip(thread: string, func: Function): ObjectClient {
+  return lookupThreadClient(thread).pauseGrip(func);
+}
+
+function registerSource(source: Source) {
+  if (isOriginalId(source.id)) {
+    throw new Error("registerSource called with original ID");
+  }
+  sourceThreads[source.id] = lookupThreadClient(source.thread);
 }
 
-async function fetchSources() {
-  const { sources } = await threadClient.getSources();
+async function createSources(client: ThreadClient) {
+  const { sources } = await client.getSources();
+  return (
+    sources &&
+    sources.map(packet => createSource(client.actor, packet, { supportsWasm }))
+  );
+}
+
+async function fetchSources(): Promise<any[]> {
+  let sources = await createSources(threadClient);
 
   // NOTE: this happens when we fetch sources and then immediately navigate
   if (!sources) {
-    return;
+    return [];
   }
 
-  return sources.map(source => createSource(source, { supportsWasm }));
-}
+  if (features.windowlessWorkers) {
+    // Also fetch sources from any workers.
+    workerClients = await updateWorkerClients({
+      threadClient,
+      debuggerClient,
+      tabTarget,
+      workerClients
+    });
 
-/**
- * Temporary helper to check if the current server will support a call to
- * listWorkers. On Fennec 60 or older, the call will silently crash and prevent
- * the client from resuming.
- * XXX: Remove when FF60 for Android is no longer used or available.
- *
- * See https://bugzilla.mozilla.org/show_bug.cgi?id=1443550 for more details.
- */
-async function checkServerSupportsListWorkers() {
-  const root = await tabTarget.root;
-  // root is not available on all debug targets.
-  if (!root) {
-    return false;
+    const workerNames = Object.getOwnPropertyNames(workerClients);
+    workerNames.forEach(actor => {
+      const workerSources = createSources(workerClients[actor].thread);
+      if (workerSources) {
+        sources = sources.concat(workerSources);
+      }
+    });
   }
 
-  const deviceFront = await debuggerClient.mainRoot.getFront("device");
-  const description = await deviceFront.getDescription();
-
-  const isFennec = description.apptype === "mobile/android";
-  if (!isFennec) {
-    // Explicitly return true early to avoid calling Services.vs.compare.
-    // This would force us to extent the Services shim provided by
-    // devtools-modules, used when this code runs in a tab.
-    return true;
-  }
-
-  // We are only interested in Fennec release versions here.
-  // We assume that the server fix for Bug 1443550 will land in FF61.
-  const version = description.platformversion;
-  return Services.vc.compare(version, "61.0") >= 0;
+  return sources;
 }
 
 async function fetchWorkers(): Promise<{ workers: Worker[] }> {
-  // Temporary workaround for Bug 1443550
-  // XXX: Remove when FF60 for Android is no longer used or available.
-  const supportsListWorkers = await checkServerSupportsListWorkers();
+  if (features.windowlessWorkers) {
+    workerClients = await updateWorkerClients({
+      tabTarget,
+      debuggerClient,
+      threadClient,
+      workerClients
+    });
 
-  // NOTE: The Worker and Browser Content toolboxes do not have a parent
-  // with a listWorkers function
-  // TODO: there is a listWorkers property, but it is not a function on the
-  // parent. Investigate what it is
-  if (
-    !threadClient._parent ||
-    typeof threadClient._parent.listWorkers != "function" ||
-    !supportsListWorkers
-  ) {
+    const workerNames = Object.getOwnPropertyNames(workerClients);
+    return {
+      workers: workerNames.map(actor =>
+        createWorker(actor, workerClients[actor].url)
+      )
+    };
+  }
+
+  if (!supportsWorkers(tabTarget)) {
     return Promise.resolve({ workers: [] });
   }
 
-  return threadClient._parent.listWorkers();
+  return tabTarget.activeTab.listWorkers();
 }
 
 const clientCommands = {
   autocomplete,
   blackBox,
   createObjectClient,
   releaseActor,
   interrupt,
@@ -445,12 +487,13 @@ const clientCommands = {
   getFrameScopes,
   pauseOnExceptions,
   prettyPrint,
   disablePrettyPrint,
   fetchSources,
   fetchWorkers,
   sendPacket,
   setPausePoints,
-  setSkipPausing
+  setSkipPausing,
+  registerSource
 };
 
 export { setupCommands, clientCommands };
--- a/devtools/client/debugger/new/src/client/firefox/create.js
+++ b/devtools/client/debugger/new/src/client/firefox/create.js
@@ -8,16 +8,18 @@
 import type { Frame, Source, SourceLocation } from "../../types";
 import type {
   PausedPacket,
   FramesResponse,
   FramePacket,
   SourcePayload
 } from "./types";
 
+import { clientCommands } from "./commands";
+
 export function createFrame(frame: FramePacket): ?Frame {
   if (!frame) {
     return null;
   }
   let title;
   if (frame.type == "call") {
     const c = frame.callee;
     title = c.name || c.userDisplayName || c.displayName;
@@ -36,43 +38,48 @@ export function createFrame(frame: Frame
     location,
     generatedLocation: location,
     this: frame.this,
     scope: frame.environment
   };
 }
 
 export function createSource(
+  thread: string,
   source: SourcePayload,
   { supportsWasm }: { supportsWasm: boolean }
 ): Source {
   const createdSource = {
     id: source.actor,
+    thread,
     url: source.url,
     relativeUrl: source.url,
     isPrettyPrinted: false,
     isWasm: false,
     sourceMapURL: source.sourceMapURL,
     isBlackBoxed: false,
     loadedState: "unloaded"
   };
+  clientCommands.registerSource(createdSource);
   return Object.assign(createdSource, {
     isWasm: supportsWasm && source.introductionType === "wasm"
   });
 }
 
 export function createPause(
+  thread: string,
   packet: PausedPacket,
   response: FramesResponse
 ): any {
   // NOTE: useful when the debugger is already paused
   const frame = packet.frame || response.frames[0];
 
   return {
     ...packet,
+    thread,
     frame: createFrame(frame),
     frames: response.frames.map(createFrame)
   };
 }
 
 // Firefox only returns `actualLocation` if it actually changed,
 // but we want it always to exist. Format `actualLocation` if it
 // exists, otherwise use `location`.
@@ -87,8 +94,17 @@ export function createBreakpointLocation
 
   return {
     sourceId: actualLocation.source.actor,
     sourceUrl: actualLocation.source.url,
     line: actualLocation.line,
     column: actualLocation.column
   };
 }
+
+export function createWorker(actor: string, url: string) {
+  return {
+    actor,
+    url,
+    // Ci.nsIWorkerDebugger.TYPE_DEDICATED
+    type: 0
+  };
+}
--- a/devtools/client/debugger/new/src/client/firefox/events.js
+++ b/devtools/client/debugger/new/src/client/firefox/events.js
@@ -18,48 +18,55 @@ import sourceQueue from "../../utils/sou
 const CALL_STACK_PAGE_SIZE = 1000;
 
 type Dependencies = {
   threadClient: ThreadClient,
   actions: Actions,
   supportsWasm: boolean
 };
 
-let threadClient: ThreadClient;
 let actions: Actions;
 let supportsWasm: boolean;
 let isInterrupted: boolean;
 
+function addThreadEventListeners(client: ThreadClient) {
+  Object.keys(clientEvents).forEach(eventName => {
+    client.addListener(eventName, clientEvents[eventName].bind(null, client));
+  });
+}
+
 function setupEvents(dependencies: Dependencies) {
-  threadClient = dependencies.threadClient;
+  const threadClient = dependencies.threadClient;
   actions = dependencies.actions;
   supportsWasm = dependencies.supportsWasm;
   sourceQueue.initialize(actions);
 
   if (threadClient) {
-    Object.keys(clientEvents).forEach(eventName => {
-      threadClient.addListener(eventName, clientEvents[eventName]);
-    });
+    addThreadEventListeners(threadClient);
 
     if (threadClient._parent) {
       // Parent may be BrowsingContextTargetFront/WorkerTargetFront and
       // be protocol.js.  Or DebuggerClient and still be old fashion actor.
       if (threadClient._parent.on) {
         threadClient._parent.on("workerListChanged", workerListChanged);
       } else {
         threadClient._parent.addListener(
           "workerListChanged",
           workerListChanged
         );
       }
     }
   }
 }
 
-async function paused(_: "paused", packet: PausedPacket) {
+async function paused(
+  threadClient: ThreadClient,
+  _: "paused",
+  packet: PausedPacket
+) {
   // If paused by an explicit interrupt, which are generated by the
   // slow script dialog and internal events such as setting
   // breakpoints, ignore the event.
   const { why } = packet;
   if (why.type === "interrupted" && !packet.why.onNext) {
     isInterrupted = true;
     return;
   }
@@ -74,41 +81,49 @@ async function paused(_: "paused", packe
   }
 
   // NOTE: this happens if we fetch frames and then immediately navigate
   if (!response.hasOwnProperty("frames")) {
     return;
   }
 
   if (why.type != "alreadyPaused") {
-    const pause = createPause(packet, response);
+    const pause = createPause(threadClient.actor, packet, response);
     await sourceQueue.flush();
     actions.paused(pause);
   }
 }
 
-function resumed(_: "resumed", packet: ResumedPacket) {
+function resumed(
+  threadClient: ThreadClient,
+  _: "resumed",
+  packet: ResumedPacket
+) {
   // NOTE: the client suppresses resumed events while interrupted
   // to prevent unintentional behavior.
   // see [client docs](../README.md#interrupted) for more information.
   if (isInterrupted) {
     isInterrupted = false;
     return;
   }
 
   actions.resumed(packet);
 }
 
-function newSource(_: "newSource", { source }: SourcePacket) {
-  sourceQueue.queue(createSource(source, { supportsWasm }));
+function newSource(
+  threadClient: ThreadClient,
+  _: "newSource",
+  { source }: SourcePacket
+) {
+  sourceQueue.queue(createSource(threadClient.actor, source, { supportsWasm }));
 }
 
 function workerListChanged() {
   actions.updateWorkers();
 }
 
 const clientEvents = {
   paused,
   resumed,
   newSource
 };
 
-export { setupEvents, clientEvents };
+export { setupEvents, clientEvents, addThreadEventListeners };
--- a/devtools/client/debugger/new/src/client/firefox/moz.build
+++ b/devtools/client/debugger/new/src/client/firefox/moz.build
@@ -6,9 +6,10 @@
 DIRS += [
 
 ]
 
 DebuggerModules(
     'commands.js',
     'create.js',
     'events.js',
+    'workers.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/client/firefox/workers.js
@@ -0,0 +1,54 @@
+/* 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 { addThreadEventListeners } from "./events";
+import type { TabTarget } from "./types";
+
+export function supportsWorkers(tabTarget: TabTarget) {
+  return tabTarget.isBrowsingContext || tabTarget.isContentProcess;
+}
+
+export async function updateWorkerClients({
+  tabTarget,
+  debuggerClient,
+  threadClient,
+  workerClients
+}: Object) {
+  if (!supportsWorkers(tabTarget)) {
+    return {};
+  }
+
+  const newWorkerClients = {};
+
+  const { workers } = await tabTarget.activeTab.listWorkers();
+  for (const workerTargetFront of workers) {
+    await workerTargetFront.attach();
+    const [, workerThread] = await workerTargetFront.attachThread();
+
+    if (workerClients[workerThread.actor]) {
+      if (workerClients[workerThread.actor].thread != workerThread) {
+        throw new Error(`Multiple clients for actor ID: ${workerThread.actor}`);
+      }
+      newWorkerClients[workerThread.actor] = workerClients[workerThread.actor];
+    } else {
+      addThreadEventListeners(workerThread);
+      workerThread.resume();
+
+      const [, consoleClient] = await debuggerClient.attachConsole(
+        workerTargetFront.targetForm.consoleActor,
+        []
+      );
+
+      newWorkerClients[workerThread.actor] = {
+        url: workerTargetFront.url,
+        thread: workerThread,
+        console: consoleClient
+      };
+    }
+  }
+
+  return newWorkerClients;
+}
--- a/devtools/client/debugger/new/src/client/index.js
+++ b/devtools/client/debugger/new/src/client/index.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import * as firefox from "./firefox";
+import * as chrome from "./chrome";
 
 import { prefs, asyncStore } from "../utils/prefs";
 import { setupHelper } from "../utils/dbg";
 
 import {
   bootstrapApp,
   bootstrapStore,
   bootstrapWorkers
@@ -41,44 +42,55 @@ async function loadInitialState() {
   const tabs = await asyncStore.tabs;
   const xhrBreakpoints = await asyncStore.xhrBreakpoints;
 
   const breakpoints = initialBreakpointsState(xhrBreakpoints);
 
   return { pendingBreakpoints, tabs, breakpoints };
 }
 
+function getClient(connection: any) {
+  const {
+    tab: { clientType }
+  } = connection;
+  return clientType == "firefox" ? firefox : chrome;
+}
+
 export async function onConnect(
   connection: Object,
   { services, toolboxActions }: Object
 ) {
   // NOTE: the landing page does not connect to a JS process
   if (!connection) {
     return;
   }
 
-  const commands = firefox.clientCommands;
+  const client = getClient(connection);
+  const commands = client.clientCommands;
+
   const initialState = await loadInitialState();
+
   const { store, actions, selectors } = bootstrapStore(
     commands,
     {
       services,
       toolboxActions
     },
     initialState
   );
 
   const workers = bootstrapWorkers();
-  await firefox.onConnect(connection, actions);
+  await client.onConnect(connection, actions);
+
   await loadFromPrefs(actions);
   syncXHRBreakpoints();
   setupHelper({
     store,
     actions,
     selectors,
     workers: { ...workers, ...services },
     connection,
-    client: firefox.clientCommands
+    client: client.clientCommands
   });
 
   bootstrapApp(store);
   return { store, actions, selectors, client: commands };
 }
--- a/devtools/client/debugger/new/src/client/moz.build
+++ b/devtools/client/debugger/new/src/client/moz.build
@@ -1,13 +1,15 @@
 # vim: set filetype=python:
 # 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/.
 
 DIRS += [
+    'chrome',
     'firefox',
 ]
 
 DebuggerModules(
+    'chrome.js',
     'firefox.js',
     'index.js',
 )
--- a/devtools/client/debugger/new/src/components/App.js
+++ b/devtools/client/debugger/new/src/components/App.js
@@ -1,18 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 import React, { Component } from "react";
-import { connect } from "react-redux";
 import PropTypes from "prop-types";
 import classnames from "classnames";
 
+import { connect } from "../utils/connect";
 import { prefs, features } from "../utils/prefs";
 import actions from "../actions";
 import A11yIntention from "./A11yIntention";
 import { ShortcutsModal } from "./ShortcutsModal";
 
 import {
   getSelectedSource,
   getPaneCollapse,
@@ -39,16 +39,18 @@ const verticalLayoutBreakpoint = window.
 );
 
 import "./variables.css";
 import "./App.css";
 
 // $FlowIgnore
 import "devtools-launchpad/src/components/Root.css";
 
+import type { ActiveSearchType } from "../selectors";
+
 import "./shared/menu.css";
 import "./shared/reps.css";
 
 import SplitBox from "devtools-splitter";
 import ProjectSearch from "./ProjectSearch";
 import PrimaryPanes from "./PrimaryPanes";
 import Editor from "./Editor";
 import SecondaryPanes from "./SecondaryPanes";
@@ -56,17 +58,17 @@ import WelcomeBox from "./WelcomeBox";
 import EditorTabs from "./Editor/Tabs";
 import QuickOpenModal from "./QuickOpenModal";
 
 type Props = {
   selectedSource: Source,
   orientation: OrientationType,
   startPanelCollapsed: boolean,
   endPanelCollapsed: boolean,
-  activeSearch: string,
+  activeSearch: ActiveSearchType,
   quickOpenEnabled: boolean,
   canRewind: boolean,
   setActiveSearch: typeof actions.setActiveSearch,
   closeActiveSearch: typeof actions.closeActiveSearch,
   closeProjectSearch: typeof actions.closeProjectSearch,
   openQuickOpen: typeof actions.openQuickOpen,
   closeQuickOpen: typeof actions.closeQuickOpen,
   setOrientation: typeof actions.setOrientation
--- a/devtools/client/debugger/new/src/components/Editor/Breakpoints.js
+++ b/devtools/client/debugger/new/src/components/Editor/Breakpoints.js
@@ -1,20 +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 { connect } from "react-redux";
 import React, { Component } from "react";
 import Breakpoint from "./Breakpoint";
 
 import { getSelectedSource, getFirstVisibleBreakpoints } from "../../selectors";
 import { makeLocationId } from "../../utils/breakpoint";
 import { isLoaded } from "../../utils/source";
+import { connect } from "../../utils/connect";
 
 import type { Breakpoint as BreakpointType, Source } from "../../types";
 
 type Props = {
   selectedSource: Source,
   breakpoints: BreakpointType[],
   editor: Object
 };
--- a/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoints.js
+++ b/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoints.js
@@ -1,19 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 import React, { Component } from "react";
-import { connect } from "react-redux";
 
 import ColumnBreakpoint from "./ColumnBreakpoint";
 import "./ColumnBreakpoints.css";
 
 import { getSelectedSource, visibleColumnBreakpoints } from "../../selectors";
+import { connect } from "../../utils/connect";
 import { makeLocationId } from "../../utils/breakpoint";
 import actions from "../../actions";
 
 import type { Source } from "../../types";
 // eslint-disable-next-line max-len
 import type { ColumnBreakpoint as ColumnBreakpointType } from "../../selectors/visibleColumnBreakpoints";
 
 class ColumnBreakpoints extends Component {
--- a/devtools/client/debugger/new/src/components/Editor/ConditionalPanel.js
+++ b/devtools/client/debugger/new/src/components/Editor/ConditionalPanel.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 import React, { PureComponent } from "react";
 import ReactDOM from "react-dom";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import "./ConditionalPanel.css";
 import { toEditorLine } from "../../utils/editor";
 import actions from "../../actions";
 
 import {
   getBreakpointForLocation,
   getConditionalPanelLocation
 } from "../../selectors";
--- a/devtools/client/debugger/new/src/components/Editor/DebugLine.js
+++ b/devtools/client/debugger/new/src/components/Editor/DebugLine.js
@@ -9,17 +9,17 @@ import {
   getDocument,
   hasDocument,
   startOperation,
   endOperation
 } from "../../utils/editor";
 import { isLoaded } from "../../utils/source";
 import { isException } from "../../utils/pause";
 import { getIndentation } from "../../utils/indentation";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import {
   getVisibleSelectedFrame,
   getPauseReason,
   getSelectedSource
 } from "../../selectors";
 
 import type { Frame, Why, Source } from "../../types";
 
--- a/devtools/client/debugger/new/src/components/Editor/EditorMenu.js
+++ b/devtools/client/debugger/new/src/components/Editor/EditorMenu.js
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 import { Component } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import { showMenu } from "devtools-contextmenu";
 import { isOriginalId } from "devtools-source-map";
 
 import { copyToTheClipboard } from "../../utils/clipboard";
 import { findFunctionText } from "../../utils/function";
 import { findClosestFunction } from "../../utils/ast";
 import {
   getSourceLocationFromMouseEvent,
--- a/devtools/client/debugger/new/src/components/Editor/EmptyLines.js
+++ b/devtools/client/debugger/new/src/components/Editor/EmptyLines.js
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import { Component } from "react";
 import { getSelectedSource, getEmptyLines } from "../../selectors";
 import type { Source } from "../../types";
 import { toEditorLine } from "../../utils/editor";
 
 type props = {
   selectedSource: Source,
   editor: Object,
--- a/devtools/client/debugger/new/src/components/Editor/Footer.js
+++ b/devtools/client/debugger/new/src/components/Editor/Footer.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 React, { PureComponent } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import classnames from "classnames";
 import Svg from "../shared/Svg";
 import actions from "../../actions";
 import {
   getSelectedSource,
   getPrettySource,
   getPaneCollapse
 } from "../../selectors";
--- a/devtools/client/debugger/new/src/components/Editor/GutterMenu.js
+++ b/devtools/client/debugger/new/src/components/Editor/GutterMenu.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/>. */
 
 import { Component } from "react";
 import { showMenu } from "devtools-contextmenu";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import { lineAtHeight } from "../../utils/editor";
 import {
   getContextMenu,
   getEmptyLines,
   getSelectedLocation,
   getSelectedSource,
   getVisibleBreakpoints,
   isPaused as getIsPaused
--- a/devtools/client/debugger/new/src/components/Editor/HighlightLine.js
+++ b/devtools/client/debugger/new/src/components/Editor/HighlightLine.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 import { Component } from "react";
 import { toEditorLine, endOperation, startOperation } from "../../utils/editor";
 import { getDocument, hasDocument } from "../../utils/editor/source-documents";
 import { isLoaded } from "../../utils/source";
 
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import {
   getVisibleSelectedFrame,
   getSelectedLocation,
   getSelectedSource,
   getPauseCommand
 } from "../../selectors";
 
 import type { Frame, SourceLocation, Source } from "../../types";
--- a/devtools/client/debugger/new/src/components/Editor/HighlightLines.js
+++ b/devtools/client/debugger/new/src/components/Editor/HighlightLines.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 import { Component } from "react";
 import { range, isEmpty } from "lodash";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import { getHighlightedLineRange } from "../../selectors";
 
 type Props = {
   highlightedLineRange: Object,
   editor: Object
 };
 
 class HighlightLines extends Component<Props> {
--- a/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js
+++ b/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js
@@ -1,36 +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 React, { Component } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../../utils/connect";
 
 import Reps from "devtools-reps";
 const {
   REPS: { Rep },
   MODE,
   objectInspector
 } = Reps;
 
 const { ObjectInspector, utils } = objectInspector;
 
 const {
-  node: { createNode, getChildren, getValue, nodeIsPrimitive, NODE_TYPES },
+  node: { createNode, getChildren, getValue, nodeIsPrimitive },
   loadProperties: { loadItemProperties }
 } = utils;
 
 import actions from "../../../actions";
 import { getAllPopupObjectProperties } from "../../../selectors";
 import Popover from "../../shared/Popover";
 import PreviewFunction from "../../shared/PreviewFunction";
-import { isReactComponent, isImmutablePreview } from "../../../utils/preview";
 
 import Svg from "../../shared/Svg";
 import { createObjectClient } from "../../../client/firefox";
 
 import "./Popup.css";
 
 import type { EditorRange } from "../../../utils/editor/types";
 import type { Coords } from "../../shared/Popover";
@@ -40,17 +39,16 @@ type Props = {
   popupObjectProperties: Object,
   popoverPos: Object,
   value: PopupValue,
   expression: string,
   onClose: () => void,
   range: EditorRange,
   editor: any,
   editorRef: ?HTMLDivElement,
-  extra: Object,
   setPopupObjectProperties: typeof actions.setPopupObjectProperties,
   addExpression: typeof actions.addExpression,
   selectSourceURL: typeof actions.selectSourceURL,
   openLink: typeof actions.openLink,
   openElementInInspector: typeof actions.openElementInInspectorCommand
 };
 
 type State = {
@@ -124,27 +122,22 @@ export class Popup extends Component<Pro
 
   onKeyDown = (e: KeyboardEvent) => {
     if (e.key === "Escape") {
       this.props.onClose();
     }
   };
 
   getRoot() {
-    const { expression, value, extra } = this.props;
-
-    let rootValue = value;
-    if (extra.immutable) {
-      rootValue = extra.immutable.entries;
-    }
+    const { expression, value } = this.props;
 
     return createNode({
       name: expression,
       path: expression,
-      contents: { value: rootValue }
+      contents: { value }
     });
   }
 
   getObjectProperties() {
     const { popupObjectProperties } = this.props;
     const root = this.getRoot();
     const value = getValue(root);
     if (!value) {
@@ -218,43 +211,32 @@ export class Popup extends Component<Pro
       <div className="header-container">
         <Svg name="immutable" className="logo" />
         <h3>{immutableHeader}</h3>
       </div>
     );
   }
 
   renderObjectPreview() {
-    const { extra, value } = this.props;
     const root = this.getRoot();
 
     if (nodeIsPrimitive(root)) {
       return null;
     }
 
-    let roots = this.getChildren();
+    const roots = this.getChildren();
     if (!Array.isArray(roots) || roots.length === 0) {
       return null;
     }
 
-    let header = null;
-    if (extra.immutable && isImmutablePreview(value)) {
-      header = this.renderImmutable(extra.immutable);
-      roots = roots.filter(r => r.type != NODE_TYPES.PROTOTYPE);
-    } else if (extra.react && isReactComponent(this.getObjectProperties())) {
-      header = this.renderReact(extra.react);
-      roots = roots.filter(r => ["state", "props"].includes(r.name));
-    }
-
     return (
       <div
         className="preview-popup"
         style={{ maxHeight: this.calculateMaxHeight() }}
       >
-        {header}
         {this.renderObjectInspector(roots)}
       </div>
     );
   }
 
   renderSimplePreview(value: any) {
     const { openLink } = this.props;
     return (
--- a/devtools/client/debugger/new/src/components/Editor/Preview/index.js
+++ b/devtools/client/debugger/new/src/components/Editor/Preview/index.js
@@ -1,33 +1,33 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { PureComponent } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../../utils/connect";
 
 import Popup from "./Popup";
 
 import { getPreview, getSelectedSource, getIsPaused } from "../../../selectors";
 import actions from "../../../actions";
 import { toEditorRange } from "../../../utils/editor";
 
 import type { Source } from "../../../types";
 
 import type { Preview as PreviewType } from "../../../reducers/ast";
 
 type Props = {
   editor: any,
   editorRef: ?HTMLDivElement,
   selectedSource: Source,
   preview: PreviewType,
-  isPaused: Boolean,
+  isPaused: boolean,
   clearPreview: typeof actions.clearPreview,
   setPopupObjectProperties: typeof actions.setPopupObjectProperties,
   addExpression: typeof actions.addExpression,
   updatePreview: typeof actions.updatePreview
 };
 
 type State = {
   selecting: boolean
@@ -143,33 +143,32 @@ class Preview extends PureComponent<Prop
     if (!this.props.editor || !selectedSource || this.state.selecting) {
       return null;
     }
 
     if (!preview || preview.updating) {
       return null;
     }
 
-    const { result, expression, location, cursorPos, extra } = preview;
+    const { result, expression, location, cursorPos } = preview;
     const value = result;
     if (typeof value == "undefined" || value.optimizedOut) {
       return null;
     }
 
     const editorRange = toEditorRange(selectedSource.id, location);
 
     return (
       <Popup
         value={value}
         editor={this.props.editor}
         editorRef={this.props.editorRef}
         range={editorRange}
         expression={expression}
         popoverPos={cursorPos}
-        extra={extra}
         onClose={this.onClose}
       />
     );
   }
 }
 
 const mapStateToProps = state => ({
   preview: getPreview(state),
--- a/devtools/client/debugger/new/src/components/Editor/SearchBar.js
+++ b/devtools/client/debugger/new/src/components/Editor/SearchBar.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import PropTypes from "prop-types";
 import React, { Component } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import { CloseButton } from "../shared/Button";
 import Svg from "../shared/Svg";
 import actions from "../../actions";
 import {
   getActiveSearch,
   getSelectedSource,
   getSelectedLocation,
   getFileSearchQuery,
--- a/devtools/client/debugger/new/src/components/Editor/Tab.js
+++ b/devtools/client/debugger/new/src/components/Editor/Tab.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { PureComponent } from "react";
-import { connect } from "react-redux";
+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";
@@ -30,26 +30,27 @@ import { copyToTheClipboard } from "../.
 import { getTabMenuItems } from "../../utils/tabs";
 
 import {
   getSelectedSource,
   getActiveSearch,
   getSourcesForTabs,
   getHasSiblingOfSameName
 } from "../../selectors";
+import type { ActiveSearchType } from "../../selectors";
 
 import classnames from "classnames";
 
 type SourcesList = List<Source>;
 
 type Props = {
   tabSources: SourcesList,
   selectedSource: Source,
   source: Source,
-  activeSearch: string,
+  activeSearch: ActiveSearchType,
   hasSiblingOfSameName: boolean,
   selectSource: typeof actions.selectSource,
   closeTab: typeof actions.closeTab,
   closeTabs: typeof actions.closeTabs,
   togglePrettyPrint: typeof actions.togglePrettyPrint,
   showSource: typeof actions.showSource
 };
 
--- a/devtools/client/debugger/new/src/components/Editor/Tabs.js
+++ b/devtools/client/debugger/new/src/components/Editor/Tabs.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { PureComponent } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 
 import { getSelectedSource, getSourcesForTabs } from "../../selectors";
 import { isVisible } from "../../utils/ui";
 
 import { getHiddenTabs } from "../../utils/tabs";
 import { getFilename, isPretty } from "../../utils/source";
 import actions from "../../actions";
 
--- a/devtools/client/debugger/new/src/components/Editor/index.js
+++ b/devtools/client/debugger/new/src/components/Editor/index.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import PropTypes from "prop-types";
 import React, { PureComponent } from "react";
 import ReactDOM from "react-dom";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import classnames from "classnames";
 import { debounce } from "lodash";
 
 import { isLoaded } from "../../utils/source";
 import { isFirefox } from "devtools-environment";
 import { features } from "../../utils/prefs";
 import { getIndentation } from "../../utils/indentation";
 
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/Outline.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/Outline.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { Component } from "react";
 import { showMenu } from "devtools-contextmenu";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import { score as fuzzaldrinScore } from "fuzzaldrin-plus";
 
 import { copyToTheClipboard } from "../../utils/clipboard";
 import { findFunctionText } from "../../utils/function";
 
 import actions from "../../actions";
 import {
   getSelectedSource,
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 // Dependencies
 import React, { Component } from "react";
 import classnames from "classnames";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 
 // Selectors
 import {
   getShownSource,
   getSelectedSource,
   getDebuggeeUrl,
   getExpandedState,
   getProjectDirectoryRoot,
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTreeItem.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTreeItem.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { Component } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import classnames from "classnames";
 import { showMenu } from "devtools-contextmenu";
 
 import SourceIcon from "../shared/SourceIcon";
 import AccessibleImage from "../shared/AccessibleImage";
 import Svg from "../shared/Svg";
 
 import {
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/index.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/index.js
@@ -1,40 +1,40 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { Component } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import { Tab, Tabs, TabList, TabPanels } from "react-aria-components/src/tabs";
 import { formatKeyShortcut } from "../../utils/text";
 import actions from "../../actions";
 import {
   getSources,
   getActiveSearch,
   getSelectedPrimaryPaneTab
 } from "../../selectors";
 import { features, prefs } from "../../utils/prefs";
 import "./Sources.css";
 import classnames from "classnames";
 
 import Outline from "./Outline";
 import SourcesTree from "./SourcesTree";
 
 import type { SourcesMap } from "../../reducers/types";
-import type { SelectedPrimaryPaneTabType } from "../../reducers/ui";
+import type { SelectedPrimaryPaneTabType } from "../../selectors";
 
 type State = {
   alphabetizeOutline: boolean
 };
 
 type Props = {
-  selectedTab: string,
+  selectedTab: SelectedPrimaryPaneTabType,
   sources: SourcesMap,
   horizontal: boolean,
   sourceSearchOn: boolean,
   setPrimaryPaneTab: typeof actions.setPrimaryPaneTab,
   setActiveSearch: typeof actions.setActiveSearch,
   closeActiveSearch: typeof actions.closeActiveSearch
 };
 
--- a/devtools/client/debugger/new/src/components/ProjectSearch.js
+++ b/devtools/client/debugger/new/src/components/ProjectSearch.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import PropTypes from "prop-types";
 import React, { Component } from "react";
-import { connect } from "react-redux";
+import { connect } from "../utils/connect";
 import classnames from "classnames";
 import actions from "../actions";
 
 import { getEditor } from "../utils/editor";
 import { highlightMatches } from "../utils/project-search";
 
 import { statusType } from "../reducers/project-text-search";
 import { getRelativePath } from "../utils/sources-tree";
@@ -152,30 +152,18 @@ export class ProjectSearch extends Compo
     this.props.doSearchForHighlight(
       this.state.inputValue,
       getEditor(),
       matchItem.line,
       matchItem.column
     );
   };
 
-  getResults = (): Result[] => {
-    const { results } = this.props;
-    return results
-      .toJS()
-      .map(result => ({
-        type: "RESULT",
-        ...result,
-        matches: result.matches.map(m => ({ type: "MATCH", ...m }))
-      }))
-      .filter(result => result.filepath && result.matches.length > 0);
-  };
-
   getResultCount = () =>
-    this.getResults().reduce((count, file) => count + file.matches.length, 0);
+    this.props.results.reduce((count, file) => count + file.matches.length, 0);
 
   onKeyDown = (e: SyntheticKeyboardEvent<HTMLInputElement>) => {
     if (e.key === "Escape") {
       return;
     }
 
     e.stopPropagation();
 
@@ -266,18 +254,17 @@ export class ProjectSearch extends Compo
   ) => {
     if (item.type === "RESULT") {
       return this.renderFile(item, focused, expanded, setExpanded);
     }
     return this.renderMatch(item, focused);
   };
 
   renderResults = () => {
-    const results = this.getResults();
-    const { status } = this.props;
+    const { status, results } = this.props;
     if (!this.props.query) {
       return;
     }
     if (results.length && status === statusType.done) {
       return (
         <ManagedTree
           getRoots={() => results}
           getChildren={file => file.matches || []}
--- a/devtools/client/debugger/new/src/components/QuickOpenModal.js
+++ b/devtools/client/debugger/new/src/components/QuickOpenModal.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 React, { Component } from "react";
-import { connect } from "react-redux";
+import { connect } from "../utils/connect";
 import fuzzyAldrin from "fuzzaldrin-plus";
 import { basename } from "../utils/path";
 
 import actions from "../actions";
 import {
   getRelativeSources,
   getQuickOpenEnabled,
   getQuickOpenQuery,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { PureComponent } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../../utils/connect";
 import { createSelector } from "reselect";
 import classnames from "classnames";
 
 import actions from "../../../actions";
 
 import showContextMenu from "./BreakpointsContextMenu";
 import { CloseButton } from "../../shared/Button";
 
@@ -23,32 +23,31 @@ import type { FormattedBreakpoint } from
 
 import type {
   Breakpoint as BreakpointType,
   Frame,
   Source,
   SourceLocation
 } from "../../../types";
 
-type FormattedFrame = {
-  ...Frame,
+type FormattedFrame = Frame & {
   selectedLocation: SourceLocation
 };
 
 import {
   getBreakpointsList,
   getSelectedFrame,
   getSelectedSource
 } from "../../../selectors";
 
 type Props = {
   breakpoint: FormattedBreakpoint,
   breakpoints: BreakpointType[],
   source: Source,
-  frame: ?FormattedFrame,
+  frame: FormattedFrame,
   enableBreakpoint: typeof actions.enableBreakpoint,
   removeBreakpoint: typeof actions.removeBreakpoint,
   removeBreakpoints: typeof actions.removeBreakpoints,
   removeAllBreakpoints: typeof actions.removeAllBreakpoints,
   disableBreakpoint: typeof actions.disableBreakpoint,
   setBreakpointCondition: typeof actions.setBreakpointCondition,
   toggleAllBreakpoints: typeof actions.toggleAllBreakpoints,
   toggleBreakpoints: typeof actions.toggleBreakpoints,
@@ -171,17 +170,17 @@ class Breakpoint extends PureComponent<P
       </div>
     );
   }
 }
 
 const getFormattedFrame = createSelector(
   getSelectedSource,
   getSelectedFrame,
-  (selectedSource: Source, frame: Frame) => {
+  (selectedSource: Source, frame: Frame): ?FormattedFrame => {
     if (!frame) {
       return null;
     }
 
     return {
       ...frame,
       selectedLocation: getSelectedLocation(frame, selectedSource)
     };
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointHeading.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointHeading.js
@@ -1,61 +1,80 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 import React, { PureComponent } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../../utils/connect";
 import actions from "../../../actions";
 import {
   getTruncatedFileName,
   getDisplayPath,
   getSourceQueryString,
   getFileURL
 } from "../../../utils/source";
-import { getHasSiblingOfSameName } from "../../../selectors";
+import {
+  getHasSiblingOfSameName,
+  getBreakpointsForSource
+} from "../../../selectors";
 
 import SourceIcon from "../../shared/SourceIcon";
 
-import type { Source } from "../../../types";
+import type { Source, Breakpoint } from "../../../types";
+import showContextMenu from "./BreakpointHeadingsContextMenu";
 
 type Props = {
   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 path = getDisplayPath(source, sources);
     const query = hasSiblingOfSameName ? getSourceQueryString(source) : "";
 
     return (
       <div
         className="breakpoint-heading"
         title={getFileURL(source, false)}
         onClick={() => selectSource(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 }) => ({
-  hasSiblingOfSameName: getHasSiblingOfSameName(state, source)
+  hasSiblingOfSameName: getHasSiblingOfSameName(state, source),
+  breakpointsForSource: getBreakpointsForSource(state, source.id)
 });
 
 export default connect(
   mapStateToProps,
-  { selectSource: actions.selectSource }
+  {
+    selectSource: actions.selectSource,
+    enableBreakpointsInSource: actions.enableBreakpointsInSource,
+    disableBreakpointsInSource: actions.disableBreakpointsInSource,
+    removeBreakpointsInSource: actions.removeBreakpointsInSource
+  }
 )(BreakpointHeading);
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointHeadingsContextMenu.js
@@ -0,0 +1,90 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+// @flow
+
+import { buildMenu, showMenu } from "devtools-contextmenu";
+
+import actions from "../../../actions";
+import type { Breakpoint, Source } from "../../../types";
+
+type Props = {
+  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 {
+    source,
+    breakpointsForSource,
+    disableBreakpointsInSource,
+    enableBreakpointsInSource,
+    removeBreakpointsInSource,
+    contextMenuEvent
+  } = props;
+
+  contextMenuEvent.preventDefault();
+
+  const enableInSourceLabel = L10N.getStr(
+    "breakpointHeadingsMenuItem.enableInSource.label"
+  );
+  const disableInSourceLabel = L10N.getStr(
+    "breakpointHeadingsMenuItem.disableInSource.label"
+  );
+  const removeInSourceLabel = L10N.getStr(
+    "breakpointHeadingsMenuItem.removeInSource.label"
+  );
+  const enableInSourceKey = L10N.getStr(
+    "breakpointHeadingsMenuItem.enableInSource.accesskey"
+  );
+  const disableInSourceKey = L10N.getStr(
+    "breakpointHeadingsMenuItem.disableInSource.accesskey"
+  );
+  const removeInSourceKey = L10N.getStr(
+    "breakpointHeadingsMenuItem.removeInSource.accesskey"
+  );
+
+  const disableInSourceItem = {
+    id: "node-menu-disable-in-source",
+    label: disableInSourceLabel,
+    accesskey: disableInSourceKey,
+    disabled: false,
+    click: () => disableBreakpointsInSource(source)
+  };
+
+  const enableInSourceItem = {
+    id: "node-menu-enable-in-source",
+    label: enableInSourceLabel,
+    accesskey: enableInSourceKey,
+    disabled: false,
+    click: () => enableBreakpointsInSource(source)
+  };
+
+  const removeInSourceItem = {
+    id: "node-menu-enable-in-source",
+    label: removeInSourceLabel,
+    accesskey: removeInSourceKey,
+    disabled: false,
+    click: () => removeBreakpointsInSource(source)
+  };
+
+  const hideDisableInSourceItem = breakpointsForSource.every(
+    breakpoint => breakpoint.disabled
+  );
+  const hideEnableInSourceItem = breakpointsForSource.every(
+    breakpoint => !breakpoint.disabled
+  );
+
+  const items = [
+    { item: disableInSourceItem, hidden: () => hideDisableInSourceItem },
+    { item: enableInSourceItem, hidden: () => hideEnableInSourceItem },
+    { item: removeInSourceItem, hidden: () => false }
+  ];
+
+  showMenu(contextMenuEvent, buildMenu(items));
+}
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { Component } from "react";
 import classnames from "classnames";
-import { connect } from "react-redux";
+import { connect } from "../../../utils/connect";
 
 import ExceptionOption from "./ExceptionOption";
 
 import Breakpoint from "./Breakpoint";
 import BreakpointHeading from "./BreakpointHeading";
 
 import actions from "../../../actions";
 import { getDisplayPath } from "../../../utils/source";
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/moz.build
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/moz.build
@@ -5,12 +5,13 @@
 
 DIRS += [
 
 ]
 
 DebuggerModules(
     'Breakpoint.js',
     'BreakpointHeading.js',
+    'BreakpointHeadingsContextMenu.js',
     'BreakpointsContextMenu.js',
     'ExceptionOption.js',
     'index.js',
 )
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/CommandBar.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/CommandBar.js
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import PropTypes from "prop-types";
 import React, { Component } from "react";
 
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import classnames from "classnames";
 import { features } from "../../utils/prefs";
 import {
   isPaused as getIsPaused,
   getIsWaitingOnBreak,
   getCanRewind,
   getSkipPausing
 } from "../../selectors";
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Expressions.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Expressions.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 React, { Component } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import classnames from "classnames";
 import { features } from "../../utils/prefs";
 import { objectInspector } from "devtools-reps";
 
 import actions from "../../actions";
 import {
   getExpressions,
   getExpressionError,
@@ -159,16 +159,17 @@ class Expressions extends Component<Prop
     if (e.key === "Escape") {
       this.clear();
     }
   };
 
   hideInput = () => {
     this.setState({ focused: false });
     this.props.onExpressionAdded();
+    this.props.clearExpressionError();
   };
 
   onFocus = () => {
     this.setState({ focused: true });
   };
 
   onBlur() {
     this.clear();
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/index.js
@@ -1,33 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { Component } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../../utils/connect";
 import PropTypes from "prop-types";
 
 import type { Frame, Why } 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,
-  isPaused as getIsPaused,
   getCallStackFrames,
   getPauseReason
 } from "../../../selectors";
 
 import type { LocalFrame } from "./types";
 
 import "./Frames.css";
 
@@ -209,18 +208,17 @@ class Frames extends Component<Props, St
 }
 
 Frames.contextTypes = { l10n: PropTypes.object };
 
 const mapStateToProps = state => ({
   frames: getCallStackFrames(state),
   why: getPauseReason(state),
   frameworkGroupingOn: getFrameworkGroupingState(state),
-  selectedFrame: getSelectedFrame(state),
-  pause: getIsPaused(state)
+  selectedFrame: getSelectedFrame(state)
 });
 
 export default connect(
   mapStateToProps,
   {
     selectFrame: actions.selectFrame,
     toggleBlackBox: actions.toggleBlackBox,
     toggleFrameworkGrouping: actions.toggleFrameworkGrouping,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/FrameworkComponent.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/FrameworkComponent.js
@@ -1,39 +1,38 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
-// @flow
 import React, { PureComponent } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import actions from "../../actions";
 
 import { createObjectClient } from "../../client/firefox";
 import { getSelectedFrame, getAllPopupObjectProperties } from "../../selectors";
 
 import { objectInspector } from "devtools-reps";
 import { isReactComponent } from "../../utils/preview";
 
-import type { Frame, Grip } from "../../types";
+import type { Frame } from "../../types";
 
 const {
   component: ObjectInspector,
   utils: {
     createNode,
     getChildren,
     loadProperties: { loadItemProperties }
   }
 } = objectInspector;
 
 type Props = {
   selectedFrame: Frame,
   popupObjectProperties: Object,
   setPopupObjectProperties: typeof actions.setPopupObjectProperties,
-  openElementInInspector: (grip: Grip) => void
+  openElementInInspector: typeof actions.setPopupObjectProperties
 };
 
 class FrameworkComponent extends PureComponent<Props> {
   async componentWillMount() {
     const expression = "this;";
     const { selectedFrame, setPopupObjectProperties } = this.props;
     const value = selectedFrame.this;
 
@@ -94,18 +93,18 @@ class FrameworkComponent extends PureCom
     }
 
     return null;
   }
 }
 
 const mapStateToProps = state => ({
   selectedFrame: getSelectedFrame(state),
-  popupObjectProperties: getAllPopupObjectProperties(state),
-  openElementInInspector: actions.openElementInInspectorCommand
+  popupObjectProperties: getAllPopupObjectProperties(state)
 });
 
 export default connect(
   mapStateToProps,
   {
-    setPopupObjectProperties: actions.setPopupObjectProperties
+    setPopupObjectProperties: actions.setPopupObjectProperties,
+    openElementInInspector: actions.openElementInInspectorCommand
   }
 )(FrameworkComponent);
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Scopes.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Scopes.js
@@ -1,38 +1,39 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 import React, { PureComponent } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import actions from "../../actions";
 import { createObjectClient } from "../../client/firefox";
 
 import {
   getSelectedSource,
   getSelectedFrame,
   getGeneratedFrameScope,
   getOriginalFrameScope,
   isPaused as getIsPaused,
   getPauseReason
 } from "../../selectors";
 import { getScopes } from "../../utils/pause/scopes";
 
 import { objectInspector } from "devtools-reps";
-import type { Pause, Why } from "../../types";
+
+import type { Why } from "../../types";
 import type { NamedValue } from "../../utils/pause/scopes/types";
 
 import "./Scopes.css";
 
 const { ObjectInspector } = objectInspector;
 
 type Props = {
-  isPaused: Pause,
+  isPaused: boolean,
   selectedFrame: Object,
   generatedFrameScopes: Object,
   originalFrameScopes: Object | null,
   isLoading: boolean,
   why: Why,
   openLink: typeof actions.openLink,
   openElementInInspector: typeof actions.openElementInInspectorCommand
 };
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Workers.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Workers.js
@@ -1,31 +1,60 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
-import React, { PureComponent } from "react";
-import { connect } from "react-redux";
+import React, { Component } from "react";
+import { connect } from "../../utils/connect";
 import type { List } from "immutable";
 
 import "./Workers.css";
 
 import actions from "../../actions";
-import { getWorkers } from "../../selectors";
+import {
+  getMainThread,
+  getCurrentThread,
+  threadIsPaused,
+  getWorkers
+} from "../../selectors";
 import { basename } from "../../utils/path";
+import { features } from "../../utils/prefs";
 import type { Worker } from "../../types";
 import AccessibleImage from "../shared/AccessibleImage";
+import classnames from "classnames";
 
-export class Workers extends PureComponent {
+type Props = {
+  selectThread: string => void
+};
+
+export class Workers extends Component<Props> {
   props: {
     workers: List<Worker>,
-    openWorkerToolbox: object => void
+    openWorkerToolbox: object => void,
+    mainThread: string,
+    currentThread: string
   };
 
-  renderWorkers(workers) {
+  renderWorkers(workers, mainThread, currentThread) {
+    if (features.windowlessWorkers) {
+      return [{ actor: mainThread }, ...workers].map(worker => (
+        <div
+          className={classnames(
+            "worker",
+            worker.actor == currentThread && "selected"
+          )}
+          key={worker.actor}
+          onClick={() => this.props.selectThread(worker.actor)}
+        >
+          <img className="domain" />
+          {(worker.url ? basename(worker.url) : "Main Thread") +
+            (this.props.threadIsPaused(worker.actor) ? " PAUSED" : "")}
+        </div>
+      ));
+    }
     const { openWorkerToolbox } = this.props;
     return workers.map(worker => (
       <div
         className="worker"
         key={worker.actor}
         onClick={() => openWorkerToolbox(worker)}
       >
         <AccessibleImage className="domain" />
@@ -34,27 +63,33 @@ export class Workers extends PureCompone
     ));
   }
 
   renderNoWorkersPlaceholder() {
     return <div className="pane-info">{L10N.getStr("noWorkersText")}</div>;
   }
 
   render() {
-    const { workers } = this.props;
+    const { workers, mainThread, currentThread } = this.props;
     return (
       <div className="pane workers-list">
         {workers && workers.size > 0
-          ? this.renderWorkers(workers)
+          ? this.renderWorkers(workers, mainThread, currentThread)
           : this.renderNoWorkersPlaceholder()}
       </div>
     );
   }
 }
 
 const mapStateToProps = state => ({
-  workers: getWorkers(state)
+  workers: getWorkers(state),
+  mainThread: getMainThread(state),
+  currentThread: getCurrentThread(state),
+  threadIsPaused: thread => threadIsPaused(state, thread)
 });
 
 export default connect(
   mapStateToProps,
-  actions
+  {
+    openWorkerToolbox: actions.openWorkerToolbox,
+    selectThread: actions.selectThread
+  }
 )(Workers);
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/XHRBreakpoints.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/XHRBreakpoints.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { Component } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import classnames from "classnames";
 import actions from "../../actions";
 
 import { CloseButton } from "../shared/Button";
 
 import "./XHRBreakpoints.css";
 import { getXHRBreakpoints, shouldPauseOnAnyXHR } from "../../selectors";
 import ExceptionOption from "./Breakpoints/ExceptionOption";
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/index.js
@@ -1,44 +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 React, { Component } from "react";
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 import { List } from "immutable";
 
 import actions from "../../actions";
 import {
   getTopFrame,
   getBreakpointsList,
   getBreakpointsDisabled,
   getBreakpointsLoading,
   getExpressions,
   getIsWaitingOnBreak,
   getShouldPauseOnExceptions,
   getShouldPauseOnCaughtExceptions,
-  getWorkers,
-  getExtra
+  getWorkers
 } from "../../selectors";
 
 import Svg from "../shared/Svg";
 import { prefs, features } from "../../utils/prefs";
 
 import Breakpoints from "./Breakpoints";
 import Expressions from "./Expressions";
 import SplitBox from "devtools-splitter";
 import Frames from "./Frames";
 import Workers from "./Workers";
 import Accordion from "../shared/Accordion";
 import CommandBar from "./CommandBar";
 import UtilsBar from "./UtilsBar";
-import FrameworkComponent from "./FrameworkComponent";
 import XHRBreakpoints from "./XHRBreakpoints";
 
 import Scopes from "./Scopes";
 
 import "./SecondaryPanes.css";
 
 import type { Expression } from "../../types";
 import type { WorkersList } from "../../reducers/types";
@@ -67,17 +65,16 @@ function debugBtn(onClick, type, classNa
 
 type State = {
   showExpressionsInput: boolean,
   showXHRInput: boolean
 };
 
 type Props = {
   expressions: List<Expression>,
-  extra: Object,
   hasFrames: boolean,
   horizontal: boolean,
   breakpoints: Object,
   breakpointsDisabled: boolean,
   breakpointsLoading: boolean,
   isWaitingOnBreak: boolean,
   shouldPauseOnExceptions: boolean,
   shouldPauseOnCaughtExceptions: boolean,
@@ -211,32 +208,16 @@ class SecondaryPanes extends Component<P
       component: <Scopes />,
       opened: prefs.scopesVisible,
       onToggle: opened => {
         prefs.scopesVisible = opened;
       }
     };
   }
 
-  getComponentItem() {
-    const {
-      extra: { react }
-    } = this.props;
-
-    return {
-      header: react.displayName,
-      className: "component-pane",
-      component: <FrameworkComponent />,
-      opened: prefs.componentVisible,
-      onToggle: opened => {
-        prefs.componentVisible = opened;
-      }
-    };
-  }
-
   getWatchItem(): AccordionPaneItem {
     return {
       header: L10N.getStr("watchExpressions.header"),
       className: "watch-expressions-pane",
       buttons: this.watchExpressionHeaderButtons(),
       component: (
         <Expressions
           showInput={this.state.showExpressionsInput}
@@ -313,71 +294,63 @@ class SecondaryPanes extends Component<P
       opened: prefs.breakpointsVisible,
       onToggle: opened => {
         prefs.breakpointsVisible = opened;
       }
     };
   }
 
   getStartItems() {
-    const { extra, workers } = this.props;
+    const { workers } = this.props;
 
     const items: Array<AccordionPaneItem> = [];
     if (this.props.horizontal) {
       if (features.workers && workers.size > 0) {
         items.push(this.getWorkersItem());
       }
 
       items.push(this.getWatchItem());
     }
 
     items.push(this.getBreakpointsItem());
 
     if (this.props.hasFrames) {
       items.push(this.getCallStackItem());
 
       if (this.props.horizontal) {
-        if (features.componentPane && extra && extra.react) {
-          items.push(this.getComponentItem());
-        }
-
         items.push(this.getScopeItem());
       }
     }
 
     if (features.xhrBreakpoints) {
       items.push(this.getXHRItem());
     }
 
     return items.filter(item => item);
   }
 
   renderHorizontalLayout() {
     return <Accordion items={this.getItems()} />;
   }
 
   getEndItems() {
-    const { extra, workers } = this.props;
+    const { workers } = this.props;
 
     let items: Array<AccordionPaneItem> = [];
 
     if (this.props.horizontal) {
       return [];
     }
 
     if (features.workers && workers.size > 0) {
       items.push(this.getWorkersItem());
     }
 
     items.push(this.getWatchItem());
 
-    if (features.componentPane && extra && extra.react) {
-      items.push(this.getComponentItem());
-    }
-
     if (this.props.hasFrames) {
       items = [...items, this.getScopeItem()];
     }
 
     return items;
   }
 
   getItems() {
@@ -422,17 +395,16 @@ class SecondaryPanes extends Component<P
         {this.renderUtilsBar()}
       </div>
     );
   }
 }
 
 const mapStateToProps = state => ({
   expressions: getExpressions(state),
-  extra: getExtra(state),
   hasFrames: !!getTopFrame(state),
   breakpoints: getBreakpointsList(state),
   breakpointsDisabled: getBreakpointsDisabled(state),
   breakpointsLoading: getBreakpointsLoading(state),
   isWaitingOnBreak: getIsWaitingOnBreak(state),
   shouldPauseOnExceptions: getShouldPauseOnExceptions(state),
   shouldPauseOnCaughtExceptions: getShouldPauseOnCaughtExceptions(state),
   workers: getWorkers(state)
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/moz.build
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/moz.build
@@ -6,15 +6,14 @@
 DIRS += [
     'Breakpoints',
     'Frames',
 ]
 
 DebuggerModules(
     'CommandBar.js',
     'Expressions.js',
-    'FrameworkComponent.js',
     'index.js',
     'Scopes.js',
     'UtilsBar.js',
     'Workers.js',
     'XHRBreakpoints.js',
 )
--- a/devtools/client/debugger/new/src/components/WelcomeBox.js
+++ b/devtools/client/debugger/new/src/components/WelcomeBox.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 import React, { Component } from "react";
 
-import { connect } from "react-redux";
+import { connect } from "../utils/connect";
 
 import actions from "../actions";
 import { getPaneCollapse } from "../selectors";
 import { formatKeyShortcut } from "../utils/text";
 
 import { PaneToggleButton } from "./shared/Button";
 import type { ActiveSearchType } from "../reducers/ui";
 
--- a/devtools/client/debugger/new/src/components/shared/SourceIcon.js
+++ b/devtools/client/debugger/new/src/components/shared/SourceIcon.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { PureComponent } from "react";
 
-import { connect } from "react-redux";
+import { connect } from "../../utils/connect";
 
 import AccessibleImage from "./AccessibleImage";
 
 import { getSourceClassnames } from "../../utils/source";
 import { getFramework } from "../../utils/tabs";
 import { getSourceMetaData, getTabs } from "../../selectors";
 
 import type { Source } from "../../types";
--- a/devtools/client/debugger/new/src/reducers/ast.js
+++ b/devtools/client/debugger/new/src/reducers/ast.js
@@ -8,76 +8,79 @@
  * Ast reducer
  * @module reducers/ast
  */
 
 import * as I from "immutable";
 import makeRecord from "../utils/makeRecord";
 import { findEmptyLines } from "../utils/ast";
 
-import type {
-  AstLocation,
-  SymbolDeclarations,
-  PausePoints,
-  PausePoint
-} from "../workers/parser";
+import type { AstLocation, SymbolDeclarations } from "../workers/parser";
 
 import type { Map } from "immutable";
-import type { SourceLocation, Source } from "../types";
+import type { SourceLocation, Source, Position } from "../types";
 import type { Action, DonePromiseAction } from "../actions/types";
 import type { Record } from "../utils/makeRecord";
 
 type EmptyLinesType = number[];
 
 export type Symbols = SymbolDeclarations | {| loading: true |};
 export type SymbolsMap = Map<string, Symbols>;
 export type EmptyLinesMap = Map<string, EmptyLinesType>;
 
 export type SourceMetaDataType = {
   framework: ?string
 };
 
 export type SourceMetaDataMap = Map<string, SourceMetaDataType>;
-export type PausePointsMap = Map<string, PausePoints>;
+
+export type PausePoint = {
+  location: Position,
+  generatedLocation: SourceLocation,
+  types: { break: boolean, step: boolean }
+};
+
+export type PausePointsMap = {
+  [line: string]: { [column: string]: PausePoint }
+};
+export type PausePoints = PausePoint[];
+export type PausePointsState = Map<string, PausePoint[]>;
 
 export type Preview =
   | {| updating: true |}
   | null
   | {|
       updating: false,
       expression: string,
       location: AstLocation,
       cursorPos: any,
       tokenPos: AstLocation,
-      result: Object,
-      extra: Object
+      result: Object
     |};
 
 export type ASTState = {
   symbols: SymbolsMap,
   emptyLines: EmptyLinesMap,
   outOfScopeLocations: ?Array<AstLocation>,
   inScopeLines: ?Array<Number>,
   preview: Preview,
-  pausePoints: PausePointsMap,
+  pausePoints: PausePointsState,
   sourceMetaData: SourceMetaDataMap
 };
 
-export function initialASTState() {
-  return makeRecord(
-    ({
-      symbols: I.Map(),
-      emptyLines: I.Map(),
-      outOfScopeLocations: null,
-      inScopeLines: null,
-      preview: null,
-      pausePoints: I.Map(),
-      sourceMetaData: I.Map()
-    }: ASTState)
-  )();
+export function initialASTState(): Record<ASTState> {
+  return makeRecord({
+    symbols: I.Map(),
+    emptyLines: I.Map(),
+    outOfScopeLocations: null,
+    inScopeLines: null,
+    preview: null,
+    pausePoints: I.Map(),
+    sourceMetaData: I.Map()
+  })();
 }
 
 function update(
   state: Record<ASTState> = initialASTState(),
   action: Action
 ): Record<ASTState> {
   switch (action.type) {
     case "SET_SYMBOLS": {
@@ -216,22 +219,45 @@ export function getPausePoint(
   }
 
   const { column, line, sourceId } = location;
   const pausePoints = getPausePoints(state, sourceId);
   if (!pausePoints) {
     return;
   }
 
-  const linePoints = pausePoints[String(line)];
-  if (linePoints && column) {
-    return linePoints[String(column)];
+  for (const point of pausePoints) {
+    const { location: pointLocation } = point;
+    if (pointLocation.line == line && pointLocation.column == column) {
+      return point;
+    }
   }
 }
 
+export function getFirstPausePointLocation(
+  state: OuterState,
+  location: SourceLocation
+): SourceLocation {
+  const { sourceId } = location;
+  const pausePoints = getPausePoints(state, location.sourceId);
+  if (!pausePoints) {
+    return location;
+  }
+
+  const pausesAtLine = pausePoints[location.line];
+  if (pausesAtLine) {
+    const values: PausePoint[] = (Object.values(pausesAtLine): any);
+    const firstPausePoint = values.find(pausePoint => pausePoint.types.break);
+    if (firstPausePoint) {
+      return { ...firstPausePoint.location, sourceId };
+    }
+  }
+  return location;
+}
+
 export function hasPausePoints(state: OuterState, sourceId: string): boolean {
   const pausePoints = getPausePoints(state, sourceId);
   return !!pausePoints;
 }
 
 export function getOutOfScopeLocations(state: OuterState) {
   return state.ast.get("outOfScopeLocations");
 }
--- a/devtools/client/debugger/new/src/reducers/debuggee.js
+++ b/devtools/client/debugger/new/src/reducers/debuggee.js
@@ -16,21 +16,19 @@ import type { Action } from "../actions/
 import makeRecord from "../utils/makeRecord";
 
 export type WorkersList = List<Worker>;
 
 type DebuggeeState = {
   workers: WorkersList
 };
 
-export const createDebuggeeState = makeRecord(
-  ({
-    workers: List()
-  }: DebuggeeState)
-);
+export const createDebuggeeState: () => Record<DebuggeeState> = makeRecord({
+  workers: List()
+});
 
 export default function debuggee(
   state: Record<DebuggeeState> = createDebuggeeState(),
   action: Action
 ): Record<DebuggeeState> {
   switch (action.type) {
     case "SET_WORKERS":
       return state.set("workers", List(action.workers));
--- a/devtools/client/debugger/new/src/reducers/expressions.js
+++ b/devtools/client/debugger/new/src/reducers/expressions.js
@@ -8,38 +8,36 @@
  * Expressions reducer
  * @module reducers/expressions
  */
 
 import makeRecord from "../utils/makeRecord";
 import { List, Map } from "immutable";
 import { omit, zip } from "lodash";
 
-import { createSelector } from "reselect";
+import { createSelector } from "../utils/createSelector";
 import { prefs } from "../utils/prefs";
 
 import type { Expression } from "../types";
 import type { Action } from "../actions/types";
 import type { Record } from "../utils/makeRecord";
 
 export type ExpressionState = {
   expressions: List<Expression>,
   expressionError: boolean,
   autocompleteMatches: Map<string, List<string>>,
   currentAutocompleteInput: string | null
 };
 
-export const createExpressionState = makeRecord(
-  ({
-    expressions: List(restoreExpressions()),
-    expressionError: false,
-    autocompleteMatches: Map({}),
-    currentAutocompleteInput: null
-  }: ExpressionState)
-);
+export const createExpressionState: () => Record<ExpressionState> = makeRecord({
+  expressions: List(restoreExpressions()),
+  expressionError: false,
+  autocompleteMatches: Map({}),
+  currentAutocompleteInput: null
+});
 
 function update(
   state: Record<ExpressionState> = createExpressionState(),
   action: Action
 ): Record<ExpressionState> {
   switch (action.type) {
     case "ADD_EXPRESSION":
       if (action.expressionError) {
--- a/devtools/client/debugger/new/src/reducers/file-search.js
+++ b/devtools/client/debugger/new/src/reducers/file-search.js
@@ -41,27 +41,25 @@ export type FileSearchState = {
 
 const emptySearchResults = Object.freeze({
   matches: Object.freeze([]),
   matchIndex: -1,
   index: -1,
   count: 0
 });
 
-export const createFileSearchState = makeRecord(
-  ({
-    query: "",
-    searchResults: emptySearchResults,
-    modifiers: makeRecord({
-      caseSensitive: prefs.fileSearchCaseSensitive,
-      wholeWord: prefs.fileSearchWholeWord,
-      regexMatch: prefs.fileSearchRegexMatch
-    })()
-  }: FileSearchState)
-);
+export const createFileSearchState: () => Record<FileSearchState> = makeRecord({
+  query: "",
+  searchResults: emptySearchResults,
+  modifiers: makeRecord({
+    caseSensitive: prefs.fileSearchCaseSensitive,
+    wholeWord: prefs.fileSearchWholeWord,
+    regexMatch: prefs.fileSearchRegexMatch
+  })()
+});
 
 function update(
   state: Record<FileSearchState> = createFileSearchState(),
   action: Action
 ): Record<FileSearchState> {
   switch (action.type) {
     case "UPDATE_FILE_SEARCH_QUERY": {
       return state.set("query", action.query);
--- a/devtools/client/debugger/new/src/reducers/pause.js
+++ b/devtools/client/debugger/new/src/reducers/pause.js
@@ -5,17 +5,17 @@
 // @flow
 /* eslint complexity: ["error", 30]*/
 
 /**
  * Pause reducer
  * @module reducers/pause
  */
 
-import { createSelector } from "reselect";
+import { createSelector } from "../utils/createSelector";
 import { isGeneratedId } from "devtools-source-map";
 import { prefs } from "../utils/prefs";
 import { getSelectedSource } from "./sources";
 
 import type { OriginalScope } from "../utils/pause/mapScopes";
 import type { Action } from "../actions/types";
 import type { State } from "./types";
 import type { Why, Scope, SourceId, FrameId, MappedLocation } from "../types";
@@ -27,18 +27,18 @@ export type Command =
   | "stepOut"
   | "resume"
   | "rewind"
   | "reverseStepOver"
   | "reverseStepIn"
   | "reverseStepOut"
   | "expression";
 
-export type PauseState = {
-  extra: ?Object,
+// Pause state associated with an individual thread.
+type ThreadPauseState = {
   why: ?Why,
   isWaitingOnBreak: boolean,
   frames: ?(any[]),
   frameScopes: {
     generated: {
       [FrameId]: {
         pending: boolean,
         scope: Scope
@@ -55,206 +55,255 @@ export type PauseState = {
         [string]: string | null
       }
     }
   },
   selectedFrameId: ?string,
   loadedObjects: Object,
   shouldPauseOnExceptions: boolean,
   shouldPauseOnCaughtExceptions: boolean,
-  canRewind: boolean,
-  debuggeeUrl: string,
   command: Command,
   previousLocation: ?MappedLocation,
   skipPausing: boolean
 };
 
+// Pause state describing all threads.
+export type PauseState = {
+  mainThread: string,
+  currentThread: string,
+  debuggeeUrl: string,
+  canRewind: boolean,
+  threads: { [string]: ThreadPauseState }
+};
+
 export const createPauseState = (): PauseState => ({
-  ...emptyPauseState,
-  extra: {},
-  isWaitingOnBreak: false,
-  shouldPauseOnExceptions: prefs.pauseOnExceptions,
-  shouldPauseOnCaughtExceptions: prefs.pauseOnCaughtExceptions,
+  mainThread: "UnknownThread",
+  currentThread: "UnknownThread",
+  threads: {},
   canRewind: false,
-  debuggeeUrl: "",
-  command: null,
-  previousLocation: null,
-  skipPausing: prefs.skipPausing
+  debuggeeUrl: ""
 });
 
-const emptyPauseState = {
+const resumedPauseState = {
   frames: null,
   frameScopes: {
     generated: {},
     original: {},
     mappings: {}
   },
   selectedFrameId: null,
   loadedObjects: {},
   why: null
 };
 
+const createInitialPauseState = () => ({
+  ...resumedPauseState,
+  isWaitingOnBreak: false,
+  shouldPauseOnExceptions: prefs.pauseOnExceptions,
+  shouldPauseOnCaughtExceptions: prefs.pauseOnCaughtExceptions,
+  canRewind: false,
+  debuggeeUrl: "",
+  command: null,
+  previousLocation: null,
+  skipPausing: prefs.skipPausing
+});
+
+function getThreadPauseState(state: PauseState, thread: string) {
+  // Thread state is lazily initialized so that we don't have to keep track of
+  // the current set of worker threads.
+  return state.threads[thread] || createInitialPauseState();
+}
+
 function update(
   state: PauseState = createPauseState(),
   action: Action
 ): PauseState {
+  // Actions need to specify any thread they are operating on. These helpers
+  // manage updating the pause state for that thread.
+  const threadState = () => {
+    if (!action.thread) {
+      throw new Error(`Missing thread in action ${action.type}`);
+    }
+    return getThreadPauseState(state, action.thread);
+  };
+
+  const updateThreadState = newThreadState => {
+    if (!action.thread) {
+      throw new Error(`Missing thread in action ${action.type}`);
+    }
+    return {
+      ...state,
+      threads: {
+        ...state.threads,
+        [action.thread]: { ...threadState(), ...newThreadState }
+      }
+    };
+  };
+
   switch (action.type) {
+    case "SELECT_THREAD":
+      return { ...state, currentThread: action.thread };
+
     case "PAUSED": {
-      const { selectedFrameId, frames, loadedObjects, why } = action;
+      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;
       });
 
-      return {
-        ...state,
+      state = { ...state, currentThread: thread };
+      return updateThreadState({
         isWaitingOnBreak: false,
         selectedFrameId,
         frames,
-        frameScopes: { ...emptyPauseState.frameScopes },
+        frameScopes: { ...resumedPauseState.frameScopes },
         loadedObjects: objectMap,
         why
-      };
+      });
     }
 
     case "MAP_FRAMES": {
       const { selectedFrameId, frames } = action;
-      return { ...state, frames, selectedFrameId };
-    }
-
-    case "ADD_EXTRA": {
-      return { ...state, extra: action.extra };
+      return updateThreadState({ frames, selectedFrameId });
     }
 
     case "ADD_SCOPES": {
       const { frame, status, value } = action;
       const selectedFrameId = frame.id;
 
       const generated = {
-        ...state.frameScopes.generated,
+        ...threadState().frameScopes.generated,
         [selectedFrameId]: {
           pending: status !== "done",
           scope: value
         }
       };
-      return {
-        ...state,
+
+      return updateThreadState({
         frameScopes: {
-          ...state.frameScopes,
+          ...threadState().frameScopes,
           generated
         }
-      };
+      });
     }
 
     case "TRAVEL_TO":
-      return {
-        ...state,
-        ...action.data.paused
-      };
+      return updateThreadState({ ...action.data.paused });
 
     case "MAP_SCOPES": {
       const { frame, status, value } = action;
       const selectedFrameId = frame.id;
 
       const original = {
-        ...state.frameScopes.original,
+        ...threadState().frameScopes.original,
         [selectedFrameId]: {
           pending: status !== "done",
           scope: value && value.scope
         }
       };
 
       const mappings = {
-        ...state.frameScopes.mappings,
+        ...threadState().frameScopes.mappings,
         [selectedFrameId]: value && value.mappings
       };
-      return {
-        ...state,
+
+      return updateThreadState({
         frameScopes: {
-          ...state.frameScopes,
+          ...threadState().frameScopes,
           original,
           mappings
         }
-      };
+      });
     }
 
     case "BREAK_ON_NEXT":
-      return { ...state, isWaitingOnBreak: true };
+      return updateThreadState({ isWaitingOnBreak: true });
 
     case "SELECT_FRAME":
-      return {
-        ...state,
-        selectedFrameId: action.frame.id
-      };
+      return updateThreadState({ selectedFrameId: action.frame.id });
 
-    case "SET_POPUP_OBJECT_PROPERTIES":
+    case "SET_POPUP_OBJECT_PROPERTIES": {
       if (!action.properties) {
-        return { ...state };
+        return state;
       }
 
-      return {
-        ...state,
+      return updateThreadState({
         loadedObjects: {
-          ...state.loadedObjects,
+          ...threadState().loadedObjects,
           [action.objectId]: action.properties
         }
-      };
+      });
+    }
 
     case "CONNECT":
       return {
         ...createPauseState(),
+        mainThread: action.thread,
+        currentThread: action.thread,
         debuggeeUrl: action.url,
         canRewind: action.canRewind
       };
 
-    case "PAUSE_ON_EXCEPTIONS":
+    case "PAUSE_ON_EXCEPTIONS": {
       const { shouldPauseOnExceptions, shouldPauseOnCaughtExceptions } = action;
 
       prefs.pauseOnExceptions = shouldPauseOnExceptions;
       prefs.pauseOnCaughtExceptions = shouldPauseOnCaughtExceptions;
 
       // Preserving for the old debugger
       prefs.ignoreCaughtExceptions = !shouldPauseOnCaughtExceptions;
 
-      return {
-        ...state,
+      return updateThreadState({
         shouldPauseOnExceptions,
         shouldPauseOnCaughtExceptions
-      };
-
-    case "COMMAND": {
-      return action.status === "start"
-        ? {
-            ...state,
-            ...emptyPauseState,
-            command: action.command,
-            previousLocation: getPauseLocation(state, action)
-          }
-        : { ...state, command: null };
+      });
     }
 
+    case "COMMAND":
+      if (action.status === "start") {
+        return updateThreadState({
+          ...resumedPauseState,
+          command: action.command,
+          previousLocation: getPauseLocation(threadState(), action)
+        });
+      }
+      return updateThreadState({ command: null });
+
     case "RESUME":
-      return { ...state, ...emptyPauseState };
+      // Workaround for threads resuming before the initial connection.
+      if (!action.thread && !state.currentThread) {
+        return state;
+      }
+      return updateThreadState(resumedPauseState);
 
     case "EVALUATE_EXPRESSION":
+      return updateThreadState({
+        command: action.status === "start" ? "expression" : null
+      });
+
+    case "NAVIGATE":
       return {
         ...state,
-        command: action.status === "start" ? "expression" : null
+        currentThread: state.mainThread,
+        threads: {
+          [state.mainThread]: {
+            ...state.threads[state.mainThread],
+            ...resumedPauseState
+          }
+        },
+        debuggeeUrl: action.url
       };
 
-    case "NAVIGATE":
-      return { ...state, ...emptyPauseState, debuggeeUrl: action.url };
-
     case "TOGGLE_SKIP_PAUSING": {
       const { skipPausing } = action;
       prefs.skipPausing = skipPausing;
 
-      return { ...state, skipPausing };
+      return updateThreadState({ skipPausing });
     }
   }
 
   return state;
 }
 
 function getPauseLocation(state, action) {
   const { frames, previousLocation } = state;
@@ -282,77 +331,87 @@ 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;
 
-const getPauseState = state => state.pause;
+function getCurrentPauseState(state: OuterState): ThreadPauseState {
+  return getThreadPauseState(state.pause, state.pause.currentThread);
+}
 
 export const getAllPopupObjectProperties = createSelector(
-  getPauseState,
+  getCurrentPauseState,
   pauseWrapper => pauseWrapper.loadedObjects
 );
 
 export function getPauseReason(state: OuterState): ?Why {
-  return state.pause.why;
+  return getCurrentPauseState(state).why;
 }
 
 export function getPauseCommand(state: OuterState): Command {
-  return state.pause && state.pause.command;
+  return getCurrentPauseState(state).command;
 }
 
 export function isStepping(state: OuterState) {
   return ["stepIn", "stepOver", "stepOut"].includes(getPauseCommand(state));
 }
 
+export function getMainThread(state: OuterState) {
+  return state.pause.mainThread;
+}
+
+export function getCurrentThread(state: OuterState) {
+  return state.pause.currentThread;
+}
+
+export function threadIsPaused(state: OuterState, thread: string) {
+  return !!getThreadPauseState(state.pause, thread).frames;
+}
+
 export function isPaused(state: OuterState) {
   return !!getFrames(state);
 }
 
 export function getIsPaused(state: OuterState) {
   return !!getFrames(state);
 }
 
 export function getPreviousPauseFrameLocation(state: OuterState) {
-  return state.pause.previousLocation;
+  return getCurrentPauseState(state).previousLocation;
 }
 
 export function isEvaluatingExpression(state: OuterState) {
-  return state.pause.command === "expression";
+  return getCurrentPauseState(state).command === "expression";
 }
 
 export function getPopupObjectProperties(state: OuterState, objectId: string) {
   return getAllPopupObjectProperties(state)[objectId];
 }
 
 export function getIsWaitingOnBreak(state: OuterState) {
-  return state.pause.isWaitingOnBreak;
+  return getCurrentPauseState(state).isWaitingOnBreak;
 }
 
 export function getShouldPauseOnExceptions(state: OuterState) {
-  return state.pause.shouldPauseOnExceptions;
+  return getCurrentPauseState(state).shouldPauseOnExceptions;
 }
 
 export function getShouldPauseOnCaughtExceptions(state: OuterState) {
-  return state.pause.shouldPauseOnCaughtExceptions;
+  return getCurrentPauseState(state).shouldPauseOnCaughtExceptions;
 }
 
 export function getCanRewind(state: OuterState) {
   return state.pause.canRewind;
 }
 
-export function getExtra(state: OuterState) {
-  return state.pause.extra;
-}
-
 export function getFrames(state: OuterState) {
-  return state.pause.frames;
+  return getCurrentPauseState(state).frames;
 }
 
 function getGeneratedFrameId(frameId: string): string {
   if (frameId.includes("-originalFrame")) {
     // The mapFrames can add original stack frames -- get generated frameId.
     return frameId.substr(0, frameId.lastIndexOf("-originalFrame"));
   }
   return frameId;
@@ -384,17 +443,17 @@ export function getOriginalFrameScope(
   if (!isGenerated && original && (original.pending || original.scope)) {
     return original;
   }
 
   return null;
 }
 
 export function getFrameScopes(state: OuterState) {
-  return state.pause.frameScopes;
+  return getCurrentPauseState(state).frameScopes;
 }
 
 export function getSelectedFrameBindings(state: OuterState) {
   const scopes = getFrameScopes(state);
   const selectedFrameId = getSelectedFrameId(state);
   if (!scopes || !selectedFrameId) {
     return null;
   }
@@ -462,17 +521,17 @@ export function getSelectedScopeMappings
   if (!frameId) {
     return null;
   }
 
   return getFrameScopes(state).mappings[frameId];
 }
 
 export function getSelectedFrameId(state: OuterState) {
-  return state.pause.selectedFrameId;
+  return getCurrentPauseState(state).selectedFrameId;
 }
 
 export function getTopFrame(state: OuterState) {
   const frames = getFrames(state);
   return frames && frames[0];
 }
 
 export const getSelectedFrame = createSelector(
@@ -487,17 +546,17 @@ export const getSelectedFrame = createSe
   }
 );
 
 export function getDebuggeeUrl(state: OuterState) {
   return state.pause.debuggeeUrl;
 }
 
 export function getSkipPausing(state: OuterState) {
-  return state.pause.skipPausing;
+  return getCurrentPauseState(state).skipPausing;
 }
 
 // NOTE: currently only used for chrome
 export function getChromeScopes(state: OuterState) {
   const frame = getSelectedFrame(state);
   return frame ? frame.scopeChain : undefined;
 }
 
--- a/devtools/client/debugger/new/src/reducers/project-text-search.js
+++ b/devtools/client/debugger/new/src/reducers/project-text-search.js
@@ -5,102 +5,95 @@
 // @flow
 // @format
 
 /**
  * Project text search reducer
  * @module reducers/project-text-search
  */
 
-import * as I from "immutable";
-import makeRecord from "../utils/makeRecord";
-
 import type { Action } from "../actions/types";
-import type { Record } from "../utils/makeRecord";
-import type { List } from "immutable";
 
 export type Search = {
-  id: string,
-  filepath: string,
-  matches: I.List<any>
+  +sourceId: string,
+  +filepath: string,
+  +matches: any[]
 };
 export type StatusType = "INITIAL" | "FETCHING" | "DONE" | "ERROR";
 export const statusType = {
   initial: "INITIAL",
   fetching: "FETCHING",
   done: "DONE",
   error: "ERROR"
 };
 
-export type ResultRecord = Record<Search>;
-export type ResultList = List<ResultRecord>;
+export type ResultList = Search[];
 export type ProjectTextSearchState = {
-  query: string,
-  results: ResultList,
-  status: string
+  +query: string,
+  +results: ResultList,
+  +status: string
 };
 
-export function initialProjectTextSearchState(): Record<
-  ProjectTextSearchState
-> {
-  return makeRecord(
-    ({
-      query: "",
-      results: I.List(),
-      status: statusType.initial
-    }: ProjectTextSearchState)
-  )();
+export function initialProjectTextSearchState(): ProjectTextSearchState {
+  return {
+    query: "",
+    results: [],
+    status: statusType.initial
+  };
 }
 
 function update(
-  state: Record<ProjectTextSearchState> = initialProjectTextSearchState(),
+  state: ProjectTextSearchState = initialProjectTextSearchState(),
   action: Action
-): Record<ProjectTextSearchState> {
+): ProjectTextSearchState {
   switch (action.type) {
     case "ADD_QUERY":
-      const actionCopy = action;
-      return state.update("query", value => actionCopy.query);
+      return { ...state, query: action.query };
 
     case "CLEAR_QUERY":
-      return state.merge({
+      return {
+        ...state,
         query: "",
         status: statusType.initial
-      });
+      };
 
     case "ADD_SEARCH_RESULT":
-      const results = state.get("results");
-      return state.merge({ results: results.push(action.result) });
+      const results = state.results;
+      if (action.result.matches.length === 0) {
+        return state;
+      }
+
+      const result = {
+        type: "RESULT",
+        ...action.result,
+        matches: action.result.matches.map(m => ({ type: "MATCH", ...m }))
+      };
+      return { ...state, results: [...results, result] };
 
     case "UPDATE_STATUS":
-      return state.merge({ status: action.status });
+      return { ...state, status: action.status };
 
     case "CLEAR_SEARCH_RESULTS":
-      return state.merge({
-        results: state.get("results").clear()
-      });
+      return { ...state, results: [] };
 
     case "CLEAR_SEARCH":
     case "CLOSE_PROJECT_SEARCH":
     case "NAVIGATE":
-      return state.merge({
-        query: "",
-        results: state.get("results").clear(),
-        status: statusType.initial
-      });
+      return initialProjectTextSearchState();
   }
   return state;
 }
 
-type OuterState = { projectTextSearch: Record<ProjectTextSearchState> };
+type OuterState = { projectTextSearch: ProjectTextSearchState };
 
 export function getTextSearchResults(state: OuterState) {
-  return state.projectTextSearch.get("results");
+  return state.projectTextSearch.results;
 }
 
 export function getTextSearchStatus(state: OuterState) {
-  return state.projectTextSearch.get("status");
+  return state.projectTextSearch.status;
 }
 
 export function getTextSearchQuery(state: OuterState) {
-  return state.projectTextSearch.get("query");
+  return state.projectTextSearch.query;
 }
 
 export default update;
--- a/devtools/client/debugger/new/src/reducers/sources.js
+++ b/devtools/client/debugger/new/src/reducers/sources.js
@@ -4,17 +4,17 @@
 
 // @flow
 
 /**
  * Sources reducer
  * @module reducers/sources
  */
 
-import { createSelector } from "reselect";
+import { createSelector } from "../utils/createSelector";
 import {
   getPrettySourceURL,
   underRoot,
   getRelativeUrl,
   isGenerated,
   isOriginal as isOriginalSource
 } from "../utils/source";
 import { originalToGeneratedId } from "devtools-source-map";
--- a/devtools/client/debugger/new/src/reducers/tabs.js
+++ b/devtools/client/debugger/new/src/reducers/tabs.js
@@ -4,17 +4,17 @@
 
 // @flow
 
 /**
  * Tabs reducer
  * @module reducers/tabs
  */
 
-import { createSelector } from "reselect";
+import { createSelector } from "../utils/createSelector";
 import { isOriginalId } from "devtools-source-map";
 import move from "lodash-move";
 
 import { asyncStore } from "../utils/prefs";
 import {
   getSource,
   getSources,
   getUrls,
--- a/devtools/client/debugger/new/src/reducers/ui.js
+++ b/devtools/client/debugger/new/src/reducers/ui.js
@@ -38,31 +38,29 @@ export type UIState = {
   highlightedLineRange?: {
     start?: number,
     end?: number,
     sourceId?: number
   },
   conditionalPanelLocation: null | SourceLocation
 };
 
-export const createUIState = makeRecord(
-  ({
-    selectedPrimaryPaneTab: "sources",
-    activeSearch: null,
-    contextMenu: {},
-    shownSource: null,
-    startPanelCollapsed: prefs.startPanelCollapsed,
-    endPanelCollapsed: prefs.endPanelCollapsed,
-    frameworkGroupingOn: prefs.frameworkGroupingOn,
-    highlightedLineRange: undefined,
-    conditionalPanelLocation: null,
-    orientation: "horizontal",
-    viewport: null
-  }: UIState)
-);
+export const createUIState: () => Record<UIState> = makeRecord({
+  selectedPrimaryPaneTab: "sources",
+  activeSearch: null,
+  contextMenu: {},
+  shownSource: null,
+  startPanelCollapsed: prefs.startPanelCollapsed,
+  endPanelCollapsed: prefs.endPanelCollapsed,
+  frameworkGroupingOn: prefs.frameworkGroupingOn,
+  highlightedLineRange: undefined,
+  conditionalPanelLocation: null,
+  orientation: "horizontal",
+  viewport: null
+});
 
 function update(
   state: Record<UIState> = createUIState(),
   action: Action
 ): Record<UIState> {
   switch (action.type) {
     case "TOGGLE_ACTIVE_SEARCH": {
       return state.set("activeSearch", action.value);
@@ -181,17 +179,17 @@ export function getHighlightedLineRange(
 }
 
 export function getConditionalPanelLocation(
   state: OuterState
 ): null | SourceLocation {
   return state.ui.get("conditionalPanelLocation");
 }
 
-export function getOrientation(state: OuterState): boolean {
+export function getOrientation(state: OuterState): OrientationType {
   return state.ui.get("orientation");
 }
 
 export function getViewport(state: OuterState) {
   return state.ui.get("viewport");
 }
 
 export default update;
--- a/devtools/client/debugger/new/src/selectors/breakpointSources.js
+++ b/devtools/client/debugger/new/src/selectors/breakpointSources.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import { sortBy, uniq } from "lodash";
-import { createSelector } from "reselect";
+import { createSelector } from "../utils/createSelector";
 import {
   getSources,
   getBreakpointsList,
   getSelectedSource
 } from "../selectors";
 import { isGenerated, getFilename } from "../utils/source";
 import { getSelectedLocation } from "../utils/source-maps";
 
--- a/devtools/client/debugger/new/src/selectors/breakpoints.js
+++ b/devtools/client/debugger/new/src/selectors/breakpoints.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 { createSelector } from "reselect";
+import { createSelector } from "../utils/createSelector";
 
 import type {
   BreakpointsState,
   XHRBreakpointsList
 } from "../reducers/breakpoints";
 
 type OuterState = { breakpoints: BreakpointsState };
 
--- a/devtools/client/debugger/new/src/selectors/getCallStackFrames.js
+++ b/devtools/client/debugger/new/src/selectors/getCallStackFrames.js
@@ -8,17 +8,17 @@ import {
   getSourceInSources
 } from "../reducers/sources";
 import { getFrames } from "../reducers/pause";
 import { annotateFrames } from "../utils/pause/frames";
 import { isOriginal } from "../utils/source";
 import { get } from "lodash";
 import type { Frame, Source } from "../types";
 import type { SourcesMap } from "../reducers/sources";
-import { createSelector } from "reselect";
+import { createSelector } from "../utils/createSelector";
 
 function getLocation(frame, isGeneratedSource) {
   return isGeneratedSource
     ? frame.generatedLocation || frame.location
     : frame.location;
 }
 
 function getSourceForFrame(sources, frame, isGeneratedSource) {
--- a/devtools/client/debugger/new/src/selectors/visibleBreakpoints.js
+++ b/devtools/client/debugger/new/src/selectors/visibleBreakpoints.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import { isGeneratedId } from "devtools-source-map";
-import { createSelector } from "reselect";
+import { createSelector } from "../utils/createSelector";
 import { uniqBy } from "lodash";
 
 import { getBreakpointsList } from "../reducers/breakpoints";
 import { getSelectedSource } from "../reducers/sources";
 
 import memoize from "../utils/memoize";
 import { sortBreakpoints } from "../utils/breakpoint";
 
@@ -41,34 +41,34 @@ function isVisible(breakpoint, selectedS
   const isGeneratedSource = isGeneratedId(sourceId);
 
   const location = getLocation(breakpoint, isGeneratedSource);
   return location.sourceId === sourceId;
 }
 
 /*
  * Finds the breakpoints, which appear in the selected source.
-  */
+ */
 export const getVisibleBreakpoints = createSelector(
   getSelectedSource,
   getBreakpointsList,
   (selectedSource: Source, breakpoints: Breakpoint[]) => {
     if (!selectedSource) {
       return null;
     }
 
     return breakpoints
       .filter(bp => isVisible(bp, selectedSource))
       .map(bp => formatBreakpoint(bp, selectedSource));
   }
 );
 
 /*
  * Finds the first breakpoint per line, which appear in the selected source.
-  */
+ */
 export const getFirstVisibleBreakpoints = createSelector(
   getVisibleBreakpoints,
   breakpoints => {
     if (!breakpoints) {
       return null;
     }
 
     return uniqBy(sortBreakpoints(breakpoints), bp => bp.location.line);
--- a/devtools/client/debugger/new/src/selectors/visibleColumnBreakpoints.js
+++ b/devtools/client/debugger/new/src/selectors/visibleColumnBreakpoints.js
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 import { groupBy, get, sortedUniqBy } from "lodash";
-import { createSelector } from "reselect";
+import { createSelector } from "../utils/createSelector";
 
 import { getViewport } from "../selectors";
 import { getVisibleBreakpoints } from "./visibleBreakpoints";
 import { getVisiblePausePoints } from "./visiblePausePoints";
 import { makeLocationId } from "../utils/breakpoint";
 
 import type { SourceLocation } from "../types";
 
--- a/devtools/client/debugger/new/src/selectors/visiblePausePoints.js
+++ b/devtools/client/debugger/new/src/selectors/visiblePausePoints.js
@@ -1,17 +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/>. */
 
 import { getSelectedSource } from "../reducers/sources";
 import { getPausePoints } from "../reducers/ast";
-import { convertToList } from "../utils/pause/pausePoints";
 
 export function getVisiblePausePoints(state) {
   const source = getSelectedSource(state);
   if (!source) {
     return null;
   }
 
-  const pausePoints = getPausePoints(state, source.id);
-  return convertToList(pausePoints);
+  return getPausePoints(state, source.id);
 }
--- a/devtools/client/debugger/new/src/selectors/visibleSelectedFrame.js
+++ b/devtools/client/debugger/new/src/selectors/visibleSelectedFrame.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import { getSelectedLocation } from "../reducers/sources";
 import { getSelectedFrame } from "../reducers/pause";
 import { isOriginalId } from "devtools-source-map";
-import { createSelector } from "reselect";
+import { createSelector } from "../utils/createSelector";
 
 import type { Frame, SourceLocation } from "../types";
 
 function getLocation(frame: Frame, location?: SourceLocation) {
   if (!location) {
     return frame.location;
   }
 
--- a/devtools/client/debugger/new/src/utils/ast.js
+++ b/devtools/client/debugger/new/src/utils/ast.js
@@ -1,27 +1,25 @@
 /* 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 { xor, range } from "lodash";
-import { convertToList } from "./pause/pausePoints";
-
 import type { SourceLocation, Position } from "../types";
 import type { Symbols } from "../reducers/ast";
 
 import type {
   AstPosition,
   AstLocation,
-  PausePoints,
   FunctionDeclaration,
   ClassDeclaration
 } from "../workers/parser";
+import type { PausePoints } from "../reducers/types";
 
 export function findBestMatchExpression(symbols: Symbols, tokenPos: Position) {
   if (symbols.loading) {
     return null;
   }
 
   const { line, column } = tokenPos;
   const { memberExpressions, identifiers, literals } = symbols;
@@ -38,24 +36,25 @@ export function findBestMatchExpression(
       if (overlaps) {
         return expression;
       }
 
       return found;
     }, null);
 }
 
-export function findEmptyLines(sourceText: string, pausePoints: PausePoints) {
+export function findEmptyLines(
+  sourceText: string,
+  pausePoints: PausePoints
+): number[] {
   if (!pausePoints || !sourceText) {
     return [];
   }
 
-  const pausePointsList = convertToList(pausePoints);
-
-  const breakpoints = pausePointsList.filter(point => point.types.break);
+  const breakpoints = pausePoints.filter(point => point.types.break);
   const breakpointLines = breakpoints.map(point => point.location.line);
 
   if (!sourceText || breakpointLines.length == 0) {
     return [];
   }
 
   const lineCount = sourceText.split("\n").length;
   const sourceLines = range(1, lineCount + 1);
--- a/devtools/client/debugger/new/src/utils/breakpoint/index.js
+++ b/devtools/client/debugger/new/src/utils/breakpoint/index.js
@@ -193,17 +193,24 @@ export function createPendingBreakpoint(
 export function sortFormattedBreakpoints(breakpoints: FormattedBreakpoint[]) {
   return _sortBreakpoints(breakpoints, "selectedLocation");
 }
 
 export function sortBreakpoints(breakpoints: Breakpoint[]) {
   return _sortBreakpoints(breakpoints, "location");
 }
 
-function _sortBreakpoints(breakpoints: Array<Object>, property: string) {
-  return sortBy(breakpoints, [
-    // Priority: line number, undefined column, column number
-    `${property}.line`,
-    bp => {
-      return bp[property].column === undefined || bp[property].column;
-    }
-  ]);
+function _sortBreakpoints(
+  breakpoints: Array<Object>,
+  property: string
+): Array<Object> {
+  // prettier-ignore
+  return sortBy(
+    breakpoints,
+    [
+      // Priority: line number, undefined column, column number
+      `${property}.line`,
+      bp => {
+        return bp[property].column === undefined || bp[property].column;
+      }
+    ]
+  );
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/utils/connect.js
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+// @flow
+
+import { connect as reduxConnect } from "react-redux";
+import * as React from "react";
+
+export function connect<Config, RSP: {}, MDP: {}>(
+  mapStateToProps: (state: any, props: any) => RSP,
+  mapDispatchToProps?: (() => MDP) | MDP
+): (
+  Component: React.AbstractComponent<Config>
+) => React.AbstractComponent<$Diff<Config, RSP & MDP>> {
+  // $FlowFixMe
+  return reduxConnect(mapStateToProps, mapDispatchToProps);
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/utils/createSelector.js
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+import { createSelector as reselectCreateSelector } from "reselect";
+
+function createSelector(...args) {
+  // $FlowFixMe
+  return reselectCreateSelector(...args);
+}
+
+export { createSelector };
--- a/devtools/client/debugger/new/src/utils/editor/index.js
+++ b/devtools/client/debugger/new/src/utils/editor/index.js
@@ -13,17 +13,17 @@ export { onMouseOver } from "./token-eve
 import { createEditor } from "./create-editor";
 import { shouldPrettyPrint, isOriginal } from "../source";
 import { findNext, findPrev } from "./source-search";
 
 import { isWasm, lineToWasmOffset, wasmOffsetToLine } from "../wasm";
 
 import type { AstLocation } from "../../workers/parser";
 import type { EditorPosition, EditorRange } from "../editor/types";
-import type { SourceLocation } from "../../types";
+import type { SearchModifiers, Source, SourceLocation } from "../../types";
 type Editor = Object;
 
 let editor: ?Editor;
 
 export function getEditor() {
   if (editor) {
     return editor;
   }
@@ -53,31 +53,37 @@ export function endOperation() {
   const codeMirror = getCodeMirror();
   if (!codeMirror) {
     return;
   }
 
   codeMirror.endOperation();
 }
 
-export function shouldShowPrettyPrint(source) {
+export function shouldShowPrettyPrint(source: Source) {
   return shouldPrettyPrint(source);
 }
 
-export function shouldShowFooter(source, horizontal) {
+export function shouldShowFooter(source: ?Source, horizontal: boolean) {
   if (!horizontal) {
     return true;
   }
   if (!source) {
     return false;
   }
   return shouldShowPrettyPrint(source) || isOriginal(source);
 }
 
-export function traverseResults(e, ctx, query, dir, modifiers) {
+export function traverseResults(
+  e: Event,
+  ctx: any,
+  query: string,
+  dir: string,
+  modifiers: SearchModifiers
+) {
   e.stopPropagation();
   e.preventDefault();
 
   if (dir == "prev") {
     findPrev(ctx, query, true, modifiers);
   } else if (dir == "next") {
     findNext(ctx, query, true, modifiers);
   }
@@ -176,27 +182,31 @@ export function getLocationsInViewport({
       line: bottomVisibleLine,
       column: rightCharacter
     }
   };
 }
 
 export function markText(
   { codeMirror }: Object,
-  className,
+  className: String,
   { start, end }: EditorRange
 ) {
   return codeMirror.markText(
     { ch: start.column, line: start.line },
     { ch: end.column, line: end.line },
     { className }
   );
 }
 
-export function lineAtHeight({ codeMirror }, sourceId, event) {
+export function lineAtHeight(
+  { codeMirror }: Object,
+  sourceId: string,
+  event: MouseEvent
+) {
   const _editorLine = codeMirror.lineAtHeight(event.clientY);
   return toSourceLine(sourceId, _editorLine);
 }
 
 export function getSourceLocationFromMouseEvent(
   { codeMirror }: Object,
   selectedLocation: SourceLocation,
   e: MouseEvent
@@ -208,17 +218,17 @@ export function getSourceLocationFromMou
 
   return {
     sourceId: selectedLocation.sourceId,
     line: line + 1,
     column: ch + 1
   };
 }
 
-export function forEachLine(codeMirror: Object, iter) {
+export function forEachLine(codeMirror: Object, iter: Function) {
   codeMirror.operation(() => {
     codeMirror.doc.iter(0, codeMirror.lineCount(), iter);
   });
 }
 
 export function removeLineClass(
   codeMirror: Object,
   line: number,
--- a/devtools/client/debugger/new/src/utils/expressions.js
+++ b/devtools/client/debugger/new/src/utils/expressions.js
@@ -12,17 +12,17 @@ export function sanitizeInput(input: str
   return input.replace(/"/g, '"');
 }
 
 /*
  * wrap the expression input in a try/catch so that it can be safely
  * evaluated.
  *
  * NOTE: we add line after the expression to protect against comments.
-*/
+ */
 export function wrapExpression(input: string) {
   return correctIndentation(`
     try {
       ${sanitizeInput(input)}
     } catch (e) {
       e
     }
   `);
--- a/devtools/client/debugger/new/src/utils/moz.build
+++ b/devtools/client/debugger/new/src/utils/moz.build
@@ -12,16 +12,18 @@ DIRS += [
 
 DebuggerModules(
     'assert.js',
     'ast.js',
     'asyncStoreHelper.js',
     'bootstrap.js',
     'build-query.js',
     'clipboard.js',
+    'connect.js',
+    'createSelector.js',
     'dbg.js',
     'defer.js',
     'DevToolsUtils.js',
     'expressions.js',
     'fromJS.js',
     'function.js',
     'indentation.js',
     'isMinified.js',
--- a/devtools/client/debugger/new/src/utils/pause/pausePoints.js
+++ b/devtools/client/debugger/new/src/utils/pause/pausePoints.js
@@ -1,62 +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 { reverse } from "lodash";
 
-import type { PausePoints } from "../../workers/parser";
-import type { Position } from "../../types";
-
-type PausePoint = {
-  location: Position,
-  types: { break: boolean, step: boolean }
-};
+import type { PausePoints, PausePointsMap } from "../../reducers/types";
 
 function insertStrtAt(string, index, newString) {
   const start = string.slice(0, index);
   const end = string.slice(index);
   return `${start}${newString}${end}`;
 }
 
-export function convertToList(pausePoints: PausePoints): PausePoint[] {
+export function convertToList(pausePoints: PausePointsMap): PausePoints {
   const list = [];
   for (const line in pausePoints) {
     for (const column in pausePoints[line]) {
       list.push(pausePoints[line][column]);
     }
   }
   return list;
 }
 
 export function formatPausePoints(text: string, pausePoints: PausePoints) {
-  const nodes = reverse(convertToList(pausePoints));
+  const nodes = reverse(pausePoints);
   const lines = text.split("\n");
   nodes.forEach((node, index) => {
     const { line, column } = node.location;
     const { break: breakPoint, step } = node.types;
     const types = `${breakPoint ? "b" : ""}${step ? "s" : ""}`;
     lines[line - 1] = insertStrtAt(lines[line - 1], column, `/*${types}*/`);
   });
 
   return lines.join("\n");
 }
-
-export async function mapPausePoints<T>(
-  pausePoints: PausePoints,
-  iteratee: PausePoint => T
-) {
-  const results = await Promise.all(convertToList(pausePoints).map(iteratee));
-  let index = 0;
-
-  const newPausePoints = {};
-  for (const line in pausePoints) {
-    const linePoints = pausePoints[line];
-    const newLinePoints = (newPausePoints[line] = {});
-    for (const column in linePoints) {
-      newLinePoints[column] = results[index++];
-    }
-  }
-
-  return newPausePoints;
-}
--- a/devtools/client/debugger/new/src/utils/prefs.js
+++ b/devtools/client/debugger/new/src/utils/prefs.js
@@ -57,16 +57,17 @@ if (isDevelopment()) {
   pref("devtools.debugger.features.pause-points", true);
   pref("devtools.debugger.features.skip-pausing", true);
   pref("devtools.debugger.features.component-pane", false);
   pref("devtools.debugger.features.autocomplete-expressions", false);
   pref("devtools.debugger.features.map-expression-bindings", true);
   pref("devtools.debugger.features.map-await-expression", true);
   pref("devtools.debugger.features.xhr-breakpoints", true);
   pref("devtools.debugger.features.origial-blackbox", false);
+  pref("devtools.debugger.features.windowless-workers", false);
 }
 
 export const prefs = new PrefsHelper("devtools", {
   logging: ["Bool", "debugger.alphabetize-outline"],
   alphabetizeOutline: ["Bool", "debugger.alphabetize-outline"],
   autoPrettyPrint: ["Bool", "debugger.auto-pretty-print"],
   clientSourceMapsEnabled: ["Bool", "source-map.client-service.enabled"],
   pauseOnExceptions: ["Bool", "debugger.pause-on-exceptions"],
@@ -101,16 +102,17 @@ export const features = new PrefsHelper(
   asyncStepping: ["Bool", "async-stepping"],
   wasm: ["Bool", "wasm"],
   shortcuts: ["Bool", "shortcuts"],
   root: ["Bool", "root"],
   columnBreakpoints: ["Bool", "column-breakpoints"],
   mapScopes: ["Bool", "map-scopes"],
   removeCommandBarOptions: ["Bool", "remove-command-bar-options"],
   workers: ["Bool", "workers"],
+  windowlessWorkers: ["Bool", "windowless-workers"],
   outline: ["Bool", "outline"],
   codeFolding: ["Bool", "code-folding"],
   pausePoints: ["Bool", "pause-points"],
   skipPausing: ["Bool", "skip-pausing"],
   autocompleteExpression: ["Bool", "autocomplete-expressions"],
   mapExpressionBindings: ["Bool", "map-expression-bindings"],
   mapAwaitExpression: ["Bool", "map-await-expression"],
   componentPane: ["Bool", "component-pane"],
--- a/devtools/client/debugger/new/src/utils/preview.js
+++ b/devtools/client/debugger/new/src/utils/preview.js
@@ -1,38 +1,9 @@
 /* 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 { Grip } from "../types";
-
-const IMMUTABLE_FIELDS = ["_root", "__ownerID", "__altered", "__hash"];
-const REACT_FIELDS = ["_reactInternalInstance", "_reactInternalFiber"];
-
-export function isImmutablePreview(result: ?Grip) {
-  return result && isImmutable(result.preview);
-}
-
-export function isImmutable(result: ?Grip) {
-  if (!result || typeof result.ownProperties != "object") {
-    return;
-  }
-
-  const ownProperties = result.ownProperties;
-  return IMMUTABLE_FIELDS.every(field =>
-    Object.keys(ownProperties).includes(field)
-  );
-}
-
-export function isReactComponent(result: ?Grip) {
-  if (!result || typeof result.ownProperties != "object") {
-    return;
-  }
-
-  const ownProperties = result.ownProperties;
-  return REACT_FIELDS.some(field => Object.keys(ownProperties).includes(field));
-}
-
 export function isConsole(expression: string) {
   return /^console/.test(expression);
 }
--- a/devtools/client/debugger/new/src/utils/source-queue.js
+++ b/devtools/client/debugger/new/src/utils/source-queue.js
@@ -9,17 +9,16 @@ import type { Source } from "../types";
 
 let newSources;
 let queuedSources;
 let currentWork;
 
 async function dispatchNewSources() {
   const sources = queuedSources;
   queuedSources = [];
-
   currentWork = await newSources(sources);
 }
 
 const queue = throttle(dispatchNewSources, 100);
 
 export default {
   initialize: (actions: Object) => {
     newSources = actions.newSources;
--- a/devtools/client/debugger/new/src/utils/sources-tree/addToTree.js
+++ b/devtools/client/debugger/new/src/utils/sources-tree/addToTree.js
@@ -77,16 +77,17 @@ function findOrCreateNode(
 }
 
 /*
  * walk the source tree to the final node for a given url,
  * adding new nodes along the way
  */
 function traverseTree(
   url: ParsedURL,
+  thread: string,
   tree: TreeDirectory,
   debuggeeHost: ?string
 ): TreeNode {
   const parts = url.path.split("/").filter(p => p !== "");
   parts.unshift(url.group);
 
   let path = "";
   return parts.reduce((subTree, part, index) => {
@@ -161,13 +162,13 @@ export function addToTree(
   projectRoot: string
 ) {
   const url = getURL(source, debuggeeHost);
 
   if (isInvalidUrl(url, source)) {
     return;
   }
 
-  const finalNode = traverseTree(url, tree, debuggeeHost);
+  const finalNode = traverseTree(url, source.thread, tree, debuggeeHost);
 
   // $FlowIgnore
   finalNode.contents = addSourceToNode(finalNode, url, source);
 }
--- a/devtools/client/debugger/new/src/utils/tabs.js
+++ b/devtools/client/debugger/new/src/utils/tabs.js
@@ -16,17 +16,17 @@ type SourcesList = Source[];
  * @param sourceTabEls HTMLCollection
  *
  * @returns Immutable.list
  */
 
 export function getHiddenTabs(
   sourceTabs: SourcesList,
   sourceTabEls: Array<any>
-) {
+): SourcesList {
   sourceTabEls = [].slice.call(sourceTabEls);
   function getTopOffset() {
     const topOffsets = sourceTabEls.map(t => t.getBoundingClientRect().top);
     return Math.min(...topOffsets);
   }
 
   function hasTopOffset(el) {
     // adding 10px helps account for cases where the tab might be offset by
--- a/devtools/client/debugger/new/src/utils/text.js
+++ b/devtools/client/debugger/new/src/utils/text.js
@@ -30,18 +30,18 @@ export function formatKeyShortcut(shortc
   if (isMacOS) {
     return shortcut
       .replace(/Shift\+/g, "\u21E7 ")
       .replace(/Command\+|Cmd\+/g, "\u2318 ")
       .replace(/CommandOrControl\+|CmdOrCtrl\+/g, "\u2318 ")
       .replace(/Alt\+/g, "\u2325 ");
   }
   return shortcut
-    .replace(/CommandOrControl\+|CmdOrCtrl\+/g, `${L10N.getStr("ctrl")} `)
-    .replace(/Shift\+/g, "Shift ");
+    .replace(/CommandOrControl\+|CmdOrCtrl\+/g, `${L10N.getStr("ctrl")}+`)
+    .replace(/Shift\+/g, "Shift+");
 }
 
 /**
  * Truncates the received text to the maxLength in the format:
  * Original: 'this is a very long text and ends here'
  * Truncated: 'this is a ver...and ends here'
  * @param {String} sourceText - Source text
  * @param {Number} maxLength - Max allowed length
--- a/devtools/client/debugger/new/src/utils/timings.js
+++ b/devtools/client/debugger/new/src/utils/timings.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import { zip } from "lodash";
 
-export function getAsyncTimes(name: string) {
+export function getAsyncTimes(name: string): number[] {
   return zip(
     window.performance.getEntriesByName(`${name}_start`),
     window.performance.getEntriesByName(`${name}_end`)
   ).map(([start, end]) => +(end.startTime - start.startTime).toPrecision(2));
 }
 
 function getTimes(name) {
   return window.performance
--- a/devtools/client/debugger/new/src/workers/parser/index.js
+++ b/devtools/client/debugger/new/src/workers/parser/index.js
@@ -2,20 +2,21 @@
  * 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 { workerUtils } from "devtools-utils";
 const { WorkerDispatcher } = workerUtils;
 
-import type { AstLocation, AstPosition, PausePoints } from "./types";
+import type { AstLocation, AstPosition } from "./types";
 import type { SourceLocation, Source, SourceId } from "../../types";
 import type { SourceScope } from "./getScopes/visitor";
 import type { SymbolDeclarations } from "./getSymbols";
+import type { PausePointsMap } from "../../reducers/types";
 
 const dispatcher = new WorkerDispatcher();
 export const start = (url: string, win: any = window) =>
   dispatcher.start(url, win);
 export const stop = () => dispatcher.stop();
 
 export const findOutOfScopeLocations = async (
   sourceId: string,
@@ -74,34 +75,35 @@ export const mapExpression = async (
     bindings,
     shouldMapBindings,
     shouldMapAwait
   );
 
 export const getFramework = async (sourceId: string): Promise<?string> =>
   dispatcher.invoke("getFramework", sourceId);
 
-export const getPausePoints = async (sourceId: string): Promise<PausePoints> =>
-  dispatcher.invoke("getPausePoints", sourceId);
+export const getPausePoints = async (
+  sourceId: string
+): Promise<PausePointsMap> => dispatcher.invoke("getPausePoints", sourceId);
 
 export type {
   SourceScope,
   BindingData,
   BindingLocation,
   BindingLocationType,
   BindingDeclarationLocation,
   BindingMetaValue,
   BindingType
 } from "./getScopes";
 
 export type {
   AstLocation,
   AstPosition,
   PausePoint,
-  PausePoints
+  PausePointsMap
 } from "./types";
 
 export type {
   ClassDeclaration,
   SymbolDeclaration,
   SymbolDeclarations,
   IdentifierDeclaration,
   FunctionDeclaration
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-columns.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-columns.js
@@ -55,23 +55,23 @@ add_task(async function() {
 
   await selectSource(dbg, "simple1");
 
   // Scroll down to desired line so that column breakpoints render
   getCM(dbg).setCursor({ line: 15, ch: 0 });
 
   // Create a breakpoint at 15:undefined
   await addBreakpoint(dbg, "simple1", 15);
-  
+
   // Wait for column breakpoint markers
   await waitForElementWithSelector(dbg, ".column-breakpoint");
 
   let columnBreakpointMarkers = getColumnBreakpointElements(dbg);
   ok(
-    columnBreakpointMarkers.length === 2, 
+    columnBreakpointMarkers.length === 2,
       "2 column breakpoint markers display"
   );
 
   // Create a breakpoint at 15:8
   columnBreakpointMarkers[0].click();
 
   // Create a breakpoint at 15:28
   columnBreakpointMarkers[1].click();
@@ -84,17 +84,17 @@ add_task(async function() {
   // Scroll down in secondary pane so element we want to right-click is showing
   dbg.win.document.querySelector(".secondary-panes").scrollTop = 100;
 
   // Set a condition at 15:8
   await setConditionalBreakpoint(dbg, 4, "Eight");
 
   // Ensure column breakpoint is yellow
   await waitForElementWithSelector(dbg, ".column-breakpoint.has-condition");
-  
+
   // Remove the breakpoint from 15:undefined via the secondary pane context menu
   removeBreakpointViaContext(dbg, 3);
 
   // Ensure that there's still a marker on line 15
   await waitForState(dbg, state => dbg.selectors.getBreakpointCount(state) == 2);
   await waitForElementWithSelector(dbg, ".column-breakpoint.has-condition");
   columnBreakpointMarkers = getColumnBreakpointElements(dbg);
   ok(hasCondition(columnBreakpointMarkers[0]), "First column breakpoint has conditional style");
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-react-app.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-react-app.js
@@ -7,12 +7,12 @@ add_task(async function() {
   invokeInTab("clickButton");
   await waitForPaused(dbg);
   await waitForState(
     dbg,
     state => dbg.selectors.getSelectedScopeMappings(state)
   );
 
   await assertPreviewTextValue(dbg, 10, 22, {
-    text: "Map\na: 2",
+    text: "size: 1",
     expression: "_this.fields;"
   });
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-search-project.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-search-project.js
@@ -21,19 +21,17 @@ async function selectResult(dbg) {
   );
   await clickElement(dbg, "fileMatch");
   return select;
 }
 
 function getResultsCount(dbg) {
   const matches = dbg.selectors
     .getTextSearchResults(dbg.getState())
-    .valueSeq()
-    .map(file => file.matches)
-    .toJS();
+    .map(file => file.matches);
 
   return [...matches].length;
 }
 
 // Testing project search
 add_task(async function() {
   await pushPref("devtools.debugger.project-text-search-enabled", true);
 
--- a/devtools/client/locales/en-US/debugger.properties
+++ b/devtools/client/locales/en-US/debugger.properties
@@ -327,16 +327,25 @@ searchPanelToken=Find in this file (%S)
 # LOCALIZATION NOTE (searchPanelGoToLine): This is the text that appears in the
 # filter panel popup for the line search operation.
 searchPanelGoToLine=Go to line (%S)
 
 # LOCALIZATION NOTE (searchPanelVariable): This is the text that appears in the
 # filter panel popup for the variables search operation.
 searchPanelVariable=Filter variables (%S)
 
+# LOCALIZATION NOTE (breakpointHeadingMenuItem.*): The text for all the elements
+# that are displayed in the breakpoint headings menu item popup.
+breakpointHeadingsMenuItem.enableInSource.label=Enable breakpoints
+breakpointHeadingsMenuItem.enableInSource.accesskey=E
+breakpointHeadingsMenuItem.disableInSource.label=Disable breakpoints
+breakpointHeadingsMenuItem.disableInSource.accesskey=D
+breakpointHeadingsMenuItem.removeInSource.label=Remove breakpoints
+breakpointHeadingsMenuItem.removeInSource.accesskey=R
+
 # LOCALIZATION NOTE (breakpointMenuItem): The text for all the elements that
 # are displayed in the breakpoints menu item popup.
 breakpointMenuItem.setConditional=Configure conditional breakpoint
 breakpointMenuItem.enableSelf2.label=Enable
 breakpointMenuItem.enableSelf2.accesskey=E
 breakpointMenuItem.disableSelf2.label=Disable
 breakpointMenuItem.disableSelf2.accesskey=D
 breakpointMenuItem.deleteSelf2.label=Remove
--- a/devtools/client/preferences/debugger.js
+++ b/devtools/client/preferences/debugger.js
@@ -63,8 +63,9 @@ pref("devtools.debugger.features.outline
 pref("devtools.debugger.features.pause-points", true);
 pref("devtools.debugger.features.component-pane", false);
 pref("devtools.debugger.features.async-stepping", true);
 pref("devtools.debugger.features.skip-pausing", true);
 pref("devtools.debugger.features.autocomplete-expressions", false);
 pref("devtools.debugger.features.map-expression-bindings", true);
 pref("devtools.debugger.features.xhr-breakpoints", true);
 pref("devtools.debugger.features.origial-blackbox", false);
+pref("devtools.debugger.features.windowless-workers", false);
--- a/devtools/shared/locales/en-US/styleinspector.properties
+++ b/devtools/shared/locales/en-US/styleinspector.properties
@@ -107,16 +107,42 @@ rule.angleSwatch.tooltip=Shift+click to 
 # LOCALIZATION NOTE (rule.flexToggle.tooltip): Text displayed in a tooltip
 # when the mouse is over a Flexbox toggle icon in the rule view.
 rule.flexToggle.tooltip=Click to toggle the Flexbox highlighter
 
 # LOCALIZATION NOTE (rule.gridToggle.tooltip): Text displayed in a tooltip
 # when the mouse is over a CSS Grid toggle icon in the rule view.
 rule.gridToggle.tooltip=Click to toggle the CSS Grid highlighter
 
+# LOCALIZATION NOTE (rule.filterStyles.placeholder): This is the placeholder that
+# goes in the search box when no search term has been entered.
+rule.filterStyles.placeholder=Filter Styles
+
+# LOCALIZATION NOTE (rule.addRule.tooltip): This is the tooltip shown when
+# hovering the `Add new rule` button in the rules view toolbar.
+rule.addRule.tooltip=Add new rule
+
+# LOCALIZATION NOTE (rule.togglePseudo.tooltip): This is the tooltip
+# shown when hovering over the `Toggle Pseudo Class Panel` button in the
+# rule view toolbar.
+rule.togglePseudo.tooltip=Toggle pseudo-classes
+
+# LOCALIZATION NOTE (rule.classPanel.toggleClass.tooltip): This is the tooltip
+# shown when hovering over the `Toggle Class Panel` button in the
+# rule view toolbar.
+rule.classPanel.toggleClass.tooltip=Toggle classes
+
+# LOCALIZATION NOTE (rule.classPanel.newClass.placeholder): This is the placeholder
+# shown inside the text field used to add a new class in the rule-view.
+rule.classPanel.newClass.placeholder=Add new class
+
+# LOCALIZATION NOTE (rule.classPanel.noClasses): This is the text displayed in the
+# class panel when the current element has no classes applied.
+rule.classPanel.noClasses=No classes on this element
+
 # LOCALIZATION NOTE (styleinspector.contextmenu.copyColor): Text displayed in the rule
 # and computed view context menu when a color value was clicked.
 styleinspector.contextmenu.copyColor=Copy Color
 
 # LOCALIZATION NOTE (styleinspector.contextmenu.copyColor.accessKey): Access key for
 # the rule and computed view context menu "Copy Color" entry.
 styleinspector.contextmenu.copyColor.accessKey=L
 
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -2947,17 +2947,19 @@ nsresult EventStateManager::PostHandleEv
   HandleCrossProcessEvent(aEvent, aStatus);
   // NOTE: the above call may have destroyed aTargetFrame, please use
   // mCurrentTarget henceforth.  This is to avoid using it accidentally:
   aTargetFrame = nullptr;
 
   // Most of the events we handle below require a frame.
   // Add special cases here.
   if (!mCurrentTarget && aEvent->mMessage != eMouseUp &&
-      aEvent->mMessage != eMouseDown) {
+      aEvent->mMessage != eMouseDown &&
+      aEvent->mMessage != eDragEnter &&
+      aEvent->mMessage != eDragOver) {
     return NS_OK;
   }
 
   // Keep the prescontext alive, we might need it after event dispatch
   RefPtr<nsPresContext> presContext = aPresContext;
   nsresult ret = NS_OK;
 
   switch (aEvent->mMessage) {
--- a/dom/svg/SVGComponentTransferFunctionElement.h
+++ b/dom/svg/SVGComponentTransferFunctionElement.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_SVGComponentTransferFunctionElement_h
 #define mozilla_dom_SVGComponentTransferFunctionElement_h
 
 #include "nsSVGEnum.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGNumber2.h"
 #include "SVGAnimatedNumberList.h"
 
 #define NS_SVG_FE_COMPONENT_TRANSFER_FUNCTION_ELEMENT_CID \
   {                                                       \
     0xafab106d, 0xbc18, 0x4f7f, {                         \
       0x9e, 0x29, 0xfe, 0xb4, 0xb0, 0x16, 0x5f, 0xf4      \
     }                                                     \
--- a/dom/svg/SVGFEBlendElement.cpp
+++ b/dom/svg/SVGFEBlendElement.cpp
@@ -81,20 +81,19 @@ bool SVGFEBlendElement::AttributeAffects
                                                   nsAtom* aAttribute) const {
   return SVGFEBlendElementBase::AttributeAffectsRendering(aNameSpaceID,
                                                           aAttribute) ||
          (aNameSpaceID == kNameSpaceID_None &&
           (aAttribute == nsGkAtoms::in || aAttribute == nsGkAtoms::in2 ||
            aAttribute == nsGkAtoms::mode));
 }
 
-void SVGFEBlendElement::GetSourceImageNames(
-    nsTArray<nsSVGStringInfo>& aSources) {
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN2], this));
+void SVGFEBlendElement::GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) {
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN1], this));
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN2], this));
 }
 
 //----------------------------------------------------------------------
 // SVGElement methods
 
 SVGElement::EnumAttributesInfo SVGFEBlendElement::GetEnumInfo() {
   return EnumAttributesInfo(mEnumAttributes, sEnumInfo, ArrayLength(sEnumInfo));
 }
--- a/dom/svg/SVGFEBlendElement.h
+++ b/dom/svg/SVGFEBlendElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFEBlendElement_h
 #define mozilla_dom_SVGFEBlendElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGEnum.h"
 
 nsresult NS_NewSVGFEBlendElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 namespace mozilla {
 namespace dom {
 
 typedef SVGFE SVGFEBlendElementBase;
@@ -34,18 +34,17 @@ class SVGFEBlendElement : public SVGFEBl
       nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
       const nsTArray<bool>& aInputsAreTainted,
       nsTArray<RefPtr<SourceSurface>>& aInputImages) override;
   virtual bool AttributeAffectsRendering(int32_t aNameSpaceID,
                                          nsAtom* aAttribute) const override;
   virtual nsSVGString& GetResultImageName() override {
     return mStringAttributes[RESULT];
   }
-  virtual void GetSourceImageNames(
-      nsTArray<nsSVGStringInfo>& aSources) override;
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> In1();
   already_AddRefed<SVGAnimatedString> In2();
   already_AddRefed<SVGAnimatedEnumeration> Mode();
 
--- a/dom/svg/SVGFEColorMatrixElement.cpp
+++ b/dom/svg/SVGFEColorMatrixElement.cpp
@@ -56,18 +56,18 @@ already_AddRefed<SVGAnimatedEnumeration>
 }
 
 already_AddRefed<DOMSVGAnimatedNumberList> SVGFEColorMatrixElement::Values() {
   return DOMSVGAnimatedNumberList::GetDOMWrapper(&mNumberListAttributes[VALUES],
                                                  this, VALUES);
 }
 
 void SVGFEColorMatrixElement::GetSourceImageNames(
-    nsTArray<nsSVGStringInfo>& aSources) {
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
+    nsTArray<SVGStringInfo>& aSources) {
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN1], this));
 }
 
 FilterPrimitiveDescription SVGFEColorMatrixElement::GetPrimitiveDescription(
     nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
     const nsTArray<bool>& aInputsAreTainted,
     nsTArray<RefPtr<SourceSurface>>& aInputImages) {
   uint32_t type = mEnumAttributes[TYPE].GetAnimValue();
   const SVGNumberList& values = mNumberListAttributes[VALUES].GetAnimValue();
--- a/dom/svg/SVGFEColorMatrixElement.h
+++ b/dom/svg/SVGFEColorMatrixElement.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_SVGFEColorMatrixElement_h
 #define mozilla_dom_SVGFEColorMatrixElement_h
 
 #include "nsSVGEnum.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "SVGAnimatedNumberList.h"
 
 nsresult NS_NewSVGFEColorMatrixElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
@@ -36,18 +36,17 @@ class SVGFEColorMatrixElement : public S
       nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
       const nsTArray<bool>& aInputsAreTainted,
       nsTArray<RefPtr<SourceSurface>>& aInputImages) override;
   virtual bool AttributeAffectsRendering(int32_t aNameSpaceID,
                                          nsAtom* aAttribute) const override;
   virtual nsSVGString& GetResultImageName() override {
     return mStringAttributes[RESULT];
   }
-  virtual void GetSourceImageNames(
-      nsTArray<nsSVGStringInfo>& aSources) override;
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> In1();
   already_AddRefed<SVGAnimatedEnumeration> Type();
   already_AddRefed<DOMSVGAnimatedNumberList> Values();
 
--- a/dom/svg/SVGFEComponentTransferElement.cpp
+++ b/dom/svg/SVGFEComponentTransferElement.cpp
@@ -79,14 +79,14 @@ SVGFEComponentTransferElement::GetPrimit
 bool SVGFEComponentTransferElement::AttributeAffectsRendering(
     int32_t aNameSpaceID, nsAtom* aAttribute) const {
   return SVGFEComponentTransferElementBase::AttributeAffectsRendering(
              aNameSpaceID, aAttribute) ||
          (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::in);
 }
 
 void SVGFEComponentTransferElement::GetSourceImageNames(
-    nsTArray<nsSVGStringInfo>& aSources) {
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
+    nsTArray<SVGStringInfo>& aSources) {
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN1], this));
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/SVGFEComponentTransferElement.h
+++ b/dom/svg/SVGFEComponentTransferElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFEComponentTransferElement_h
 #define mozilla_dom_SVGFEComponentTransferElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 
 nsresult NS_NewSVGFEComponentTransferElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
 typedef SVGFE SVGFEComponentTransferElementBase;
@@ -34,18 +34,17 @@ class SVGFEComponentTransferElement : pu
       nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
       const nsTArray<bool>& aInputsAreTainted,
       nsTArray<RefPtr<SourceSurface>>& aInputImages) override;
   virtual bool AttributeAffectsRendering(int32_t aNameSpaceID,
                                          nsAtom* aAttribute) const override;
   virtual nsSVGString& GetResultImageName() override {
     return mStringAttributes[RESULT];
   }
-  virtual void GetSourceImageNames(
-      nsTArray<nsSVGStringInfo>& aSources) override;
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) override;
 
   // nsIContent
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> In1();
 
  protected:
--- a/dom/svg/SVGFECompositeElement.cpp
+++ b/dom/svg/SVGFECompositeElement.cpp
@@ -106,19 +106,19 @@ bool SVGFECompositeElement::AttributeAff
          (aNameSpaceID == kNameSpaceID_None &&
           (aAttribute == nsGkAtoms::in || aAttribute == nsGkAtoms::in2 ||
            aAttribute == nsGkAtoms::k1 || aAttribute == nsGkAtoms::k2 ||
            aAttribute == nsGkAtoms::k3 || aAttribute == nsGkAtoms::k4 ||
            aAttribute == nsGkAtoms::_operator));
 }
 
 void SVGFECompositeElement::GetSourceImageNames(
-    nsTArray<nsSVGStringInfo>& aSources) {
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN2], this));
+    nsTArray<SVGStringInfo>& aSources) {
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN1], this));
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN2], this));
 }
 
 //----------------------------------------------------------------------
 // SVGElement methods
 
 SVGElement::NumberAttributesInfo SVGFECompositeElement::GetNumberInfo() {
   return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
                               ArrayLength(sNumberInfo));
--- a/dom/svg/SVGFECompositeElement.h
+++ b/dom/svg/SVGFECompositeElement.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_SVGFECompositeElement_h
 #define mozilla_dom_SVGFECompositeElement_h
 
 #include "nsSVGEnum.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGNumber2.h"
 
 nsresult NS_NewSVGFECompositeElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
@@ -36,18 +36,17 @@ class SVGFECompositeElement : public SVG
       nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
       const nsTArray<bool>& aInputsAreTainted,
       nsTArray<RefPtr<SourceSurface>>& aInputImages) override;
   virtual bool AttributeAffectsRendering(int32_t aNameSpaceID,
                                          nsAtom* aAttribute) const override;
   virtual nsSVGString& GetResultImageName() override {
     return mStringAttributes[RESULT];
   }
-  virtual void GetSourceImageNames(
-      nsTArray<nsSVGStringInfo>& aSources) override;
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> In1();
   already_AddRefed<SVGAnimatedString> In2();
   already_AddRefed<SVGAnimatedEnumeration> Operator();
   already_AddRefed<SVGAnimatedNumber> K1();
--- a/dom/svg/SVGFEConvolveMatrixElement.cpp
+++ b/dom/svg/SVGFEConvolveMatrixElement.cpp
@@ -116,18 +116,18 @@ SVGFEConvolveMatrixElement::KernelUnitLe
 
 already_AddRefed<SVGAnimatedNumber>
 SVGFEConvolveMatrixElement::KernelUnitLengthY() {
   return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber(
       nsSVGNumberPair::eSecond, this);
 }
 
 void SVGFEConvolveMatrixElement::GetSourceImageNames(
-    nsTArray<nsSVGStringInfo>& aSources) {
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
+    nsTArray<SVGStringInfo>& aSources) {
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN1], this));
 }
 
 FilterPrimitiveDescription SVGFEConvolveMatrixElement::GetPrimitiveDescription(
     nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
     const nsTArray<bool>& aInputsAreTainted,
     nsTArray<RefPtr<SourceSurface>>& aInputImages) {
   FilterPrimitiveDescription failureDescription;
 
--- a/dom/svg/SVGFEConvolveMatrixElement.h
+++ b/dom/svg/SVGFEConvolveMatrixElement.h
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_SVGFEConvolveMatrixElement_h
 #define mozilla_dom_SVGFEConvolveMatrixElement_h
 
 #include "nsSVGBoolean.h"
 #include "nsSVGEnum.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGInteger.h"
 #include "nsSVGIntegerPair.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGString.h"
 #include "SVGAnimatedNumberList.h"
 
 nsresult NS_NewSVGFEConvolveMatrixElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
@@ -44,18 +44,17 @@ class SVGFEConvolveMatrixElement : publi
       nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
       const nsTArray<bool>& aInputsAreTainted,
       nsTArray<RefPtr<SourceSurface>>& aInputImages) override;
   virtual bool AttributeAffectsRendering(int32_t aNameSpaceID,
                                          nsAtom* aAttribute) const override;
   virtual nsSVGString& GetResultImageName() override {
     return mStringAttributes[RESULT];
   }
-  virtual void GetSourceImageNames(
-      nsTArray<nsSVGStringInfo>& aSources) override;
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> In1();
   already_AddRefed<SVGAnimatedInteger> OrderX();
   already_AddRefed<SVGAnimatedInteger> OrderY();
   already_AddRefed<DOMSVGAnimatedNumberList> KernelMatrix();
--- a/dom/svg/SVGFEDiffuseLightingElement.h
+++ b/dom/svg/SVGFEDiffuseLightingElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFEDiffuseLightingElement_h
 #define mozilla_dom_SVGFEDiffuseLightingElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 
 nsresult NS_NewSVGFEDiffuseLightingElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
 typedef SVGFELightingElement SVGFEDiffuseLightingElementBase;
--- a/dom/svg/SVGFEDisplacementMapElement.cpp
+++ b/dom/svg/SVGFEDisplacementMapElement.cpp
@@ -100,19 +100,19 @@ bool SVGFEDisplacementMapElement::Attrib
          (aNameSpaceID == kNameSpaceID_None &&
           (aAttribute == nsGkAtoms::in || aAttribute == nsGkAtoms::in2 ||
            aAttribute == nsGkAtoms::scale ||
            aAttribute == nsGkAtoms::xChannelSelector ||
            aAttribute == nsGkAtoms::yChannelSelector));
 }
 
 void SVGFEDisplacementMapElement::GetSourceImageNames(
-    nsTArray<nsSVGStringInfo>& aSources) {
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN2], this));
+    nsTArray<SVGStringInfo>& aSources) {
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN1], this));
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN2], this));
 }
 
 //----------------------------------------------------------------------
 // SVGElement methods
 
 SVGElement::NumberAttributesInfo SVGFEDisplacementMapElement::GetNumberInfo() {
   return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
                               ArrayLength(sNumberInfo));
--- a/dom/svg/SVGFEDisplacementMapElement.h
+++ b/dom/svg/SVGFEDisplacementMapElement.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_SVGFEDisplacementMapElement_h
 #define mozilla_dom_SVGFEDisplacementMapElement_h
 
 #include "nsSVGEnum.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 
 nsresult NS_NewSVGFEDisplacementMapElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
 typedef SVGFE SVGFEDisplacementMapElementBase;
@@ -34,18 +34,17 @@ class SVGFEDisplacementMapElement : publ
       nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
       const nsTArray<bool>& aInputsAreTainted,
       nsTArray<RefPtr<SourceSurface>>& aInputImages) override;
   virtual bool AttributeAffectsRendering(int32_t aNameSpaceID,
                                          nsAtom* aAttribute) const override;
   virtual nsSVGString& GetResultImageName() override {
     return mStringAttributes[RESULT];
   }
-  virtual void GetSourceImageNames(
-      nsTArray<nsSVGStringInfo>& aSources) override;
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> In1();
   already_AddRefed<SVGAnimatedString> In2();
   already_AddRefed<SVGAnimatedNumber> Scale();
   already_AddRefed<SVGAnimatedEnumeration> XChannelSelector();
--- a/dom/svg/SVGFEDistantLightElement.h
+++ b/dom/svg/SVGFEDistantLightElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFEDistantLightElement_h
 #define mozilla_dom_SVGFEDistantLightElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGNumber2.h"
 
 nsresult NS_NewSVGFEDistantLightElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/svg/SVGFEDropShadowElement.cpp
+++ b/dom/svg/SVGFEDropShadowElement.cpp
@@ -107,18 +107,18 @@ bool SVGFEDropShadowElement::AttributeAf
                                                                aAttribute) ||
          (aNameSpaceID == kNameSpaceID_None &&
           (aAttribute == nsGkAtoms::in ||
            aAttribute == nsGkAtoms::stdDeviation ||
            aAttribute == nsGkAtoms::dx || aAttribute == nsGkAtoms::dy));
 }
 
 void SVGFEDropShadowElement::GetSourceImageNames(
-    nsTArray<nsSVGStringInfo>& aSources) {
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
+    nsTArray<SVGStringInfo>& aSources) {
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN1], this));
 }
 
 //----------------------------------------------------------------------
 // nsIContent methods
 
 NS_IMETHODIMP_(bool)
 SVGFEDropShadowElement::IsAttributeMapped(const nsAtom* name) const {
   static const MappedAttributeEntry* const map[] = {sFEFloodMap};
--- a/dom/svg/SVGFEDropShadowElement.h
+++ b/dom/svg/SVGFEDropShadowElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFEDropShadowElement_h
 #define mozilla_dom_SVGFEDropShadowElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGNumberPair.h"
 #include "nsSVGString.h"
 
 nsresult NS_NewSVGFEDropShadowElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
@@ -37,18 +37,17 @@ class SVGFEDropShadowElement : public SV
       nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
       const nsTArray<bool>& aInputsAreTainted,
       nsTArray<RefPtr<SourceSurface>>& aInputImages) override;
   virtual bool AttributeAffectsRendering(int32_t aNameSpaceID,
                                          nsAtom* aAttribute) const override;
   virtual nsSVGString& GetResultImageName() override {
     return mStringAttributes[RESULT];
   }
-  virtual void GetSourceImageNames(
-      nsTArray<nsSVGStringInfo>& aSources) override;
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) override;
 
   // nsIContent interface
   NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> In1();
--- a/dom/svg/SVGFEFloodElement.h
+++ b/dom/svg/SVGFEFloodElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFEFloodElement_h
 #define mozilla_dom_SVGFEFloodElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 
 nsresult NS_NewSVGFEFloodElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
 typedef SVGFE SVGFEFloodElementBase;
--- a/dom/svg/SVGFEGaussianBlurElement.cpp
+++ b/dom/svg/SVGFEGaussianBlurElement.cpp
@@ -79,18 +79,18 @@ bool SVGFEGaussianBlurElement::Attribute
   return SVGFEGaussianBlurElementBase::AttributeAffectsRendering(aNameSpaceID,
                                                                  aAttribute) ||
          (aNameSpaceID == kNameSpaceID_None &&
           (aAttribute == nsGkAtoms::in ||
            aAttribute == nsGkAtoms::stdDeviation));
 }
 
 void SVGFEGaussianBlurElement::GetSourceImageNames(
-    nsTArray<nsSVGStringInfo>& aSources) {
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
+    nsTArray<SVGStringInfo>& aSources) {
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN1], this));
 }
 
 //----------------------------------------------------------------------
 // SVGElement methods
 
 SVGElement::NumberPairAttributesInfo
 SVGFEGaussianBlurElement::GetNumberPairInfo() {
   return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo,
--- a/dom/svg/SVGFEGaussianBlurElement.h
+++ b/dom/svg/SVGFEGaussianBlurElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFEGaussianBlurElement_h
 #define mozilla_dom_SVGFEGaussianBlurElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGNumberPair.h"
 #include "nsSVGString.h"
 
 nsresult NS_NewSVGFEGaussianBlurElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
@@ -36,18 +36,17 @@ class SVGFEGaussianBlurElement : public 
       nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
       const nsTArray<bool>& aInputsAreTainted,
       nsTArray<RefPtr<SourceSurface>>& aInputImages) override;
   virtual bool AttributeAffectsRendering(int32_t aNameSpaceID,
                                          nsAtom* aAttribute) const override;
   virtual nsSVGString& GetResultImageName() override {
     return mStringAttributes[RESULT];
   }
-  virtual void GetSourceImageNames(
-      nsTArray<nsSVGStringInfo>& aSources) override;
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> In1();
   already_AddRefed<SVGAnimatedNumber> StdDeviationX();
   already_AddRefed<SVGAnimatedNumber> StdDeviationY();
   void SetStdDeviation(float stdDeviationX, float stdDeviationY);
--- a/dom/svg/SVGFEImageElement.h
+++ b/dom/svg/SVGFEImageElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFEImageElement_h
 #define mozilla_dom_SVGFEImageElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "SVGAnimatedPreserveAspectRatio.h"
 
 class SVGFEImageFrame;
 
 nsresult NS_NewSVGFEImageElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
--- a/dom/svg/SVGFEMergeElement.cpp
+++ b/dom/svg/SVGFEMergeElement.cpp
@@ -27,23 +27,22 @@ NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEMer
 
 FilterPrimitiveDescription SVGFEMergeElement::GetPrimitiveDescription(
     nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
     const nsTArray<bool>& aInputsAreTainted,
     nsTArray<RefPtr<SourceSurface>>& aInputImages) {
   return FilterPrimitiveDescription(AsVariant(MergeAttributes()));
 }
 
-void SVGFEMergeElement::GetSourceImageNames(
-    nsTArray<nsSVGStringInfo>& aSources) {
+void SVGFEMergeElement::GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) {
   for (nsIContent* child = nsINode::GetFirstChild(); child;
        child = child->GetNextSibling()) {
     if (child->IsSVGElement(nsGkAtoms::feMergeNode)) {
       SVGFEMergeNodeElement* node = static_cast<SVGFEMergeNodeElement*>(child);
-      aSources.AppendElement(nsSVGStringInfo(node->GetIn1(), node));
+      aSources.AppendElement(SVGStringInfo(node->GetIn1(), node));
     }
   }
 }
 
 //----------------------------------------------------------------------
 // SVGElement methods
 
 SVGElement::StringAttributesInfo SVGFEMergeElement::GetStringInfo() {
--- a/dom/svg/SVGFEMergeElement.h
+++ b/dom/svg/SVGFEMergeElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFEMergeElement_h
 #define mozilla_dom_SVGFEMergeElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 
 nsresult NS_NewSVGFEMergeElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
 typedef SVGFE SVGFEMergeElementBase;
@@ -32,18 +32,17 @@ class SVGFEMergeElement : public SVGFEMe
  public:
   virtual FilterPrimitiveDescription GetPrimitiveDescription(
       nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
       const nsTArray<bool>& aInputsAreTainted,
       nsTArray<RefPtr<SourceSurface>>& aInputImages) override;
   virtual nsSVGString& GetResultImageName() override {
     return mStringAttributes[RESULT];
   }
-  virtual void GetSourceImageNames(
-      nsTArray<nsSVGStringInfo>& aSources) override;
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) override;
 
   // nsIContent
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
  protected:
   virtual StringAttributesInfo GetStringInfo() override;
 
   enum { RESULT };
--- a/dom/svg/SVGFEMergeNodeElement.h
+++ b/dom/svg/SVGFEMergeNodeElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFEMergeNodeElement_h
 #define mozilla_dom_SVGFEMergeNodeElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 
 nsresult NS_NewSVGFEMergeNodeElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
 typedef SVGFEUnstyledElement SVGFEMergeNodeElementBase;
--- a/dom/svg/SVGFEMorphologyElement.cpp
+++ b/dom/svg/SVGFEMorphologyElement.cpp
@@ -61,18 +61,18 @@ already_AddRefed<SVGAnimatedNumber> SVGF
       nsSVGNumberPair::eSecond, this);
 }
 
 void SVGFEMorphologyElement::SetRadius(float rx, float ry) {
   mNumberPairAttributes[RADIUS].SetBaseValues(rx, ry, this);
 }
 
 void SVGFEMorphologyElement::GetSourceImageNames(
-    nsTArray<nsSVGStringInfo>& aSources) {
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
+    nsTArray<SVGStringInfo>& aSources) {
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN1], this));
 }
 
 #define MORPHOLOGY_EPSILON 0.0001
 
 void SVGFEMorphologyElement::GetRXY(int32_t* aRX, int32_t* aRY,
                                     const nsSVGFilterInstance& aInstance) {
   // Subtract an epsilon here because we don't want a value that's just
   // slightly larger than an integer to round up to the next integer; it's
--- a/dom/svg/SVGFEMorphologyElement.h
+++ b/dom/svg/SVGFEMorphologyElement.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_SVGFEMorphologyElement_h
 #define mozilla_dom_SVGFEMorphologyElement_h
 
 #include "nsSVGEnum.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGNumberPair.h"
 #include "nsSVGString.h"
 
 nsresult NS_NewSVGFEMorphologyElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
@@ -37,18 +37,17 @@ class SVGFEMorphologyElement : public SV
       nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
       const nsTArray<bool>& aInputsAreTainted,
       nsTArray<RefPtr<SourceSurface>>& aInputImages) override;
   virtual bool AttributeAffectsRendering(int32_t aNameSpaceID,
                                          nsAtom* aAttribute) const override;
   virtual nsSVGString& GetResultImageName() override {
     return mStringAttributes[RESULT];
   }
-  virtual void GetSourceImageNames(
-      nsTArray<nsSVGStringInfo>& aSources) override;
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> In1();
   already_AddRefed<SVGAnimatedEnumeration> Operator();
   already_AddRefed<SVGAnimatedNumber> RadiusX();
   already_AddRefed<SVGAnimatedNumber> RadiusY();
--- a/dom/svg/SVGFEOffsetElement.cpp
+++ b/dom/svg/SVGFEOffsetElement.cpp
@@ -64,18 +64,18 @@ bool SVGFEOffsetElement::AttributeAffect
   return SVGFEOffsetElementBase::AttributeAffectsRendering(aNameSpaceID,
                                                            aAttribute) ||
          (aNameSpaceID == kNameSpaceID_None &&
           (aAttribute == nsGkAtoms::in || aAttribute == nsGkAtoms::dx ||
            aAttribute == nsGkAtoms::dy));
 }
 
 void SVGFEOffsetElement::GetSourceImageNames(
-    nsTArray<nsSVGStringInfo>& aSources) {
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
+    nsTArray<SVGStringInfo>& aSources) {
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN1], this));
 }
 
 //----------------------------------------------------------------------
 // SVGElement methods
 
 SVGElement::NumberAttributesInfo SVGFEOffsetElement::GetNumberInfo() {
   return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
                               ArrayLength(sNumberInfo));
--- a/dom/svg/SVGFEOffsetElement.h
+++ b/dom/svg/SVGFEOffsetElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFEOffsetElement_h
 #define mozilla_dom_SVGFEOffsetElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGString.h"
 
 nsresult NS_NewSVGFEOffsetElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
@@ -36,18 +36,17 @@ class SVGFEOffsetElement : public SVGFEO
       nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
       const nsTArray<bool>& aInputsAreTainted,
       nsTArray<RefPtr<SourceSurface>>& aInputImages) override;
   virtual bool AttributeAffectsRendering(int32_t aNameSpaceID,
                                          nsAtom* aAttribute) const override;
   virtual nsSVGString& GetResultImageName() override {
     return mStringAttributes[RESULT];
   }
-  virtual void GetSourceImageNames(
-      nsTArray<nsSVGStringInfo>& aSources) override;
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> In1();
   already_AddRefed<SVGAnimatedNumber> Dx();
   already_AddRefed<SVGAnimatedNumber> Dy();
 
--- a/dom/svg/SVGFEPointLightElement.h
+++ b/dom/svg/SVGFEPointLightElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFEPointLightElement_h
 #define mozilla_dom_SVGFEPointLightElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGNumber2.h"
 
 nsresult NS_NewSVGFEPointLightElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/svg/SVGFESpecularLightingElement.h
+++ b/dom/svg/SVGFESpecularLightingElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFESpecularLightingElement_h
 #define mozilla_dom_SVGFESpecularLightingElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 
 nsresult NS_NewSVGFESpecularLightingElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
 //---------------------SpecularLighting------------------------
--- a/dom/svg/SVGFESpotLightElement.h
+++ b/dom/svg/SVGFESpotLightElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFESpotLightElement_h
 #define mozilla_dom_SVGFESpotLightElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGNumber2.h"
 
 nsresult NS_NewSVGFESpotLightElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/svg/SVGFETileElement.cpp
+++ b/dom/svg/SVGFETileElement.cpp
@@ -28,19 +28,18 @@ SVGElement::StringInfo SVGFETileElement:
 // nsINode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFETileElement)
 
 already_AddRefed<SVGAnimatedString> SVGFETileElement::In1() {
   return mStringAttributes[IN1].ToDOMAnimatedString(this);
 }
 
-void SVGFETileElement::GetSourceImageNames(
-    nsTArray<nsSVGStringInfo>& aSources) {
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
+void SVGFETileElement::GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) {
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN1], this));
 }
 
 //----------------------------------------------------------------------
 // SVGElement methods
 
 FilterPrimitiveDescription SVGFETileElement::GetPrimitiveDescription(
     nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
     const nsTArray<bool>& aInputsAreTainted,
--- a/dom/svg/SVGFETileElement.h
+++ b/dom/svg/SVGFETileElement.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_SVGFETileElement_h
 #define mozilla_dom_SVGFETileElement_h
 
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 
 nsresult NS_NewSVGFETileElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
 typedef SVGFE SVGFETileElementBase;
@@ -36,18 +36,17 @@ class SVGFETileElement : public SVGFETil
       nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
       const nsTArray<bool>& aInputsAreTainted,
       nsTArray<RefPtr<SourceSurface>>& aInputImages) override;
   virtual bool AttributeAffectsRendering(int32_t aNameSpaceID,
                                          nsAtom* aAttribute) const override;
   virtual nsSVGString& GetResultImageName() override {
     return mStringAttributes[RESULT];
   }
-  virtual void GetSourceImageNames(
-      nsTArray<nsSVGStringInfo>& aSources) override;
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> In1();
 
  protected:
   virtual StringAttributesInfo GetStringInfo() override;
--- a/dom/svg/SVGFETurbulenceElement.h
+++ b/dom/svg/SVGFETurbulenceElement.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_SVGFETurbulenceElement_h
 #define mozilla_dom_SVGFETurbulenceElement_h
 
 #include "nsSVGEnum.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGInteger.h"
 #include "nsSVGString.h"
 
 nsresult NS_NewSVGFETurbulenceElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
rename from dom/svg/nsSVGFilters.cpp
rename to dom/svg/SVGFilters.cpp
--- a/dom/svg/nsSVGFilters.cpp
+++ b/dom/svg/SVGFilters.cpp
@@ -14,17 +14,17 @@
 #include "nsSVGIntegerPair.h"
 #include "nsSVGBoolean.h"
 #include "nsCOMPtr.h"
 #include "nsSVGFilterInstance.h"
 #include "nsSVGEnum.h"
 #include "SVGNumberList.h"
 #include "SVGAnimatedNumberList.h"
 #include "DOMSVGAnimatedNumberList.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsLayoutUtils.h"
 #include "nsSVGUtils.h"
 #include "mozilla/ComputedStyle.h"
 #include "nsIFrame.h"
 #include "imgIContainer.h"
 #include "mozilla/dom/SVGFilterElement.h"
 #include "nsSVGString.h"
 #include "SVGContentUtils.h"
@@ -70,17 +70,17 @@ NS_IMPL_RELEASE_INHERITED(SVGFE, SVGFEBa
 
 NS_INTERFACE_MAP_BEGIN(SVGFE)
   NS_INTERFACE_MAP_ENTRY_CONCRETE(SVGFE)
 NS_INTERFACE_MAP_END_INHERITING(SVGFEBase)
 
 //----------------------------------------------------------------------
 // Implementation
 
-void SVGFE::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) {}
+void SVGFE::GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) {}
 
 bool SVGFE::OutputIsTainted(const nsTArray<bool>& aInputsAreTainted,
                             nsIPrincipal* aReferencePrincipal) {
   // This is the default implementation for OutputIsTainted.
   // Our output is tainted if we have at least one tainted input.
   for (uint32_t i = 0; i < aInputsAreTainted.Length(); i++) {
     if (aInputsAreTainted[i]) {
       return true;
@@ -393,18 +393,18 @@ SVGFELightingElement::IsAttributeMapped(
   static const MappedAttributeEntry* const map[] = {sColorMap,
                                                     sLightingEffectsMap};
 
   return FindAttributeDependence(name, map) ||
          SVGFELightingElementBase::IsAttributeMapped(name);
 }
 
 void SVGFELightingElement::GetSourceImageNames(
-    nsTArray<nsSVGStringInfo>& aSources) {
-  aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
+    nsTArray<SVGStringInfo>& aSources) {
+  aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN1], this));
 }
 
 LightType SVGFELightingElement::ComputeLightAttributes(
     nsSVGFilterInstance* aInstance, nsTArray<float>& aFloatAttributes) {
   // find specified light
   for (nsCOMPtr<nsIContent> child = nsINode::GetFirstChild(); child;
        child = child->GetNextSibling()) {
     if (child->IsAnyOfSVGElements(nsGkAtoms::feDistantLight,
rename from dom/svg/nsSVGFilters.h
rename to dom/svg/SVGFilters.h
--- a/dom/svg/nsSVGFilters.h
+++ b/dom/svg/SVGFilters.h
@@ -14,28 +14,27 @@
 #include "SVGElement.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGNumberPair.h"
 #include "FilterSupport.h"
 
 class nsSVGFilterInstance;
 class nsSVGNumberPair;
 
-struct nsSVGStringInfo {
-  nsSVGStringInfo(const nsSVGString* aString,
-                  mozilla::dom::SVGElement* aElement)
+namespace mozilla {
+namespace dom {
+
+struct SVGStringInfo {
+  SVGStringInfo(const nsSVGString* aString, SVGElement* aElement)
       : mString(aString), mElement(aElement) {}
 
   const nsSVGString* mString;
-  mozilla::dom::SVGElement* mElement;
+  SVGElement* mElement;
 };
 
-namespace mozilla {
-namespace dom {
-
 typedef SVGElement SVGFEBase;
 
 #define NS_SVG_FE_CID                                \
   {                                                  \
     0x60483958, 0xd229, 0x4a77, {                    \
       0x96, 0xb2, 0x62, 0x3e, 0x69, 0x95, 0x1e, 0x0e \
     }                                                \
   }
@@ -95,17 +94,17 @@ class SVGFE : public SVGFEBase {
 
   bool IsNodeOfType(uint32_t aFlags) const override {
     return !(aFlags & ~eFILTER);
   }
 
   virtual nsSVGString& GetResultImageName() = 0;
   // Return a list of all image names used as sources. Default is to
   // return no sources.
-  virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources);
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources);
 
   virtual FilterPrimitiveDescription GetPrimitiveDescription(
       nsSVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
       const nsTArray<bool>& aInputsAreTainted,
       nsTArray<RefPtr<SourceSurface>>& aInputImages) = 0;
 
   // returns true if changes to the attribute should cause us to
   // repaint the filter
@@ -192,18 +191,17 @@ class SVGFELightingElement : public SVGF
   NS_INLINE_DECL_REFCOUNTING_INHERITED(SVGFELightingElement,
                                        SVGFELightingElementBase)
 
   virtual bool AttributeAffectsRendering(int32_t aNameSpaceID,
                                          nsAtom* aAttribute) const override;
   virtual nsSVGString& GetResultImageName() override {
     return mStringAttributes[RESULT];
   }
-  virtual void GetSourceImageNames(
-      nsTArray<nsSVGStringInfo>& aSources) override;
+  virtual void GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) override;
   NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
 
  protected:
   virtual bool OperatesOnSRGB(int32_t aInputIndex,
                               bool aInputIsAlreadySRGB) override {
     return true;
   }
 
--- a/dom/svg/moz.build
+++ b/dom/svg/moz.build
@@ -125,17 +125,16 @@ UNIFIED_SOURCES += [
     'nsISVGPoint.cpp',
     'nsSVGAngle.cpp',
     'nsSVGAnimatedTransformList.cpp',
     'nsSVGBoolean.cpp',
     'nsSVGClass.cpp',
     'nsSVGDataParser.cpp',
     'nsSVGEnum.cpp',
     'nsSVGFeatures.cpp',
-    'nsSVGFilters.cpp',
     'nsSVGInteger.cpp',
     'nsSVGIntegerPair.cpp',
     'nsSVGLength2.cpp',
     'nsSVGNumber2.cpp',
     'nsSVGNumberPair.cpp',
     'nsSVGPathDataParser.cpp',
     'nsSVGString.cpp',
     'nsSVGTransform.cpp',
@@ -187,16 +186,17 @@ UNIFIED_SOURCES += [
     'SVGFEMorphologyElement.cpp',
     'SVGFEOffsetElement.cpp',
     'SVGFEPointLightElement.cpp',
     'SVGFESpecularLightingElement.cpp',
     'SVGFESpotLightElement.cpp',
     'SVGFETileElement.cpp',
     'SVGFETurbulenceElement.cpp',
     'SVGFilterElement.cpp',
+    'SVGFilters.cpp',
     'SVGForeignObjectElement.cpp',
     'SVGFragmentIdentifier.cpp',
     'SVGGElement.cpp',
     'SVGGeometryElement.cpp',
     'SVGGradientElement.cpp',
     'SVGGraphicsElement.cpp',
     'SVGImageElement.cpp',
     'SVGIntegerPairSMILType.cpp',
--- a/dom/svg/nsSVGAngle.cpp
+++ b/dom/svg/nsSVGAngle.cpp
@@ -17,84 +17,84 @@
 #include "SVGAnimatedAngle.h"
 #include "SVGOrientSMILType.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::SVGAngle_Binding;
 using namespace mozilla::dom::SVGMarkerElement_Binding;
 
-static const nsStaticAtom* const unitMap[] = {
+static const nsStaticAtom* const angleUnitMap[] = {
     nullptr, /* SVG_ANGLETYPE_UNKNOWN */
     nullptr, /* SVG_ANGLETYPE_UNSPECIFIED */
     nsGkAtoms::deg, nsGkAtoms::rad, nsGkAtoms::grad};
 
 static nsSVGAttrTearoffTable<nsSVGAngle, SVGAnimatedAngle>
     sSVGAnimatedAngleTearoffTable;
 static nsSVGAttrTearoffTable<nsSVGAngle, SVGAngle> sBaseSVGAngleTearoffTable;
 static nsSVGAttrTearoffTable<nsSVGAngle, SVGAngle> sAnimSVGAngleTearoffTable;
 
 /* Helper functions */
 
-static bool IsValidUnitType(uint16_t unit) {
+static bool IsValidAngleUnitType(uint16_t unit) {
   if (unit > SVG_ANGLETYPE_UNKNOWN && unit <= SVG_ANGLETYPE_GRAD) return true;
 
   return false;
 }
 
-static void GetUnitString(nsAString& unit, uint16_t unitType) {
-  if (IsValidUnitType(unitType)) {
-    if (unitMap[unitType]) {
-      unitMap[unitType]->ToString(unit);
+static void GetAngleUnitString(nsAString& unit, uint16_t unitType) {
+  if (IsValidAngleUnitType(unitType)) {
+    if (angleUnitMap[unitType]) {
+      angleUnitMap[unitType]->ToString(unit);
     }
     return;
   }
 
   MOZ_ASSERT_UNREACHABLE("Unknown unit type");
 }
 
-static uint16_t GetUnitTypeForString(const nsAString& unitStr) {
+static uint16_t GetAngleUnitTypeForString(const nsAString& unitStr) {
   if (unitStr.IsEmpty()) return SVG_ANGLETYPE_UNSPECIFIED;
 
   nsStaticAtom* unitAtom = NS_GetStaticAtom(unitStr);
 
   if (unitAtom) {
-    for (uint32_t i = 0; i < ArrayLength(unitMap); i++) {
-      if (unitMap[i] == unitAtom) {
+    for (uint32_t i = 0; i < ArrayLength(angleUnitMap); i++) {
+      if (angleUnitMap[i] == unitAtom) {
         return i;
       }
     }
   }
 
   return SVG_ANGLETYPE_UNKNOWN;
 }
 
-static void GetValueString(nsAString& aValueAsString, float aValue,
-                           uint16_t aUnitType) {
+static void GetAngleValueString(nsAString& aValueAsString, float aValue,
+                                uint16_t aUnitType) {
   nsTextFormatter::ssprintf(aValueAsString, u"%g", (double)aValue);
 
   nsAutoString unitString;
-  GetUnitString(unitString, aUnitType);
+  GetAngleUnitString(unitString, aUnitType);
   aValueAsString.Append(unitString);
 }
 
 /* static */ bool nsSVGAngle::GetValueFromString(const nsAString& aString,
                                                  float& aValue,
                                                  uint16_t* aUnitType) {
   RangedPtr<const char16_t> iter = SVGContentUtils::GetStartRangedPtr(aString);
   const RangedPtr<const char16_t> end =
       SVGContentUtils::GetEndRangedPtr(aString);
 
   if (!SVGContentUtils::ParseNumber(iter, end, aValue)) {
     return false;
   }
 
   const nsAString& units = Substring(iter.get(), end.get());
-  *aUnitType = GetUnitTypeForString(units);
-  return IsValidUnitType(*aUnitType);
+  *aUnitType = GetAngleUnitTypeForString(units);
+  return IsValidAngleUnitType(*aUnitType);
 }
 
 /* static */ float nsSVGAngle::GetDegreesPerUnit(uint8_t aUnit) {
   switch (aUnit) {
     case SVG_ANGLETYPE_UNSPECIFIED:
     case SVG_ANGLETYPE_DEG:
       return 1;
     case SVG_ANGLETYPE_RAD:
@@ -120,17 +120,17 @@ void nsSVGAngle::SetBaseValueInSpecified
   } else {
     aSVGElement->AnimationNeedsResample();
   }
   aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
 }
 
 nsresult nsSVGAngle::ConvertToSpecifiedUnits(uint16_t unitType,
                                              SVGElement* aSVGElement) {
-  if (!IsValidUnitType(unitType)) return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+  if (!IsValidAngleUnitType(unitType)) return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 
   if (mBaseValUnit == uint8_t(unitType)) return NS_OK;
 
   nsAttrValue emptyOrOldValue;
   if (aSVGElement) {
     emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
   }
 
@@ -146,17 +146,17 @@ nsresult nsSVGAngle::ConvertToSpecifiedU
   return NS_OK;
 }
 
 nsresult nsSVGAngle::NewValueSpecifiedUnits(uint16_t unitType,
                                             float valueInSpecifiedUnits,
                                             SVGElement* aSVGElement) {
   NS_ENSURE_FINITE(valueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE);
 
-  if (!IsValidUnitType(unitType)) return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+  if (!IsValidAngleUnitType(unitType)) return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 
   if (mBaseVal == valueInSpecifiedUnits && mBaseValUnit == uint8_t(unitType))
     return NS_OK;
 
   nsAttrValue emptyOrOldValue;
   if (aSVGElement) {
     emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
   }
@@ -234,21 +234,21 @@ nsresult nsSVGAngle::SetBaseValueString(
 
   if (aDoSetAttr) {
     aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
   }
   return NS_OK;
 }
 
 void nsSVGAngle::GetBaseValueString(nsAString& aValueAsString) const {
-  GetValueString(aValueAsString, mBaseVal, mBaseValUnit);
+  GetAngleValueString(aValueAsString, mBaseVal, mBaseValUnit);
 }
 
 void nsSVGAngle::GetAnimValueString(nsAString& aValueAsString) const {
-  GetValueString(aValueAsString, mAnimVal, mAnimValUnit);
+  GetAngleValueString(aValueAsString, mAnimVal, mAnimValUnit);
 }
 
 void nsSVGAngle::SetBaseValue(float aValue, uint8_t aUnit,
                               SVGElement* aSVGElement, bool aDoSetAttr) {
   float valueInSpecifiedUnits = aValue / GetDegreesPerUnit(aUnit);
   if (aUnit == mBaseValUnit && mBaseVal == valueInSpecifiedUnits) {
     return;
   }
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -989,17 +989,17 @@ gfxFont* gfxFontconfigFontEntry::CreateF
       return nullptr;
     }
   }
 
   RefPtr<UnscaledFontFontconfig> unscaledFont =
       mUnscaledFontCache.Lookup(ToCharPtr(file), index);
   if (!unscaledFont) {
     unscaledFont = mFontData
-                       ? new UnscaledFontFontconfig(face)
+                       ? new UnscaledFontFontconfig(mFTFace)
                        : new UnscaledFontFontconfig(ToCharPtr(file), index);
     mUnscaledFontCache.Add(unscaledFont);
   }
 
   gfxFont* newFont = new gfxFontconfigFont(
       unscaledFont, scaledFont, renderPattern, size, this, aFontStyle);
   cairo_scaled_font_destroy(scaledFont);
 
--- a/js/xpconnect/loader/ScriptPreloader.h
+++ b/js/xpconnect/loader/ScriptPreloader.h
@@ -468,17 +468,20 @@ class ScriptPreloader : public nsIObserv
 
   RefPtr<ScriptPreloader> mChildCache;
   ScriptCacheChild* mChildActor = nullptr;
 
   nsString mBaseName;
   nsCString mContentStartupFinishedTopic;
 
   nsCOMPtr<nsIFile> mProfD;
-  nsCOMPtr<nsIThread> mSaveThread;
+  // Note: We use a RefPtr rather than an nsCOMPtr here because the
+  // AssertNoQueryNeeded checks done by getter_AddRefs happen at a time that
+  // violate data access invariants.
+  RefPtr<nsIThread> mSaveThread;
   nsCOMPtr<nsITimer> mSaveTimer;
 
   // The mmapped cache data from this session's cache file.
   AutoMemMap mCacheData;
 
   Monitor mMonitor;
   Monitor mSaveMonitor;
 };
--- a/layout/svg/SVGFEContainerFrame.cpp
+++ b/layout/svg/SVGFEContainerFrame.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Keep in (case-insensitive) order:
 #include "nsContainerFrame.h"
 #include "nsGkAtoms.h"
 #include "nsIFrame.h"
 #include "nsLiteralString.h"
 #include "SVGObserverUtils.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 
 using namespace mozilla;
 
 /*
  * This frame is used by filter primitive elements that
  * have special child elements that provide parameters.
  */
 class SVGFEContainerFrame final : public nsContainerFrame {
--- a/layout/svg/SVGFEImageFrame.cpp
+++ b/layout/svg/SVGFEImageFrame.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Keep in (case-insensitive) order:
 #include "nsContainerFrame.h"
 #include "nsFrame.h"
 #include "nsGkAtoms.h"
 #include "nsLiteralString.h"
 #include "SVGObserverUtils.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "mozilla/dom/SVGFEImageElement.h"
 #include "mozilla/dom/MutationEventBinding.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 class SVGFEImageFrame final : public nsFrame {
   friend nsIFrame* NS_NewSVGFEImageFrame(nsIPresShell* aPresShell,
--- a/layout/svg/SVGFELeafFrame.cpp
+++ b/layout/svg/SVGFELeafFrame.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Keep in (case-insensitive) order:
 #include "ComputedStyle.h"
 #include "nsContainerFrame.h"
 #include "nsFrame.h"
 #include "nsGkAtoms.h"
 #include "SVGObserverUtils.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 
 using namespace mozilla;
 
 /*
  * This frame is used by filter primitive elements that don't
  * have special child elements that provide parameters.
  */
 class SVGFELeafFrame final : public nsFrame {
--- a/layout/svg/SVGFEUnstyledLeafFrame.cpp
+++ b/layout/svg/SVGFEUnstyledLeafFrame.cpp
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Keep in (case-insensitive) order:
 #include "nsContainerFrame.h"
 #include "nsFrame.h"
 #include "nsGkAtoms.h"
 #include "SVGObserverUtils.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 
 using namespace mozilla;
 
 class SVGFEUnstyledLeafFrame final : public nsFrame {
   friend nsIFrame* NS_NewSVGFEUnstyledLeafFrame(nsIPresShell* aPresShell,
                                                 ComputedStyle* aStyle);
 
  protected:
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -10,17 +10,17 @@
 #include "gfxMatrix.h"
 #include "gfxPoint.h"
 #include "gfxRect.h"
 #include "nsCOMPtr.h"
 #include "nsHashKeys.h"
 #include "nsPoint.h"
 #include "nsRect.h"
 #include "nsSize.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGNumberPair.h"
 #include "nsTArray.h"
 #include "nsIFrame.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 class gfxContext;
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsSVGFilterInstance.cpp
@@ -314,17 +314,17 @@ int32_t nsSVGFilterInstance::GetOrCreate
   return mSourceAlphaIndex;
 }
 
 nsresult nsSVGFilterInstance::GetSourceIndices(
     SVGFE* aPrimitiveElement,
     nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
     const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable,
     nsTArray<int32_t>& aSourceIndices) {
-  AutoTArray<nsSVGStringInfo, 2> sources;
+  AutoTArray<SVGStringInfo, 2> sources;
   aPrimitiveElement->GetSourceImageNames(sources);
 
   for (uint32_t j = 0; j < sources.Length(); j++) {
     nsAutoString str;
     sources[j].mString->GetAnimValue(str, sources[j].mElement);
 
     int32_t sourceIndex = 0;
     if (str.EqualsLiteral("SourceGraphic")) {
--- a/layout/svg/nsSVGFilterInstance.h
+++ b/layout/svg/nsSVGFilterInstance.h
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __NS_SVGFILTERINSTANCE_H__
 #define __NS_SVGFILTERINSTANCE_H__
 
 #include "gfxMatrix.h"
 #include "gfxRect.h"
-#include "nsSVGFilters.h"
+#include "SVGFilters.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGNumberPair.h"
 #include "nsTArray.h"
 
 class nsSVGFilterFrame;
 struct nsStyleFilter;
 
 namespace mozilla {
--- a/layout/svg/nsSVGSymbolFrame.cpp
+++ b/layout/svg/nsSVGSymbolFrame.cpp
@@ -26,9 +26,9 @@ NS_QUERYFRAME_TAIL_INHERITING(nsSVGViewp
 #ifdef DEBUG
 void nsSVGSymbolFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
                             nsIFrame* aPrevInFlow) {
   NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::symbol),
                "Content is not an SVG 'symbol' element!");
 
   nsSVGViewportFrame::Init(aContent, aParent, aPrevInFlow);
 }
-#endif /* DEBUG */
\ No newline at end of file
+#endif /* DEBUG */
--- a/layout/svg/nsSVGSymbolFrame.h
+++ b/layout/svg/nsSVGSymbolFrame.h
@@ -28,9 +28,9 @@ class nsSVGSymbolFrame final : public ns
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override {
     return MakeFrameName(NS_LITERAL_STRING("SVGSymbol"), aResult);
   }
 #endif
 };
 
-#endif
\ No newline at end of file
+#endif
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -1993,18 +1993,18 @@ nsresult nsCookieService::GetCookieStrin
   bool firstPartyStorageAccessGranted = false;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->GetIsTrackingResource();
 
     // Check first-party storage access even for non-tracking resources, since
     // we will need the result when computing the access rights for the reject
     // foreign cookie behavior mode.
-    if (isForeign && AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
-                         httpChannel, aHostURI, nullptr)) {
+    if (AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
+            httpChannel, aHostURI, nullptr)) {
       firstPartyStorageAccessGranted = true;
     }
   }
 
   OriginAttributes attrs;
   if (aChannel) {
     NS_GetOriginAttributes(aChannel, attrs);
   }
@@ -2094,18 +2094,18 @@ nsresult nsCookieService::SetCookieStrin
   bool firstPartyStorageAccessGranted = false;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->GetIsTrackingResource();
 
     // Check first-party storage access even for non-tracking resources, since
     // we will need the result when computing the access rights for the reject
     // foreign cookie behavior mode.
-    if (isForeign && AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
-                         httpChannel, aHostURI, nullptr)) {
+    if (AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
+            httpChannel, aHostURI, nullptr)) {
       firstPartyStorageAccessGranted = true;
     }
   }
 
   OriginAttributes attrs;
   if (aChannel) {
     NS_GetOriginAttributes(aChannel, attrs);
   }
--- a/taskcluster/ci/config.yml
+++ b/taskcluster/ci/config.yml
@@ -27,16 +27,18 @@ treeherder:
         'R-e10s': 'Reftests with e10s'
         'R-sw-e10s': 'Reftests with serviceworker redesign and e10s'
         'Rap': 'Raptor performance tests on Firefox'
         'Rap-e10s': 'Raptor performance tests on Firefox with e10s'
         'Rap-Prof': 'Raptor performance tests on Firefox with Gecko Profiling'
         'Rap-Prof-e10s': 'Raptor performance tests on Firefox with Gecko Profiling'
         'Rap-C': 'Raptor performance tests on Google Chrome'
         'Rap-C-e10s': 'Raptor performance tests on Google Chrome with e10s'
+        'Rap-P': 'Raptor power tests on Firefox'
+        'Rap-P-e10s': 'Raptor power tests on Firefox with e10s'
         'T': 'Talos performance tests'
         'Tsd': 'Talos performance tests with Stylo disabled'
         'Tss': 'Talos performance tests with Stylo sequential'
         'T-e10s': 'Talos performance tests with e10s'
         'Tsd-e10s': 'Talos performance tests with e10s, Stylo disabled'
         'Tss-e10s': 'Talos performance tests with e10s, Stylo sequential'
         'tt': 'Telemetry tests'
         'tt-e10s': 'Telemetry tests with e10s'
--- a/taskcluster/ci/test/raptor.yml
+++ b/taskcluster/ci/test/raptor.yml
@@ -326,16 +326,34 @@ raptor-speedometer-geckoview:
             android-hw.*: built-projects
     max-run-time: 900
     mozharness:
         extra-options:
             - --test=raptor-speedometer
             - --app=geckoview
             - --binary=org.mozilla.geckoview_example
 
+raptor-speedometer-geckoview-power:
+    description: "Raptor Speedometer Power on Geckoview"
+    try-name: raptor-speedometer-geckoview-power
+    treeherder-symbol: Rap-P(sp)
+    target: geckoview_example.apk
+    run-on-projects:
+        by-test-platform:
+            android-hw.*: ['try']
+    max-run-time: 1800
+    mozharness:
+        extra-options:
+            - --test=raptor-speedometer
+            - --app=geckoview
+            - --binary=org.mozilla.geckoview_example
+            - --power-test
+            - --page-cycles 5
+            - --host HOST_IP
+
 raptor-speedometer-chrome:
     description: "Raptor Speedometer on Chrome"
     try-name: raptor-speedometer-chrome
     treeherder-symbol: Rap-C(sp)
     run-on-projects: ['try', 'mozilla-central']
     tier: 2
     max-run-time: 1500
     mozharness:
--- a/taskcluster/ci/test/test-platforms.yml
+++ b/taskcluster/ci/test/test-platforms.yml
@@ -335,29 +335,32 @@ android-em-7.0-x86/opt:
 # android-hw test platforms execute on real devices attached to Autophone hosts.
 
 # android-hw-g5-7-0 Motorola Moto G5 Android 7.0
 
 android-hw-g5-7-0-arm7-api-16/opt:
     build-platform: android-api-16/opt
     test-sets:
         - android-hw-arm7-raptor
+        - android-hw-arm7-raptor-power
         - raptor-fetch-geckoview
 
 # android-hw-p2-8-0 Google Pixel 2 Android 8.0
 
 android-hw-p2-8-0-arm7-api-16/opt:
     build-platform: android-api-16/opt
     test-sets:
         - android-hw-arm7-opt-unittests
         - android-hw-arm7-raptor
+        - android-hw-arm7-raptor-power
         - raptor-fetch-geckoview
 
 android-hw-p2-8-0-arm7-api-16/debug:
     build-platform: android-api-16/debug
     test-sets:
         - android-hw-arm7-debug-unittests
 
 android-hw-p2-8-0-android-aarch64/opt:
     build-platform: android-aarch64/opt
     test-sets:
         - android-hw-aarch64-opt-unittests
         - android-hw-aarch64-raptor
+        - android-hw-aarch64-raptor-power
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -403,8 +403,14 @@ android-hw-aarch64-opt-unittests:
     - jittest
 
 # Coming soonish!
 android-hw-arm7-raptor:
     - raptor-speedometer-geckoview
 
 android-hw-aarch64-raptor:
     - raptor-speedometer-geckoview
+
+android-hw-arm7-raptor-power:
+    - raptor-speedometer-geckoview-power
+
+android-hw-aarch64-raptor-power:
+    - raptor-speedometer-geckoview-power
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -1044,25 +1044,29 @@ def set_worker_type(config, tests):
             else:
                 # the other jobs run on a vm which may or may not be a win10 vm
                 win_worker_type_platform = WINDOWS_WORKER_TYPES[
                     test_platform.split('/')[0]
                 ]
             # now we have the right platform set the worker type accordingly
             test['worker-type'] = win_worker_type_platform[test['virtualization']]
         elif test_platform.startswith('android-hw-g5'):
-            if test['suite'] == 'raptor':
+            if test['suite'] != 'raptor':
+                test['worker-type'] = 'proj-autophone/gecko-t-ap-unit-g5'
+            elif '--power-test' in test['mozharness']['extra-options']:
+                test['worker-type'] = 'proj-autophone/gecko-t-ap-batt-g5'
+            else:
                 test['worker-type'] = 'proj-autophone/gecko-t-ap-perf-g5'
-            else:
-                test['worker-type'] = 'proj-autophone/gecko-t-ap-unit-g5'
         elif test_platform.startswith('android-hw-p2'):
-            if test['suite'] == 'raptor':
+            if test['suite'] != 'raptor':
+                test['worker-type'] = 'proj-autophone/gecko-t-ap-unit-p2'
+            elif '--power-test' in test['mozharness']['extra-options']:
+                test['worker-type'] = 'proj-autophone/gecko-t-ap-batt-p2'
+            else:
                 test['worker-type'] = 'proj-autophone/gecko-t-ap-perf-p2'
-            else:
-                test['worker-type'] = 'proj-autophone/gecko-t-ap-unit-p2'
         elif test_platform.startswith('android-em-7.0-x86'):
             test['worker-type'] = 'terraform-packet/gecko-t-linux'
         elif test_platform.startswith('linux') or test_platform.startswith('android'):
             if test.get('suite', '') in ['talos', 'raptor'] and \
                  not test['build-platform'].startswith('linux64-ccov'):
                 test['worker-type'] = 'releng-hardware/gecko-t-linux-talos'
             else:
                 test['worker-type'] = LINUX_WORKER_TYPES[test['instance-size']]
--- a/taskcluster/taskgraph/try_option_syntax.py
+++ b/taskcluster/taskgraph/try_option_syntax.py
@@ -548,16 +548,20 @@ class TryOptionSyntax(object):
             if attr('nightly') and not self.include_nightly:
                 return False
             return set(['try', 'all']) & set(attr('run_on_projects', []))
 
         # Don't schedule code coverage when try option syntax is used
         if 'ccov' in attr('build_platform', []):
             return False
 
+        # Don't schedule android-hw tests when try option syntax is used
+        if 'android-hw' in task.label:
+            return False
+
         def match_test(try_spec, attr_name):
             run_by_default = True
             if attr('build_type') not in self.build_types:
                 return False
             if self.platforms is not None:
                 if attr('build_platform') not in self.platforms:
                     return False
             else:
--- a/taskcluster/taskgraph/util/workertypes.py
+++ b/taskcluster/taskgraph/util/workertypes.py
@@ -44,18 +44,20 @@ WORKER_TYPES = {
     'scriptworker-prov-v1/pushapk-v1': ('push-apk', None),
     "scriptworker-prov-v1/signing-linux-v1": ('scriptworker-signing', None),
     "scriptworker-prov-v1/shipit": ('shipit', None),
     "scriptworker-prov-v1/shipit-dev": ('shipit', None),
     "scriptworker-prov-v1/treescript-v1": ('treescript', None),
     'releng-hardware/gecko-t-osx-1010': ('generic-worker', 'macosx'),
     'proj-autophone/gecko-t-ap-perf-g5': ('script-engine-autophone', 'linux'),
     'proj-autophone/gecko-t-ap-unit-g5': ('script-engine-autophone', 'linux'),
+    'proj-autophone/gecko-t-ap-batt-g5': ('script-engine-autophone', 'linux'),
     'proj-autophone/gecko-t-ap-perf-p2': ('script-engine-autophone', 'linux'),
     'proj-autophone/gecko-t-ap-unit-p2': ('script-engine-autophone', 'linux'),
+    'proj-autophone/gecko-t-ap-batt-p2': ('script-engine-autophone', 'linux'),
     'terraform-packet/gecko-t-linux': ('docker-worker', 'linux'),
 }
 
 
 def worker_type_implementation(worker_type):
     """Get the worker implementation and OS for the given workerType, where the
     OS represents the host system, not the target OS, in the case of
     cross-compiles."""
--- a/testing/mozharness/mozharness/mozilla/testing/raptor.py
+++ b/testing/mozharness/mozharness/mozilla/testing/raptor.py
@@ -120,22 +120,25 @@ class Raptor(TestingMixin, MercurialScri
         }],
         [["--page-timeout"], {
             "dest": "page_timeout",
             "type": "int",
             "help": "How long to wait (ms) for one page_cycle to complete, before timing out"
         }],
         [["--host"], {
             "dest": "host",
-            "help": "Hostname from which to serve urls (default: 127.0.0.1).",
+            "help": "Hostname from which to serve urls (default: 127.0.0.1). "
+                    "The value HOST_IP will cause the value of host to be "
+                    "to be loaded from the environment variable HOST_IP.",
         }],
         [["--power-test"], {
             "dest": "power_test",
-            "help": "Use Raptor to measure power usage. Currently supported only when "
-                    "--host specified for geckoview.",
+            "help": "Use Raptor to measure power usage. Currently only supported for Geckoview. "
+                    "The host ip address must be specified either via the --host command line "
+                    "argument.",
         }],
         [["--debug-mode"], {
             "dest": "debug_mode",
             "action": "store_true",
             "default": False,
             "help": "Run Raptor in debug mode (open browser console, limited page-cycles, etc.)",
         }],
 
@@ -196,16 +199,18 @@ class Raptor(TestingMixin, MercurialScri
         self.obj_path = self.config.get("obj_path")
         self.test = None
         self.gecko_profile = self.config.get('gecko_profile') or \
             "--geckoProfile" in self.config.get("raptor_cmd_line_args", [])
         self.gecko_profile_interval = self.config.get('gecko_profile_interval')
         self.gecko_profile_entries = self.config.get('gecko_profile_entries')
         self.test_packages_url = self.config.get('test_packages_url')
         self.host = self.config.get('host')
+        if self.host == 'HOST_IP':
+            self.host = os.environ['HOST_IP']
         self.power_test = self.config.get('power_test')
         self.is_release_build = self.config.get('is_release_build')
         self.debug_mode = self.config.get('debug_mode', False)
 
     # We accept some configuration options from the try commit message in the
     # format mozharness: <options>. Example try commit message: mozharness:
     # --geckoProfile try: <stuff>
     def query_gecko_profile_options(self):
@@ -340,16 +345,18 @@ class Raptor(TestingMixin, MercurialScri
         # options overwritten from **kw
         if 'test' in self.config:
             kw_options['test'] = self.config['test']
         if self.symbols_path:
             kw_options['symbolsPath'] = self.symbols_path
         if self.config.get('obj_path', None) is not None:
             kw_options['obj-path'] = self.config['obj_path']
         kw_options.update(kw)
+        if self.host:
+            kw_options['host'] = self.host
         # configure profiling options
         options.extend(self.query_gecko_profile_options())
         # extra arguments
         if args is not None:
             options += args
         if self.config.get('run_local', False):
             options.extend(['--run-local'])
         if 'raptor_cmd_line_args' in self.config:
@@ -463,19 +470,22 @@ class Raptor(TestingMixin, MercurialScri
                                                   'requirements.txt')
             self.info("installing requirements for the view-gecko-profile tool")
             self.install_module(requirements=[view_gecko_profile_req])
 
     def _validate_treeherder_data(self, parser):
         # late import is required, because install is done in create_virtualenv
         import jsonschema
 
-        if len(parser.found_perf_data) != 1:
-            self.critical("PERFHERDER_DATA was seen %d times, expected 1."
-                          % len(parser.found_perf_data))
+        expected_perfherder = 1
+        if self.config.get('power_test', None):
+            expected_perfherder += 1
+        if len(parser.found_perf_data) != expected_perfherder:
+            self.critical("PERFHERDER_DATA was seen %d times, expected %d."
+                          % (len(parser.found_perf_data), expected_perfherder))
             return
 
         schema_path = os.path.join(external_tools_path,
                                    'performance-artifact-schema.json')
         self.info("Validating PERFHERDER_DATA against %s" % schema_path)
         try:
             with open(schema_path) as f:
                 schema = json.load(f)
--- a/testing/raptor/mach_commands.py
+++ b/testing/raptor/mach_commands.py
@@ -50,16 +50,18 @@ class RaptorRunner(MozbuildObject):
                                              'raptor-in_tree_conf.json')
         self.binary_path = self.get_binary_path() if kwargs['app'] != 'geckoview' else None
         self.virtualenv_script = os.path.join(self.topsrcdir, 'third_party', 'python',
                                               'virtualenv', 'virtualenv.py')
         self.virtualenv_path = os.path.join(self._topobjdir, 'testing',
                                             'raptor-venv')
         self.python_interp = sys.executable
         self.raptor_args = raptor_args
+        if kwargs.get('host', None) == 'HOST_IP':
+            kwargs['host'] = os.environ['HOST_IP']
         self.host = kwargs['host']
         self.power_test = kwargs['power_test']
         self.is_release_build = kwargs['is_release_build']
 
     def setup_benchmarks(self):
         """Make sure benchmarks are linked to the proper location in the objdir.
 
         Benchmarks can either live in-tree or in an external repository. In the latter
@@ -199,17 +201,17 @@ class MachRaptor(MachCommandBase):
                 adbhost.command_output(["connect", device_serial])
                 while len(adbhost.devices()) > 1:
                     raw_input("You must disconnect your device from USB before continuing.")
                 # must reset the environment DEVICE_SERIAL which was set during
                 # verify_android_device to match our new tcpip value.
                 os.environ["DEVICE_SERIAL"] = device_serial
             return raptor.run_test(sys.argv[2:], kwargs)
         except Exception as e:
-            print(str(e))
+            print(repr(e))
             return 1
         finally:
             try:
                 if kwargs['app'] == 'geckoview' and kwargs['power_test']:
                     raw_input("Connect device via usb and press ENTER...")
                     device = ADBAndroid(device=device_serial, verbose=True)
                     device.command_output(["usb"])
                     adbhost.command_output(["disconnect", device_serial])
--- a/testing/raptor/raptor/cmdline.py
+++ b/testing/raptor/raptor/cmdline.py
@@ -16,21 +16,23 @@ def create_parser(mach_interface=False):
     add_arg('-t', '--test', required=True, dest='test',
             help="name of raptor test to run")
     add_arg('--app', default='firefox', dest='app',
             help="name of the application we are testing (default: firefox)",
             choices=['firefox', 'chrome', 'geckoview'])
     add_arg('-b', '--binary', dest='binary',
             help="path to the browser executable that we are testing")
     add_arg('--host', dest='host',
-            help="Hostname from which to serve urls, defaults to 127.0.0.1.",
+            help="Hostname from which to serve urls, defaults to 127.0.0.1. "
+            "The value HOST_IP will cause the value of host to be "
+            "loaded from the environment variable HOST_IP.",
             default='127.0.0.1')
     add_arg('--power-test', dest="power_test", action="store_true",
-            help="Use Raptor to measure power usage. Currently supported only when "
-            "--host specified for geckoview.")
+            help="Use Raptor to measure power usage. Currently supported for Geckoview. "
+            "The host ip address must be specified via the --host command line argument.")
     add_arg('--is-release-build', dest="is_release_build", default=False,
             action='store_true',
             help="Whether the build is a release build which requires work arounds "
             "using MOZ_DISABLE_NONLOCAL_CONNECTIONS to support installing unsigned "
             "webextensions. Defaults to False.")
     add_arg('--geckoProfile', action="store_true", dest="gecko_profile",
             help=argparse.SUPPRESS)
     add_arg('--geckoProfileInterval', dest='gecko_profile_interval', type=float,
@@ -86,16 +88,18 @@ def verify_options(parser, args):
         if args.app != "geckoview" or args.host in ('localhost', '127.0.0.1'):
             parser.error("Power test is only supported when running raptor on Geckoview "
                          "when host is specified!")
 
 
 def parse_args(argv=None):
     parser = create_parser()
     args = parser.parse_args(argv)
+    if args.host == 'HOST_IP':
+        args.host = os.environ['HOST_IP']
     verify_options(parser, args)
     return args
 
 
 class _StopAction(argparse.Action):
     def __init__(self, option_strings, dest=argparse.SUPPRESS,
                  default=argparse.SUPPRESS, help=None):
         super(_StopAction, self).__init__(
--- a/testing/raptor/raptor/power.py
+++ b/testing/raptor/raptor/power.py
@@ -21,16 +21,57 @@ def init_geckoview_power_test(raptor):
     raptor.device.shell_output("settings put system screen_off_timeout 7200000")
     raptor.device.shell_output("dumpsys batterystats --reset")
     raptor.device.shell_output("dumpsys batterystats --enable full-wake-history")
     filepath = os.path.join(upload_dir, 'battery-before.txt')
     with open(filepath, 'w') as output:
         output.write(raptor.device.shell_output("dumpsys battery"))
 
 
+# The batterystats output for Estimated power use differs
+# for Android 7 and Android 8 and later.
+#
+# Android 7
+# Estimated power use (mAh):
+#   Capacity: 2100, Computed drain: 625, actual drain: 1197-1218
+#   Unaccounted: 572 ( )
+#   Uid u0a78: 329 ( cpu=329 )
+#   Screen: 190
+#   Cell standby: 87.6 ( radio=87.6 )
+#   Idle: 4.10
+#   Uid 1000: 1.82 ( cpu=0.537 sensor=1.28 )
+#   Wifi: 0.800 ( cpu=0.310 wifi=0.490 )
+# Android 8
+# Estimated power use (mAh):
+#   Capacity: 2700, Computed drain: 145, actual drain: 135-162
+#   Screen: 68.2 Excluded from smearing
+#   Uid u0a208: 61.7 ( cpu=60.5 wifi=1.28 ) Including smearing: 141 ( screen=67.7 proportional=9. )
+#   Cell standby: 2.49 ( radio=2.49 ) Excluded from smearing
+#   Idle: 1.63 Excluded from smearing
+#   Bluetooth: 0.527 ( cpu=0.00319 bt=0.524 ) Including smearing: 0.574 ( proportional=... )
+#   Wifi: 0.423 ( cpu=0.343 wifi=0.0800 ) Including smearing: 0.461 ( proportional=0.0375 )
+#
+# For Android 8 the cpu, wifi, screen and proportional values from the
+# Uid line for the app. If the test does not run long enough it
+# appears that the screen value from the Uid will be missing but the
+# standalone Screen value is available.
+#
+# For Android 7 only the cpu value is available from the Uid line. We
+# can use the Screen and Wifi values for Android 7 from the Screen
+# and Wifi lines which may include contributions from the system or
+# other apps however it should be useful for spotting changes in power
+# usage.
+#
+# If the energy values from Uid line for Android 8 are available they
+# will be used. If for any reason that the screen or wifi power is
+# missing, the values from the Screen and Wifi lines will be used.
+#
+# If only the cpu energy value is available, then it will be used
+# along with the values from the Screen and Wifi lines.
+
 def finish_geckoview_power_test(raptor):
     upload_dir = os.getenv('MOZ_UPLOAD_DIR')
     if not upload_dir:
         raptor.log.critical("Geckoview power test ignored because MOZ_UPLOAD_DIR was not set")
         return
     # Restore the screen off timeout.
     raptor.device.shell_output(
         "settings put system screen_off_timeout %s" % raptor.screen_off_timeout)
@@ -42,41 +83,79 @@ def finish_geckoview_power_test(raptor):
     filepath = os.path.join(upload_dir, 'batterystats.csv')
     with open(filepath, 'w') as output:
         output.write(raptor.device.shell_output("dumpsys batterystats --checkin"))
     filepath = os.path.join(upload_dir, 'batterystats.txt')
     with open(filepath, 'w') as output:
         batterystats = raptor.device.shell_output("dumpsys batterystats")
         output.write(batterystats)
     raptor.device._verbose = verbose
+    estimated_power = False
     uid = None
-    cpu = wifi = smearing = screen = proportional = 0
-    r_uid = re.compile(r'proc=([^:]+):"%s"' % raptor.config['binary'])
+    total = cpu = wifi = smearing = screen = proportional = 0
+    full_screen = 0
+    full_wifi = 0
+    re_uid = re.compile(r'proc=([^:]+):"%s"' % raptor.config['binary'])
+    re_estimated_power = re.compile(r'\s+Estimated power use [(]mAh[)]')
+    re_proportional = re.compile(r'proportional=([\d.]+)')
+    re_screen = re.compile(r'screen=([\d.]+)')
+    re_full_screen = re.compile(r'\s+Screen:\s+([\d.]+)')
+    re_full_wifi = re.compile(r'\s+Wifi:\s+([\d.]+)')
+    re_power = None
     batterystats = batterystats.split('\n')
     for line in batterystats:
         if uid is None:
-            match = r_uid.search(line)
+            # The proc line containing the uid and app name appears
+            # before the Estimated power line.
+            match = re_uid.search(line)
             if match:
                 uid = match.group(1)
-                r_power = re.compile(
-                    r'\s+Uid %s:\s+[\d.]+ [(] cpu=([\d.]+) wifi=([\d.]+) [)] '
-                    r'Including smearing: ([\d.]+)' % uid)
-        else:
-            match = r_power.match(line)
+                re_power = re.compile(
+                    r'\s+Uid %s:\s+([\d.]+) ([(] cpu=([\d.]+) wifi=([\d.]+) [)] '
+                    r'Including smearing: ([\d.]+))?' % uid)
+                continue
+        if not estimated_power:
+            # Do not attempt to parse data until we have seen
+            # Estimated Power in the output.
+            match = re_estimated_power.match(line)
+            if match:
+                estimated_power = True
+            continue
+        if full_screen == 0:
+            match = re_full_screen.match(line)
+            if match:
+                full_screen = match.group(1)
+                continue
+        if full_wifi == 0:
+            match = re_full_wifi.match(line)
             if match:
-                (cpu, wifi, smearing) = match.groups()
-                r_screen = re.compile(r'screen=([\d.]+)')
-                match = r_screen.search(line)
-                if match:
-                    screen = match.group(1)
-                r_proportional = re.compile(r'proportional=([\d.]+)')
-                match = r_proportional.search(line)
-                if match:
-                    proportional = match.group(1)
-                break
+                full_wifi = match.group(1)
+                continue
+        if re_power:
+            match = re_power.match(line)
+            if match:
+                (total, android8, cpu, wifi, smearing) = match.groups()
+                if android8:
+                    # android8 is not None only if the Uid line
+                    # contained values for cpu and wifi which is
+                    # true only for Android 8+.
+                    match = re_screen.search(line)
+                    if match:
+                        screen = match.group(1)
+                    match = re_proportional.search(line)
+                    if match:
+                        proportional = match.group(1)
+        if full_screen and full_wifi and (cpu and wifi and smearing or total):
+            # Stop parsing batterystats once we have a full set of data.
+            break
+
+    cpu = total if cpu is None else cpu
+    screen = full_screen if screen == 0 else screen
+    wifi = full_wifi if wifi is None else wifi
+
     raptor.log.info('power data for uid: %s, cpu: %s, wifi: %s, screen: %s, proportional: %s' %
                     (uid, cpu, wifi, screen, proportional))
 
     # send power data directly to the control server results handler;
     # so it can be formatted and output for perfherder ingestion
 
     power_data = {'type': 'power',
                   'test': 'raptor-speedometer-geckoview',
--- a/testing/raptor/raptor/raptor.py
+++ b/testing/raptor/raptor/raptor.py
@@ -51,16 +51,19 @@ from power import init_geckoview_power_t
 
 class Raptor(object):
     """Container class for Raptor"""
 
     def __init__(self, app, binary, run_local=False, obj_path=None,
                  gecko_profile=False, gecko_profile_interval=None, gecko_profile_entries=None,
                  symbols_path=None, host=None, power_test=False, is_release_build=False,
                  debug_mode=False):
+        # Override the magic --host HOST_IP with the value of the environment variable.
+        if host == 'HOST_IP':
+            host = os.environ['HOST_IP']
         self.config = {}
         self.config['app'] = app
         self.config['binary'] = binary
         self.config['platform'] = mozinfo.os
         self.config['processor'] = mozinfo.processor
         self.config['run_local'] = run_local
         self.config['obj_path'] = obj_path
         self.config['gecko_profile'] = gecko_profile
--- a/toolkit/components/antitracking/test/browser/browser.ini
+++ b/toolkit/components/antitracking/test/browser/browser.ini
@@ -36,16 +36,17 @@ skip-if = serviceworker_e10s
 [browser_blockingServiceWorkers.js]
 skip-if = (os == "win" && os_version == "6.1" && bits == 32 && !debug) # Bug 1491937
 [browser_blockingSharedWorkers.js]
 skip-if = (os == "win" && os_version == "6.1" && bits == 32 && !debug) # Bug 1491937
 [browser_blockingMessaging.js]
 [browser_blockingNoOpener.js]
 [browser_doublyNestedTracker.js]
 [browser_existingCookiesForSubresources.js]
+[browser_firstPartyCookieRejectionHonoursAllowList.js]
 [browser_imageCache4.js]
 [browser_imageCache4-1.js]
 [browser_imageCache4-2.js]
 [browser_imageCache8.js]
 [browser_onBeforeRequestNotificationForTrackingResources.js]
 [browser_onModifyRequestNotificationForTrackingResources.js]
 [browser_permissionInNormalWindows.js]
 skip-if = serviceworker_e10s
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/browser_firstPartyCookieRejectionHonoursAllowList.js
@@ -0,0 +1,69 @@
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+add_task(async function() {
+  info("Starting subResources test");
+
+  await SpecialPowers.flushPrefEnv();
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["browser.contentblocking.allowlist.annotations.enabled", true],
+    ["browser.contentblocking.allowlist.storage.enabled", true],
+    ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT],
+    ["privacy.trackingprotection.enabled", false],
+    ["privacy.trackingprotection.pbmode.enabled", false],
+    ["privacy.trackingprotection.annotate_channels", true],
+  ]});
+
+  let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
+  gBrowser.selectedTab = tab;
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(browser);
+
+  info("Disabling content blocking for this page");
+  ContentBlocking.disableForCurrentPage();
+
+  // The previous function reloads the browser, so wait for it to load again!
+  await BrowserTestUtils.browserLoaded(browser);
+
+  await ContentTask.spawn(browser, {},
+                          async function(obj) {
+    await new content.Promise(async resolve => {
+      let document = content.document;
+      let window = document.defaultView;
+
+      is(document.cookie, "", "No cookies for me");
+
+      await window.fetch("server.sjs").then(r => r.text()).then(text => {
+        is(text, "cookie-not-present", "We should not have cookies");
+      });
+
+      document.cookie = "name=value";
+      ok(document.cookie.includes("name=value"), "Some cookies for me");
+      ok(document.cookie.includes("foopy=1"), "Some cookies for me");
+
+      await window.fetch("server.sjs").then(r => r.text()).then(text => {
+        is(text, "cookie-present", "We should have cookies");
+      });
+
+      ok(document.cookie.length, "Some Cookies for me");
+
+      resolve();
+    });
+  });
+
+  info("Enabling content blocking for this page");
+  ContentBlocking.enableForCurrentPage();
+
+  // The previous function reloads the browser, so wait for it to load again!
+  await BrowserTestUtils.browserLoaded(browser);
+
+  BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function() {
+  info("Cleaning up.");
+  await new Promise(resolve => {
+    Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve());
+  });
+});
+
--- a/toolkit/content/widgets/tree.xml
+++ b/toolkit/content/widgets/tree.xml
@@ -87,16 +87,21 @@
       <property name="inputField" readonly="true">
         <getter><![CDATA[
           if (!this._inputField)
             this._inputField = document.getAnonymousElementByAttribute(this, "anonid", "input");
           return this._inputField;
         ]]></getter>
       </property>
 
+     <property name="disableKeyNavigation"
+                onget="return this.hasAttribute('disableKeyNavigation');"
+                onset="if (val) this.setAttribute('disableKeyNavigation', 'true');
+                       else this.removeAttribute('disableKeyNavigation'); return val;"/>
+
       <field name="_editingRow">-1</field>
       <field name="_editingColumn">null</field>
 
       <property name="editingRow" readonly="true"
                 onget="return this._editingRow;"/>
       <property name="editingColumn" readonly="true"
                 onget="return this._editingColumn;"/>
 
@@ -859,17 +864,17 @@
 
          if (event.charCode == " ".charCodeAt(0)) {
            var c = this.currentIndex;
            if (!this.view.selection.isSelected(c) ||
                (!this.view.selection.single && event.getModifierState("Accel"))) {
              this.view.selection.toggleSelect(c);
              event.preventDefault();
            }
-         } else if (event.charCode > 0 &&
+         } else if (!this.disableKeyNavigation && event.charCode > 0 &&
                     !event.altKey && !event.getModifierState("Accel") &&
                     !event.metaKey && !event.ctrlKey) {
            var l = this._keyNavigate(event);
            if (l >= 0) {
              this.view.selection.timedSelect(l, this._selectDelay);
              this.treeBoxObject.ensureRowIsVisible(l);
            }
            event.preventDefault();