Bug 1476123 - Update Debugger Frontend v72. r=dwalsh
authorJason Laster <jason.laster.11@gmail.com>
Mon, 16 Jul 2018 18:16:47 -0400
changeset 426935 4b7d9df0259113317240716c5aec281d6e68a8b5
parent 426934 e4d9e6cdd6307bd42b176d3e2f0ba66b7244aa38
child 426936 b7334a57640fc5cde34f2daf268d3685d5fe286a
push id105360
push userjlaster@mozilla.com
push dateTue, 17 Jul 2018 15:28:36 +0000
treeherdermozilla-inbound@4b7d9df02591 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdwalsh
bugs1476123
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1476123 - Update Debugger Frontend v72. r=dwalsh MozReview-Commit-ID: GFb5n8CDtLR
devtools/client/debugger/new/README.mozilla
devtools/client/debugger/new/dist/debugger.css
devtools/client/debugger/new/dist/parser-worker.js
devtools/client/debugger/new/dist/vendors.js
devtools/client/debugger/new/src/actions/expressions.js
devtools/client/debugger/new/src/actions/file-search.js
devtools/client/debugger/new/src/actions/sources/loadSourceText.js
devtools/client/debugger/new/src/components/Editor/Footer.js
devtools/client/debugger/new/src/components/Editor/index.js
devtools/client/debugger/new/src/reducers/pause.js
devtools/client/debugger/new/src/reducers/sources.js
devtools/client/debugger/new/src/utils/editor/source-search.js
devtools/client/debugger/new/src/utils/prefs.js
devtools/client/debugger/new/src/workers/parser/index.js
devtools/client/debugger/new/src/workers/parser/mapBindings.js
devtools/client/debugger/new/src/workers/parser/mapExpression.js
devtools/client/debugger/new/src/workers/parser/moz.build
devtools/client/debugger/new/src/workers/parser/worker.js
devtools/client/debugger/new/test/mochitest/browser.ini
devtools/client/debugger/new/test/mochitest/browser_dbg-console-map-bindings.js
devtools/client/debugger/new/test/mochitest/browser_dbg-preview-source-maps.js
devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemapped-preview.js
devtools/client/debugger/new/test/mochitest/browser_dbg-tabs-pretty-print.js
devtools/client/debugger/new/test/mochitest/examples/doc-strict.html
devtools/client/jar.mn
devtools/client/preferences/debugger.js
devtools/client/themes/images/debugger/breakpoint.svg
--- 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 71
+Version 72
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-70...release-71
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-71...release-72
 
 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.11.0
--- a/devtools/client/debugger/new/dist/debugger.css
+++ b/devtools/client/debugger/new/dist/debugger.css
@@ -2701,16 +2701,48 @@ button.jump-definition {
   left: 0px;
   --editor-footer-height: 24px;
 }
 
 html[dir="rtl"] .editor-mount {
   direction: ltr;
 }
 
+.theme-light {
+  --gutter-hover-background-color: #dde1e4;
+}
+
+.theme-dark {
+  --gutter-hover-background-color: #414141;
+}
+
+:not(.empty-line):not(.new-breakpoint)
+  > .CodeMirror-gutter-wrapper:hover
+  > .CodeMirror-linenumber {
+  height: 13px;
+  color: var(--theme-body-color);
+  /* Add 1px offset to the background to match it
+    with the actual breakpoint image dimensions */
+  background: linear-gradient(to bottom, transparent 1px, var(--gutter-hover-background-color) 0);
+}
+
+:not(.empty-line):not(.new-breakpoint)
+  > .CodeMirror-gutter-wrapper:hover
+  > .CodeMirror-linenumber::after {
+    content: '';
+    position: absolute;
+    top: 1px;
+    height: 12px;
+    width: 9px;
+    background-color: var(--gutter-hover-background-color);
+    mask: url("chrome://devtools/skin/images/debugger/breakpoint.svg") no-repeat;
+    mask-size: auto 12px;
+    mask-position: right;
+  }
+
 .editor-wrapper .breakpoints {
   position: absolute;
   top: 0;
   left: 0;
 }
 
 .function-search {
   max-height: 300px;
@@ -2748,16 +2780,21 @@ html[dir="rtl"] .editor-mount {
   width: 20px;
   padding: 0px 5px;
   margin: 0px 4px;
   border-radius: 5px;
   border-color: blue;
   border: 1px solid #00b6ff;
 }
 
+.editor .breakpoint {
+    position: absolute;
+    right: -2px;
+}
+
 .editor.new-breakpoint.folding-enabled svg {
   right: -16px;
 }
 
 .new-breakpoint.has-condition svg {
   fill: var(--theme-graphs-yellow);
 }
 
--- a/devtools/client/debugger/new/dist/parser-worker.js
+++ b/devtools/client/debugger/new/dist/parser-worker.js
@@ -1938,19 +1938,19 @@ var _findOutOfScopeLocations2 = _interop
 var _steps = __webpack_require__(1625);
 
 var _validate = __webpack_require__(1629);
 
 var _frameworks = __webpack_require__(1703);
 
 var _pausePoints = __webpack_require__(3612);
 
-var _mapOriginalExpression = __webpack_require__(3613);
-
-var _mapOriginalExpression2 = _interopRequireDefault(_mapOriginalExpression);
+var _mapExpression = __webpack_require__(3755);
+
+var _mapExpression2 = _interopRequireDefault(_mapExpression);
 
 var _devtoolsUtils = __webpack_require__(1363);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 const { workerHandler } = _devtoolsUtils.workerUtils; /* 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/>. */
@@ -1964,17 +1964,17 @@ self.onmessage = workerHandler({
   clearASTs: _ast.clearASTs,
   hasSource: _sources.hasSource,
   setSource: _sources.setSource,
   clearSources: _sources.clearSources,
   getNextStep: _steps.getNextStep,
   hasSyntaxError: _validate.hasSyntaxError,
   getFramework: _frameworks.getFramework,
   getPausePoints: _pausePoints.getPausePoints,
-  mapOriginalExpression: _mapOriginalExpression2.default
+  mapExpression: _mapExpression2.default
 });
 
 /***/ }),
 
 /***/ 1620:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
@@ -25381,16 +25381,180 @@ function streamingWorkerHandler(publicIn
 module.exports = {
   WorkerDispatcher,
   workerHandler,
   streamingWorkerHandler
 };
 
 /***/ }),
 
+/***/ 3755:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = mapExpression;
+
+var _mapOriginalExpression = __webpack_require__(3613);
+
+var _mapOriginalExpression2 = _interopRequireDefault(_mapOriginalExpression);
+
+var _mapBindings = __webpack_require__(3756);
+
+var _mapBindings2 = _interopRequireDefault(_mapBindings);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/* 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/>. */
+
+function mapExpression(expression, mappings, bindings, shouldMapBindings = true) {
+  let originalExpression = expression;
+  if (mappings) {
+    originalExpression = (0, _mapOriginalExpression2.default)(expression, mappings);
+  }
+
+  let safeExpression = originalExpression;
+  if (shouldMapBindings) {
+    safeExpression = (0, _mapBindings2.default)(originalExpression, bindings);
+  }
+
+  return safeExpression;
+}
+
+/***/ }),
+
+/***/ 3756:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = mapExpressionBindings;
+
+var _ast = __webpack_require__(1375);
+
+var _generator = __webpack_require__(2365);
+
+var _generator2 = _interopRequireDefault(_generator);
+
+var _types = __webpack_require__(2268);
+
+var t = _interopRequireWildcard(_types);
+
+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; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+// translates new bindings `var a = 3` into `self.a = 3`
+// and existing bindings `var a = 3` into `a = 3` for re-assignments
+function globalizeDeclaration(node, bindings) {
+  return node.declarations.map(declaration => {
+    const identifier = bindings.includes(declaration.id.name) ? declaration.id : t.memberExpression(t.identifier("self"), declaration.id);
+
+    return t.expressionStatement(t.assignmentExpression("=", identifier, declaration.init));
+  });
+}
+
+// translates new bindings `a = 3` into `self.a = 3`
+// and keeps assignments the same for existing bindings.
+/* 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/>. */
+
+function globalizeAssignment(node, bindings) {
+  if (bindings.includes(node.left.name)) {
+    return node;
+  }
+
+  const identifier = t.memberExpression(t.identifier("self"), node.left);
+  return t.assignmentExpression(node.operator, identifier, node.right);
+}
+
+function isTopLevel(ancestors) {
+  return ancestors.filter(ancestor => ancestor.key == "body").length == 1;
+}
+
+function replaceNode(ancestors, node) {
+  const parent = ancestors[ancestors.length - 1];
+
+  if (typeof parent.index === "number") {
+    if (Array.isArray(node)) {
+      parent.node[parent.key].splice(parent.index, 1, ...node);
+    } else {
+      parent.node[parent.key][parent.index] = node;
+    }
+  } else {
+    parent.node[parent.key] = node;
+  }
+}
+
+function hasDestructuring(node) {
+  return node.declarations.some(declaration => t.isPattern(declaration.id));
+}
+
+function mapExpressionBindings(expression, bindings = []) {
+  const ast = (0, _ast.parseScript)(expression);
+  let shouldUpdate = true;
+  t.traverse(ast, (node, ancestors) => {
+    const parent = ancestors[ancestors.length - 1];
+
+    if (t.isWithStatement(node)) {
+      shouldUpdate = false;
+      return;
+    }
+
+    if (!isTopLevel(ancestors)) {
+      return;
+    }
+
+    if (t.isAssignmentExpression(node)) {
+      if (t.isIdentifier(node.left)) {
+        const newNode = globalizeAssignment(node, bindings);
+        return replaceNode(ancestors, newNode);
+      }
+
+      if (t.isPattern(node.left)) {
+        shouldUpdate = false;
+        return;
+      }
+    }
+
+    if (!t.isVariableDeclaration(node)) {
+      return;
+    }
+
+    if (hasDestructuring(node)) {
+      shouldUpdate = false;
+      return;
+    }
+
+    if (!t.isForStatement(parent.node)) {
+      const newNodes = globalizeDeclaration(node, bindings);
+      replaceNode(ancestors, newNodes);
+    }
+  });
+
+  if (!shouldUpdate) {
+    return expression;
+  }
+
+  return (0, _generator2.default)(ast).code;
+}
+
+/***/ }),
+
 /***/ 398:
 /***/ (function(module, exports, __webpack_require__) {
 
 /* WEBPACK VAR INJECTION */(function(module) {var root = __webpack_require__(8);
 
 /** Detect free variable `exports`. */
 var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
 
--- a/devtools/client/debugger/new/dist/vendors.js
+++ b/devtools/client/debugger/new/dist/vendors.js
@@ -8379,40 +8379,43 @@ var transition = _interopRequireWildcard
 var _reselect = __webpack_require__(993);
 
 var reselect = _interopRequireWildcard(_reselect);
 
 var _url = __webpack_require__(334);
 
 var url = _interopRequireWildcard(_url);
 
-var _lodashMove = __webpack_require__(3751);
-
-var lodashMove = _interopRequireWildcard(_lodashMove);
-
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
 var _devtoolsSplitter = __webpack_require__(1440);
 
 var _devtoolsSplitter2 = _interopRequireDefault(_devtoolsSplitter);
 
+var _lodashMove = __webpack_require__(3751);
+
+var _lodashMove2 = _interopRequireDefault(_lodashMove);
+
 var _Svg = __webpack_require__(1359);
 
 var _Svg2 = _interopRequireDefault(_Svg);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 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; } }
 
 // We cannot directly export literals containing special characters
 // (eg. "my-module/Test") which is why they are nested in "vendored".
 // The keys of the vendored object should match the module names
 // !!! Should remain synchronized with .babel/transform-mc.js !!!
+
+
+// Modules imported without destructuring
 /* 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/>. */
 
 /**
  * Vendors.js is a file used to bundle and expose all dependencies needed to run
  * the transpiled debugger modules when running in Firefox.
  *
@@ -8430,27 +8433,25 @@ const vendored = exports.vendored = {
   "devtools-components": devtoolsComponents,
   "devtools-config": devtoolsConfig,
   "devtools-contextmenu": devtoolsContextmenu,
   "devtools-environment": devtoolsEnvironment,
   "devtools-modules": devtoolsModules,
   "devtools-splitter": _devtoolsSplitter2.default,
   "devtools-utils": devtoolsUtils,
   "fuzzaldrin-plus": fuzzaldrinPlus,
-  "lodash-move": lodashMove,
+  "lodash-move": _lodashMove2.default,
   "react-transition-group/Transition": transition,
   reselect,
   // Svg is required via relative paths, so the key is not imported path.
   // See .babel/transform-mc.js
   Svg: _Svg2.default,
   url
 };
 
-// Modules imported without destructuring
-
 /***/ }),
 
 /***/ 3750:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
--- a/devtools/client/debugger/new/src/actions/expressions.js
+++ b/devtools/client/debugger/new/src/actions/expressions.js
@@ -15,16 +15,18 @@ exports.getMappedExpression = getMappedE
 var _selectors = require("../selectors/index");
 
 var _promise = require("./utils/middleware/promise");
 
 var _devtoolsSourceMap = require("devtools/client/shared/source-map/index.js");
 
 var _expressions = require("../utils/expressions");
 
+var _prefs = require("../utils/prefs");
+
 var _parser = require("../workers/parser/index");
 
 var parser = _interopRequireWildcard(_parser);
 
 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)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { 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
@@ -210,16 +212,17 @@ function evaluateExpression(expression) 
 function getMappedExpression(expression) {
   return async function ({
     dispatch,
     getState,
     client,
     sourceMaps
   }) {
     const mappings = (0, _selectors.getSelectedScopeMappings)(getState());
+    const bindings = (0, _selectors.getSelectedFrameBindings)(getState());
 
-    if (!mappings) {
+    if (!mappings && !bindings) {
       return expression;
     }
 
-    return parser.mapOriginalExpression(expression, mappings);
+    return parser.mapExpression(expression, mappings, bindings, _prefs.features.mapExpressionBindings);
   };
 }
\ No newline at end of file
--- a/devtools/client/debugger/new/src/actions/file-search.js
+++ b/devtools/client/debugger/new/src/actions/file-search.js
@@ -85,25 +85,30 @@ function updateSearchResults(characterIn
 function searchContents(query, editor) {
   return async ({
     getState,
     dispatch
   }) => {
     const modifiers = (0, _selectors.getFileSearchModifiers)(getState());
     const selectedSource = (0, _selectors.getSelectedSource)(getState());
 
-    if (!query || !editor || !selectedSource || !selectedSource.text || !modifiers) {
+    if (!editor || !selectedSource || !selectedSource.text || !modifiers) {
       return;
     }
 
     const ctx = {
       ed: editor,
       cm: editor.codeMirror
     };
 
+    if (!query) {
+      (0, _editor.clearSearch)(ctx.cm, query);
+      return;
+    }
+
     const _modifiers = modifiers.toJS();
 
     const matches = await (0, _search.getMatches)(query, selectedSource.text, _modifiers);
     const res = (0, _editor.find)(ctx, query, true, _modifiers);
 
     if (!res) {
       return;
     }
--- a/devtools/client/debugger/new/src/actions/sources/loadSourceText.js
+++ b/devtools/client/debugger/new/src/actions/sources/loadSourceText.js
@@ -80,17 +80,17 @@ function loadSourceText(source) {
 
     const deferred = (0, _defer2.default)();
     requests.set(id, deferred.promise);
     telemetry.start(loadSourceHistogram, source);
 
     try {
       await dispatch({
         type: "LOAD_SOURCE_TEXT",
-        sourceId: id,
+        sourceId: source.id,
         [_promise.PROMISE]: loadSource(source, {
           sourceMaps,
           client
         })
       });
     } catch (e) {
       deferred.resolve();
       requests.delete(id);
--- a/devtools/client/debugger/new/src/components/Editor/Footer.js
+++ b/devtools/client/debugger/new/src/components/Editor/Footer.js
@@ -5,26 +5,28 @@ Object.defineProperty(exports, "__esModu
 });
 
 var _react = require("devtools/client/shared/vendor/react");
 
 var _react2 = _interopRequireDefault(_react);
 
 var _reactRedux = require("devtools/client/shared/vendor/react-redux");
 
+var _devtoolsSourceMap = require("devtools/client/shared/source-map/index.js");
+
+var _classnames = require("devtools/client/debugger/new/dist/vendors").vendored["classnames"];
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
 var _actions = require("../../actions/index");
 
 var _actions2 = _interopRequireDefault(_actions);
 
 var _selectors = require("../../selectors/index");
 
-var _classnames = require("devtools/client/debugger/new/dist/vendors").vendored["classnames"];
-
-var _classnames2 = _interopRequireDefault(_classnames);
-
 var _prefs = require("../../utils/prefs");
 
 var _source = require("../../utils/source");
 
 var _sources = require("../../reducers/sources");
 
 var _editor = require("../../utils/editor/index");
 
@@ -143,17 +145,17 @@ class SourceFooter extends _react.PureCo
 
   renderSourceSummary() {
     const {
       mappedSource,
       jumpToMappedLocation,
       selectedSource
     } = this.props;
 
-    if (!mappedSource) {
+    if (!mappedSource || !(0, _devtoolsSourceMap.isOriginalId)(selectedSource.id)) {
       return null;
     }
 
     const filename = (0, _source.getFilename)(mappedSource);
     const tooltip = L10N.getFormatStr("sourceFooter.mappedSourceTooltip", filename);
     const title = L10N.getFormatStr("sourceFooter.mappedSource", filename);
     const mappedSourceLocation = {
       sourceId: selectedSource.id,
--- a/devtools/client/debugger/new/src/components/Editor/index.js
+++ b/devtools/client/debugger/new/src/components/Editor/index.js
@@ -406,25 +406,17 @@ class Editor extends _react.PureComponen
     const {
       selectedLocation,
       selectedSource
     } = this.props;
     const {
       editor
     } = this.state;
 
-    if (!nextProps.selectedSource || !editor || !nextProps.selectedLocation) {
-      return false;
-    }
-
-    if (!(0, _source.isLoaded)(nextProps.selectedSource)) {
-      return false;
-    }
-
-    if (!nextProps.selectedLocation.line) {
+    if (!editor || !nextProps.selectedSource || !nextProps.selectedLocation || !(0, _source.isLoaded)(nextProps.selectedSource)) {
       return false;
     }
 
     const isFirstLoad = (!selectedSource || !(0, _source.isLoaded)(selectedSource)) && (0, _source.isLoaded)(nextProps.selectedSource);
     const locationChanged = selectedLocation !== nextProps.selectedLocation;
     return isFirstLoad || locationChanged;
   }
 
--- a/devtools/client/debugger/new/src/reducers/pause.js
+++ b/devtools/client/debugger/new/src/reducers/pause.js
@@ -16,16 +16,17 @@ exports.getIsWaitingOnBreak = getIsWaiti
 exports.getShouldPauseOnExceptions = getShouldPauseOnExceptions;
 exports.getShouldPauseOnCaughtExceptions = getShouldPauseOnCaughtExceptions;
 exports.getCanRewind = getCanRewind;
 exports.getExtra = getExtra;
 exports.getFrames = getFrames;
 exports.getGeneratedFrameScope = getGeneratedFrameScope;
 exports.getOriginalFrameScope = getOriginalFrameScope;
 exports.getFrameScopes = getFrameScopes;
+exports.getSelectedFrameBindings = getSelectedFrameBindings;
 exports.getFrameScope = getFrameScope;
 exports.getSelectedScope = getSelectedScope;
 exports.getSelectedScopeMappings = getSelectedScopeMappings;
 exports.getSelectedFrameId = getSelectedFrameId;
 exports.getSelectedComponentIndex = getSelectedComponentIndex;
 exports.getTopFrame = getTopFrame;
 exports.getDebuggeeUrl = getDebuggeeUrl;
 exports.getSkipPausing = getSkipPausing;
@@ -376,16 +377,43 @@ function getOriginalFrameScope(state, so
 
   return null;
 }
 
 function getFrameScopes(state) {
   return state.pause.frameScopes;
 }
 
+function getSelectedFrameBindings(state) {
+  const scopes = getFrameScopes(state);
+  const selectedFrameId = getSelectedFrameId(state);
+
+  if (!scopes || !selectedFrameId) {
+    return null;
+  }
+
+  const frameScope = scopes.generated[selectedFrameId];
+
+  if (!frameScope || frameScope.pending) {
+    return;
+  }
+
+  let currentScope = frameScope.scope;
+  let frameBindings = [];
+
+  while (currentScope && currentScope.type != "object") {
+    const bindings = Object.keys(currentScope.bindings.variables);
+    const args = [].concat(...currentScope.bindings.arguments.map(argument => Object.keys(argument)));
+    frameBindings = [...frameBindings, ...bindings, ...args];
+    currentScope = currentScope.parent;
+  }
+
+  return frameBindings;
+}
+
 function getFrameScope(state, sourceId, frameId) {
   return getOriginalFrameScope(state, sourceId, frameId) || getGeneratedFrameScope(state, frameId);
 }
 
 function getSelectedScope(state) {
   const sourceRecord = (0, _sources.getSelectedSource)(state);
   const frameId = getSelectedFrameId(state);
   const {
--- a/devtools/client/debugger/new/src/reducers/sources.js
+++ b/devtools/client/debugger/new/src/reducers/sources.js
@@ -21,24 +21,28 @@ exports.getSourceInSources = getSourceIn
 exports.getSources = getSources;
 exports.getSourceList = getSourceList;
 exports.getSourceCount = getSourceCount;
 
 var _reselect = require("devtools/client/debugger/new/dist/vendors").vendored["reselect"];
 
 var _lodashMove = require("devtools/client/debugger/new/dist/vendors").vendored["lodash-move"];
 
+var _lodashMove2 = _interopRequireDefault(_lodashMove);
+
 var _source = require("../utils/source");
 
 var _devtoolsSourceMap = require("devtools/client/shared/source-map/index.js");
 
 var _lodash = require("devtools/client/shared/vendor/lodash");
 
 var _prefs = require("../utils/prefs");
 
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
 /* 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/>. */
 
 /**
  * Sources reducer
  * @module reducers/sources
  */
@@ -251,17 +255,17 @@ function restoreTabs() {
 
 
 function updateTabList(tabs, url, newIndex) {
   const currentIndex = tabs.indexOf(url);
 
   if (currentIndex === -1) {
     tabs = [url, ...tabs];
   } else if (newIndex !== undefined) {
-    tabs = (0, _lodashMove.move)(tabs, currentIndex, newIndex);
+    tabs = (0, _lodashMove2.default)(tabs, currentIndex, newIndex);
   }
 
   _prefs.prefs.tabs = tabs;
   return tabs;
 }
 
 function updateBlackBoxList(url, isBlackBoxed) {
   const tabs = getBlackBoxList();
--- a/devtools/client/debugger/new/src/utils/editor/source-search.js
+++ b/devtools/client/debugger/new/src/utils/editor/source-search.js
@@ -2,16 +2,17 @@
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.buildQuery = undefined;
 exports.getMatchIndex = getMatchIndex;
 exports.searchSourceForHighlight = searchSourceForHighlight;
 exports.removeOverlay = removeOverlay;
+exports.clearSearch = clearSearch;
 exports.find = find;
 exports.findNext = findNext;
 exports.findPrev = findPrev;
 
 var _buildQuery = require("../../workers/search/build-query");
 
 var _buildQuery2 = _interopRequireDefault(_buildQuery);
 
--- a/devtools/client/debugger/new/src/utils/prefs.js
+++ b/devtools/client/debugger/new/src/utils/prefs.js
@@ -61,16 +61,17 @@ if (isDevelopment()) {
   pref("devtools.debugger.features.outline", true);
   pref("devtools.debugger.features.column-breakpoints", true);
   pref("devtools.debugger.features.replay", true);
   pref("devtools.debugger.features.pause-points", true);
   pref("devtools.debugger.features.skip-pausing", false);
   pref("devtools.debugger.features.component-pane", false);
   pref("devtools.debugger.features.skip-pausing", true);
   pref("devtools.debugger.features.autocomplete-expressions", false);
+  pref("devtools.debugger.features.map-expression-bindings", true);
 }
 
 const prefs = exports.prefs = new PrefsHelper("devtools", {
   alphabetizeOutline: ["Bool", "debugger.alphabetize-outline"],
   autoPrettyPrint: ["Bool", "debugger.auto-pretty-print"],
   clientSourceMapsEnabled: ["Bool", "source-map.client-service.enabled"],
   pauseOnExceptions: ["Bool", "debugger.pause-on-exceptions"],
   pauseOnCaughtExceptions: ["Bool", "debugger.pause-on-caught-exceptions"],
@@ -108,17 +109,17 @@ const features = exports.features = new 
   workers: ["Bool", "workers"],
   codeCoverage: ["Bool", "code-coverage"],
   eventListeners: ["Bool", "event-listeners"],
   outline: ["Bool", "outline"],
   codeFolding: ["Bool", "code-folding"],
   replay: ["Bool", "replay"],
   pausePoints: ["Bool", "pause-points"],
   skipPausing: ["Bool", "skip-pausing"],
-  componentPane: ["Bool", "component-pane"],
-  autocompleteExpression: ["Bool", "autocomplete-expressions"]
+  autocompleteExpression: ["Bool", "autocomplete-expressions"],
+  mapExpressionBindings: ["Bool", "map-expression-bindings"]
 });
 
 if (prefs.debuggerPrefsSchemaVersion !== prefsSchemaVersion) {
   // clear pending Breakpoints
   prefs.pendingBreakpoints = {};
   prefs.debuggerPrefsSchemaVersion = prefsSchemaVersion;
 }
\ No newline at end of file
--- a/devtools/client/debugger/new/src/workers/parser/index.js
+++ b/devtools/client/debugger/new/src/workers/parser/index.js
@@ -1,14 +1,14 @@
 "use strict";
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.replaceOriginalVariableName = exports.getPausePoints = exports.getFramework = exports.mapOriginalExpression = exports.hasSyntaxError = exports.clearSources = exports.setSource = exports.hasSource = exports.getNextStep = exports.clearASTs = exports.clearScopes = exports.clearSymbols = exports.findOutOfScopeLocations = exports.getScopes = exports.getSymbols = exports.getClosestExpression = exports.stop = exports.start = undefined;
+exports.replaceOriginalVariableName = exports.getPausePoints = exports.getFramework = exports.mapExpression = exports.hasSyntaxError = exports.clearSources = exports.setSource = exports.hasSource = exports.getNextStep = exports.clearASTs = exports.clearScopes = exports.clearSymbols = exports.findOutOfScopeLocations = exports.getScopes = exports.getSymbols = exports.getClosestExpression = exports.stop = exports.start = undefined;
 
 var _devtoolsUtils = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-utils"];
 
 /* 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/>. */
 const {
   WorkerDispatcher
@@ -23,12 +23,12 @@ const findOutOfScopeLocations = exports.
 const clearSymbols = exports.clearSymbols = dispatcher.task("clearSymbols");
 const clearScopes = exports.clearScopes = dispatcher.task("clearScopes");
 const clearASTs = exports.clearASTs = dispatcher.task("clearASTs");
 const getNextStep = exports.getNextStep = dispatcher.task("getNextStep");
 const hasSource = exports.hasSource = dispatcher.task("hasSource");
 const setSource = exports.setSource = dispatcher.task("setSource");
 const clearSources = exports.clearSources = dispatcher.task("clearSources");
 const hasSyntaxError = exports.hasSyntaxError = dispatcher.task("hasSyntaxError");
-const mapOriginalExpression = exports.mapOriginalExpression = dispatcher.task("mapOriginalExpression");
+const mapExpression = exports.mapExpression = dispatcher.task("mapExpression");
 const getFramework = exports.getFramework = dispatcher.task("getFramework");
 const getPausePoints = exports.getPausePoints = dispatcher.task("getPausePoints");
 const replaceOriginalVariableName = exports.replaceOriginalVariableName = dispatcher.task("replaceOriginalVariableName");
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/workers/parser/mapBindings.js
@@ -0,0 +1,114 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = mapExpressionBindings;
+
+var _ast = require("./utils/ast");
+
+var _generator = require("@babel/generator/index");
+
+var _generator2 = _interopRequireDefault(_generator);
+
+var _types = require("@babel/types/index");
+
+var t = _interopRequireWildcard(_types);
+
+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)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/* 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/>. */
+// translates new bindings `var a = 3` into `self.a = 3`
+// and existing bindings `var a = 3` into `a = 3` for re-assignments
+function globalizeDeclaration(node, bindings) {
+  return node.declarations.map(declaration => {
+    const identifier = bindings.includes(declaration.id.name) ? declaration.id : t.memberExpression(t.identifier("self"), declaration.id);
+    return t.expressionStatement(t.assignmentExpression("=", identifier, declaration.init));
+  });
+} // translates new bindings `a = 3` into `self.a = 3`
+// and keeps assignments the same for existing bindings.
+
+
+function globalizeAssignment(node, bindings) {
+  if (bindings.includes(node.left.name)) {
+    return node;
+  }
+
+  const identifier = t.memberExpression(t.identifier("self"), node.left);
+  return t.assignmentExpression(node.operator, identifier, node.right);
+}
+
+function isTopLevel(ancestors) {
+  return ancestors.filter(ancestor => ancestor.key == "body").length == 1;
+}
+
+function replaceNode(ancestors, node) {
+  const parent = ancestors[ancestors.length - 1];
+
+  if (typeof parent.index === "number") {
+    if (Array.isArray(node)) {
+      parent.node[parent.key].splice(parent.index, 1, ...node);
+    } else {
+      parent.node[parent.key][parent.index] = node;
+    }
+  } else {
+    parent.node[parent.key] = node;
+  }
+}
+
+function hasDestructuring(node) {
+  return node.declarations.some(declaration => t.isPattern(declaration.id));
+}
+
+function mapExpressionBindings(expression, bindings = []) {
+  const ast = (0, _ast.parseScript)(expression);
+  let shouldUpdate = true;
+  t.traverse(ast, (node, ancestors) => {
+    const parent = ancestors[ancestors.length - 1];
+
+    if (t.isWithStatement(node)) {
+      shouldUpdate = false;
+      return;
+    }
+
+    if (!isTopLevel(ancestors)) {
+      return;
+    }
+
+    if (t.isAssignmentExpression(node)) {
+      if (t.isIdentifier(node.left)) {
+        const newNode = globalizeAssignment(node, bindings);
+        return replaceNode(ancestors, newNode);
+      }
+
+      if (t.isPattern(node.left)) {
+        shouldUpdate = false;
+        return;
+      }
+    }
+
+    if (!t.isVariableDeclaration(node)) {
+      return;
+    }
+
+    if (hasDestructuring(node)) {
+      shouldUpdate = false;
+      return;
+    }
+
+    if (!t.isForStatement(parent.node)) {
+      const newNodes = globalizeDeclaration(node, bindings);
+      replaceNode(ancestors, newNodes);
+    }
+  });
+
+  if (!shouldUpdate) {
+    return expression;
+  }
+
+  return (0, _generator2.default)(ast).code;
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/workers/parser/mapExpression.js
@@ -0,0 +1,35 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = mapExpression;
+
+var _mapOriginalExpression = require("./mapOriginalExpression");
+
+var _mapOriginalExpression2 = _interopRequireDefault(_mapOriginalExpression);
+
+var _mapBindings = require("./mapBindings");
+
+var _mapBindings2 = _interopRequireDefault(_mapBindings);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/* 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/>. */
+function mapExpression(expression, mappings, bindings, shouldMapBindings = true) {
+  let originalExpression = expression;
+
+  if (mappings) {
+    originalExpression = (0, _mapOriginalExpression2.default)(expression, mappings);
+  }
+
+  let safeExpression = originalExpression;
+
+  if (shouldMapBindings) {
+    safeExpression = (0, _mapBindings2.default)(originalExpression, bindings);
+  }
+
+  return safeExpression;
+}
\ No newline at end of file
--- a/devtools/client/debugger/new/src/workers/parser/moz.build
+++ b/devtools/client/debugger/new/src/workers/parser/moz.build
@@ -8,15 +8,17 @@ DIRS += [
     'utils',
 ]
 
 DevToolsModules(
     'findOutOfScopeLocations.js',
     'frameworks.js',
     'getSymbols.js',
     'index.js',
+    'mapBindings.js',
+    'mapExpression.js',
     'mapOriginalExpression.js',
     'pausePoints.js',
     'sources.js',
     'steps.js',
     'validate.js',
     'worker.js',
 )
--- a/devtools/client/debugger/new/src/workers/parser/worker.js
+++ b/devtools/client/debugger/new/src/workers/parser/worker.js
@@ -17,19 +17,19 @@ var _findOutOfScopeLocations2 = _interop
 var _steps = require("./steps");
 
 var _validate = require("./validate");
 
 var _frameworks = require("./frameworks");
 
 var _pausePoints = require("./pausePoints");
 
-var _mapOriginalExpression = require("./mapOriginalExpression");
+var _mapExpression = require("./mapExpression");
 
-var _mapOriginalExpression2 = _interopRequireDefault(_mapOriginalExpression);
+var _mapExpression2 = _interopRequireDefault(_mapExpression);
 
 var _devtoolsUtils = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-utils"];
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 /* 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/>. */
@@ -45,10 +45,10 @@ self.onmessage = workerHandler({
   clearASTs: _ast.clearASTs,
   hasSource: _sources.hasSource,
   setSource: _sources.setSource,
   clearSources: _sources.clearSources,
   getNextStep: _steps.getNextStep,
   hasSyntaxError: _validate.hasSyntaxError,
   getFramework: _frameworks.getFramework,
   getPausePoints: _pausePoints.getPausePoints,
-  mapOriginalExpression: _mapOriginalExpression2.default
+  mapExpression: _mapExpression2.default
 });
\ No newline at end of file
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -119,16 +119,17 @@ support-files =
   examples/doc-minified2.html
   examples/doc-on-load.html
   examples/doc-preview.html
   examples/doc-sourcemaps.html
   examples/doc-sourcemaps2.html
   examples/doc-sourcemaps3.html
   examples/doc-sourcemap-bogus.html
   examples/doc-sources.html
+  examples/doc-strict.html
   examples/doc-pause-points.html
   examples/doc-return-values.html
   examples/doc-wasm-sourcemaps.html
   examples/asm.js
   examples/async.js
   examples/bogus-map.js
   examples/entry.js
   examples/exceptions.js
@@ -169,16 +170,17 @@ skip-if = !e10s || verify # This test is
 [browser_dbg-breakpoint-skipping.js]
 [browser_dbg-call-stack.js]
 [browser_dbg-scopes.js]
 [browser_dbg-chrome-create.js]
 skip-if = (verify && !debug && (os == 'linux'))
 [browser_dbg-chrome-debugging.js]
 [browser_dbg-console.js]
 [browser_dbg-console-link.js]
+[browser_dbg-console-map-bindings.js]
 [browser_dbg-content-script-sources.js]
 skip-if = (os == "win" && ccov) # Bug 1424154
 [browser_dbg-debugger-buttons.js]
 [browser_dbg-editor-gutter.js]
 [browser_dbg-editor-select.js]
 [browser_dbg-editor-highlight.js]
 [browser_dbg-ember-quickstart.js]
 [browser_dbg-expressions.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-console-map-bindings.js
@@ -0,0 +1,45 @@
+// Return a promise with a reference to jsterm, opening the split
+// console if necessary.  This cleans up the split console pref so
+// it won't pollute other tests.
+function getSplitConsole(dbg) {
+  const { toolbox, win } = dbg;
+
+  if (!win) {
+    win = toolbox.win;
+  }
+
+  if (!toolbox.splitConsole) {
+    pressKey(dbg, "Escape");
+  }
+
+  return new Promise(resolve => {
+    toolbox.getPanelWhenReady("webconsole").then(() => {
+      ok(toolbox.splitConsole, "Split console is shown.");
+      let jsterm = toolbox.getPanel("webconsole").hud.jsterm;
+      resolve(jsterm);
+    });
+  });
+}
+
+async function evaluate(dbg, expression) {
+  const { toolbox } = dbg;
+  let jsterm = toolbox.getPanel("webconsole").hud.jsterm;
+  const msg = await jsterm.execute(expression);
+  return msg.innerText;
+}
+
+add_task(async function() {
+  Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true);
+  const dbg = await initDebugger("doc-strict.html");
+
+  await getSplitConsole(dbg);
+  ok(dbg.toolbox.splitConsole, "Split console is shown.");
+
+  invokeInTab("strict", 2);
+
+  await waitForPaused(dbg);
+  const msg = await evaluate(dbg, "var c = 3");
+  const msg2 = await evaluate(dbg, "c");
+
+  is(msg2, "3");
+});
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-source-maps.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-source-maps.js
@@ -44,17 +44,17 @@ add_task(async function() {
   await addBreakpoint(dbg, "times2", 2);
 
   invokeInTab("keepMeAlive");
   await waitForPaused(dbg);
   await waitForSelectedSource(dbg, "times2");
 
   info(`Test previewing in the original location`);
   await assertPreviews(dbg, [
-    { line: 2, column: 10, result: 4, expression: "x" }
+    { line: 2, column: 10, result: 4, expression: "x;" }
   ]);
 
   info(`Test previewing in the generated location`);
   await dbg.actions.jumpToMappedSelectedLocation();
   await waitForSelectedSource(dbg, "bundle.js");
   await assertPreviews(dbg, [
     { line: 70, column: 11, result: 4, expression: "x" }
   ]);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemapped-preview.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemapped-preview.js
@@ -23,29 +23,29 @@ async function breakpointPreviews(dbg, f
   ok(true, `Ran tests for ${fixture} at line ${line} column ${column}`);
 }
 
 function testForOf(dbg) {
   return breakpointPreviews(dbg, "babel-for-of", { line: 5, column: 4 }, [
     {
       line: 5,
       column: 7,
-      expression: "doThing",
+      expression: "doThing;",
       result: "doThing(arg)"
     },
     {
       line: 5,
       column: 13,
-      expression: "x",
+      expression: "x;",
       result: "1"
     },
     {
       line: 8,
       column: 16,
-      expression: "doThing",
+      expression: "doThing;",
       result: "doThing(arg)"
     }
   ]);
 }
 
 function testShadowing(dbg) {
   return breakpointPreviews(
     dbg,
@@ -54,17 +54,17 @@ function testShadowing(dbg) {
     [
       // These aren't what the user would expect, but we test them anyway since
       // they reflect what this actually returns. These shadowed bindings read
       // the binding closest to the current frame's scope even though their
       // actual value is different.
       {
         line: 2,
         column: 9,
-        expression: "aVar",
+        expression: "aVar;",
         result: '"var3"'
       },
       {
         line: 3,
         column: 9,
         expression: "_aLet2;",
         result: '"let3"'
       },
@@ -72,17 +72,17 @@ function testShadowing(dbg) {
         line: 4,
         column: 11,
         expression: "_aConst2;",
         result: '"const3"'
       },
       {
         line: 10,
         column: 11,
-        expression: "aVar",
+        expression: "aVar;",
         result: '"var3"'
       },
       {
         line: 11,
         column: 11,
         expression: "_aLet2;",
         result: '"let3"'
       },
@@ -92,17 +92,17 @@ function testShadowing(dbg) {
         expression: "_aConst2;",
         result: '"const3"'
       },
 
       // These actually result in the values the user would expect.
       {
         line: 14,
         column: 13,
-        expression: "aVar",
+        expression: "aVar;",
         result: '"var3"'
       },
       {
         line: 15,
         column: 13,
         expression: "_aLet2;",
         result: '"let3"'
       },
@@ -140,17 +140,17 @@ function testImportedBindings(dbg) {
       line: 25,
       column: 16,
       expression: "_mod3.aNamed;",
       result: '"a-named"'
     },
     {
       line: 26,
       column: 16,
-      expression: "aNamespace",
+      expression: "aNamespace;",
       fields: [["aNamed", "a-named"], ["default", "a-default"]]
     },
     {
       line: 31,
       column: 20,
       expression: "_mod7.default;",
       result: '"a-default2"'
     },
@@ -170,17 +170,17 @@ function testImportedBindings(dbg) {
       line: 34,
       column: 20,
       expression: "_mod8.aNamed2;",
       result: '"a-named2"'
     },
     {
       line: 35,
       column: 20,
-      expression: "aNamespace2",
+      expression: "aNamespace2;",
       fields: [["aNamed", "a-named2"], ["default", "a-default2"]]
     }
   ]);
 }
 
 add_task(async function() {
   await pushPref("devtools.debugger.features.map-scopes", true);
   const dbg = await initDebugger("doc-sourcemapped.html");
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-tabs-pretty-print.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-tabs-pretty-print.js
@@ -10,9 +10,12 @@ add_task(async function() {
   clickElement(dbg, "prettyPrintButton");
   await waitForSource(dbg, "math.min.js:formatted");
   // Test reloading the debugger
   await waitForSelectedSource(dbg, "math.min.js:formatted");
   await reload(dbg);
 
   await waitForSelectedSource(dbg, "math.min.js:formatted");
   ok(true, "Pretty printed source is selected on reload");
+
+  const breakpointTab = findElementWithSelector(dbg, ".source-tab.active .filename");
+  is(breakpointTab.textContent, "math.min.js", ":formatted does not display in tab label");
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/doc-strict.html
@@ -0,0 +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/. -->
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Debugger test page</title>
+  </head>
+
+  <body>
+    <script>
+      function strict(a) {
+        "use strict"
+        var b = 2;
+        debugger;
+      }
+    </script>
+  </body>
+</html>
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -259,16 +259,17 @@ devtools.jar:
     skin/images/read-only.svg (themes/images/read-only.svg)
     skin/images/reveal.svg (themes/images/reveal.svg)
 
     # Debugger
     skin/images/debugger/angular.svg (themes/images/debugger/angular.svg)
     skin/images/debugger/arrow.svg (themes/images/debugger/arrow.svg)
     skin/images/debugger/back.svg (themes/images/debugger/back.svg)
     skin/images/debugger/blackBox.svg (themes/images/debugger/blackBox.svg)
+    skin/images/debugger/breakpoint.svg (themes/images/debugger/breakpoint.svg)
     skin/images/debugger/close.svg (themes/images/debugger/close.svg)
     skin/images/debugger/coffeescript.svg (themes/images/debugger/coffeescript.svg)
     skin/images/debugger/disable-pausing.svg (themes/images/debugger/disable-pausing.svg)
     skin/images/debugger/domain.svg (themes/images/debugger/domain.svg)
     skin/images/debugger/extension.svg (themes/images/debugger/extension.svg)
     skin/images/debugger/file.svg (themes/images/debugger/file.svg)
     skin/images/debugger/folder.svg (themes/images/debugger/folder.svg)
     skin/images/debugger/forward.svg (themes/images/debugger/forward.svg)
--- a/devtools/client/preferences/debugger.js
+++ b/devtools/client/preferences/debugger.js
@@ -61,8 +61,9 @@ pref("devtools.debugger.features.event-l
 pref("devtools.debugger.features.code-folding", false);
 pref("devtools.debugger.features.outline", true);
 pref("devtools.debugger.features.replay", false);
 pref("devtools.debugger.features.pause-points", true);
 pref("devtools.debugger.features.component-stack", 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);
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/debugger/breakpoint.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 60 12">
+  <path id="base-path" d="M53.9,0H1C0.4,0,0,0.4,0,1v10c0,0.6,0.4,1,1,1h52.9c0.6,0,1.2-0.3,1.5-0.7L60,6l-4.4-5.3C55,0.3,54.5,0,53.9,0z"/>
+</svg>