Bug 1449362 - Update Debugger Frontend v29. r=jdescottes
authorJason Laster <jason.laster.11@gmail.com>
Tue, 27 Mar 2018 19:06:56 -0400
changeset 410473 5d0273aecd2995e0033227b660fc87937bd77b34
parent 410472 23c2abe70e6c5da9d8879193ee29c61161ee6fc3
child 410474 4cd76f6e6217f67a679a66facf705f2670f679d1
push id33729
push userrgurzau@mozilla.com
push dateWed, 28 Mar 2018 21:55:49 +0000
treeherdermozilla-central@6aa3b57955fe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdescottes
bugs1449362
milestone61.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 1449362 - Update Debugger Frontend v29. r=jdescottes
devtools/client/debugger/new/README.mozilla
devtools/client/debugger/new/debugger.css
devtools/client/debugger/new/debugger.js
devtools/client/debugger/new/parser-worker.js
devtools/client/preferences/debugger.js
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 28.0
+Version 29.0
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-28...release-27
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-28...release-29
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.0
 - babel-preset-react @6.24.1
 - react @16.2.0
 - react-dom @16.2.0
 - webpack @3.11.0
--- a/devtools/client/debugger/new/debugger.css
+++ b/devtools/client/debugger/new/debugger.css
@@ -1213,16 +1213,17 @@ html[dir="rtl"] .tree-node img.arrow {
   flex: 1;
   overflow-x: auto;
   overflow-y: auto;
 }
 
 .sources-list {
   flex: 1;
   display: flex;
+  overflow: hidden;
 }
 
 .sources-list .tree:focus {
   outline: none;
 }
 
 .sources-list .managed-tree {
   flex: 1;
@@ -3977,16 +3978,20 @@ html .welcomebox .toggle-button-end.coll
 }
 
 .result-list.big li {
   padding: 10px;
   flex-direction: row;
   border-bottom: 1px solid var(--theme-splitter-color);
 }
 
+.result-list.small li {
+  justify-content: space-between;
+}
+
 .result-list li:hover {
   background: var(--theme-tab-toolbar-background);
 }
 
 .result-list li.selected {
   background: var(--accordion-header-background);
 }
 
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -5400,25 +5400,27 @@ var _extends = Object.assign || function
 /* eslint complexity: ["error", 30]*/
 
 /**
  * Pause reducer
  * @module reducers/pause
  */
 
 exports.getPauseReason = getPauseReason;
+exports.getPauseCommand = getPauseCommand;
 exports.isStepping = isStepping;
 exports.isPaused = isPaused;
 exports.getPreviousPauseFrameLocation = getPreviousPauseFrameLocation;
 exports.isEvaluatingExpression = isEvaluatingExpression;
 exports.getPopupObjectProperties = getPopupObjectProperties;
 exports.getIsWaitingOnBreak = getIsWaitingOnBreak;
 exports.getShouldPauseOnExceptions = getShouldPauseOnExceptions;
 exports.getShouldIgnoreCaughtExceptions = getShouldIgnoreCaughtExceptions;
 exports.getCanRewind = getCanRewind;
+exports.getExtra = getExtra;
 exports.getFrames = getFrames;
 exports.getGeneratedFrameScope = getGeneratedFrameScope;
 exports.getOriginalFrameScope = getOriginalFrameScope;
 exports.getFrameScopes = getFrameScopes;
 exports.getFrameScope = getFrameScope;
 exports.getSelectedScope = getSelectedScope;
 exports.getSelectedScopeMappings = getSelectedScopeMappings;
 exports.getSelectedFrameId = getSelectedFrameId;
@@ -5430,31 +5432,32 @@ var _reselect = __webpack_require__(993)
 
 var _devtoolsSourceMap = __webpack_require__(1360);
 
 var _prefs = __webpack_require__(226);
 
 var _sources = __webpack_require__(1369);
 
 const createPauseState = exports.createPauseState = () => ({
+  extra: {},
   why: null,
   isWaitingOnBreak: false,
   frames: undefined,
   selectedFrameId: undefined,
   frameScopes: {
     generated: {},
     original: {},
     mappings: {}
   },
   loadedObjects: {},
   shouldPauseOnExceptions: _prefs.prefs.pauseOnExceptions,
   shouldIgnoreCaughtExceptions: _prefs.prefs.ignoreCaughtExceptions,
   canRewind: false,
   debuggeeUrl: "",
-  command: "",
+  command: null,
   previousLocation: null
 });
 
 const emptyPauseState = {
   pause: null,
   frames: null,
   frameScopes: {
     generated: {},
@@ -5490,26 +5493,27 @@ function update(state = createPauseState
 
     case "MAP_FRAMES":
       {
         return _extends({}, state, { frames: action.frames });
       }
 
     case "ADD_SCOPES":
       {
-        const { frame, status, value } = action;
+        const { frame, extra, status, value } = action;
         const selectedFrameId = frame.id;
 
         const generated = _extends({}, state.frameScopes.generated, {
           [selectedFrameId]: {
             pending: status !== "done",
             scope: value
           }
         });
         return _extends({}, state, {
+          extra: extra,
           frameScopes: _extends({}, state.frameScopes, {
             generated
           })
         });
       }
 
     case "TRAVEL_TO":
       return _extends({}, state, action.data.paused);
@@ -5573,27 +5577,27 @@ function update(state = createPauseState
         shouldIgnoreCaughtExceptions
       });
 
     case "COMMAND":
       {
         return action.status === "start" ? _extends({}, state, emptyPauseState, {
           command: action.command,
           previousLocation: buildPreviousLocation(state, action)
-        }) : _extends({}, state, { command: "" });
+        }) : _extends({}, state, { command: null });
       }
 
     case "RESUME":
       // We clear why on resume because we need it to decide if
       // we shoul re-evaluate watch expressions.
       return _extends({}, state, { why: null });
 
     case "EVALUATE_EXPRESSION":
       return _extends({}, state, {
-        command: action.status === "start" ? "expression" : ""
+        command: action.status === "start" ? "expression" : null
       });
 
     case "NAVIGATE":
       return _extends({}, state, emptyPauseState, { debuggeeUrl: action.url });
   }
 
   return state;
 }
@@ -5630,18 +5634,22 @@ function buildPreviousLocation(state, ac
 const getPauseState = state => state.pause;
 
 const getAllPopupObjectProperties = exports.getAllPopupObjectProperties = (0, _reselect.createSelector)(getPauseState, pauseWrapper => pauseWrapper.loadedObjects);
 
 function getPauseReason(state) {
   return state.pause.why;
 }
 
+function getPauseCommand(state) {
+  return state.pause && state.pause.command;
+}
+
 function isStepping(state) {
-  return ["stepIn", "stepOver", "stepOut"].includes(state.pause.command);
+  return ["stepIn", "stepOver", "stepOut"].includes(getPauseCommand(state));
 }
 
 function isPaused(state) {
   return !!getFrames(state);
 }
 
 function getPreviousPauseFrameLocation(state) {
   return state.pause.previousLocation;
@@ -5666,16 +5674,20 @@ function getShouldPauseOnExceptions(stat
 function getShouldIgnoreCaughtExceptions(state) {
   return state.pause.shouldIgnoreCaughtExceptions;
 }
 
 function getCanRewind(state) {
   return state.pause.canRewind;
 }
 
+function getExtra(state) {
+  return state.pause.extra;
+}
+
 function getFrames(state) {
   return state.pause.frames;
 }
 
 function getGeneratedFrameScope(state, frameId) {
   if (!frameId) {
     return null;
   }
@@ -6383,57 +6395,61 @@ Object.defineProperty(exports, "__esModu
 });
 exports.setSourceMetaData = setSourceMetaData;
 exports.setSymbols = setSymbols;
 exports.setOutOfScopeLocations = setOutOfScopeLocations;
 exports.setPausePoints = setPausePoints;
 
 var _selectors = __webpack_require__(3590);
 
+var _pause = __webpack_require__(1639);
+
 var _setInScopeLines = __webpack_require__(1781);
 
 var _parser = __webpack_require__(1365);
 
 var _promise = __webpack_require__(1653);
 
-/* 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 setSourceMetaData(sourceId) {
   return async ({ dispatch, getState }) => {
     const source = (0, _selectors.getSource)(getState(), sourceId);
     if (!source || !source.text || source.isWasm) {
       return;
     }
 
     const framework = await (0, _parser.getFramework)(source.id);
     dispatch({
       type: "SET_SOURCE_METADATA",
       sourceId: source.id,
       sourceMetaData: {
         framework
       }
     });
   };
-}
+} /* 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 setSymbols(sourceId) {
   return async ({ dispatch, getState }) => {
     const source = (0, _selectors.getSource)(getState(), sourceId);
     if (!source || !source.text || source.isWasm || (0, _selectors.hasSymbols)(getState(), source)) {
       return;
     }
 
     await dispatch({
       type: "SET_SYMBOLS",
       source: source.toJS(),
       [_promise.PROMISE]: (0, _parser.getSymbols)(source.id)
     });
 
+    if ((0, _selectors.isPaused)(getState())) {
+      await dispatch((0, _pause.mapFrames)());
+    }
+
     await dispatch(setPausePoints(sourceId));
     await dispatch(setSourceMetaData(sourceId));
   };
 }
 
 function setOutOfScopeLocations() {
   return async ({ dispatch, getState }) => {
     const location = (0, _selectors.getSelectedLocation)(getState());
@@ -7707,64 +7723,35 @@ module.exports = {
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.containsPosition = containsPosition;
-exports.findClosestScope = findClosestScope;
 exports.getASTLocation = getASTLocation;
 exports.findScopeByName = findScopeByName;
 
 var _parser = __webpack_require__(1365);
 
-function containsPosition(a, b) {
-  const startsBefore = a.start.line < b.line || a.start.line === b.line && a.start.column <= b.column;
-  const endsAfter = a.end.line > b.line || a.end.line === b.line && a.end.column >= b.column;
-
-  return startsBefore && endsAfter;
-} /* 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 findClosestScope(functions, location) {
-  return functions.reduce((found, currNode) => {
-    if (currNode.name === "anonymous" || !containsPosition(currNode.location, {
-      line: location.line,
-      column: location.column || 0
-    })) {
-      return found;
-    }
-
-    if (!found) {
-      return currNode;
-    }
-
-    if (found.location.start.line > currNode.location.start.line) {
-      return found;
-    }
-    if (found.location.start.line === currNode.location.start.line && found.location.start.column > currNode.location.start.column) {
-      return found;
-    }
-
-    return currNode;
-  }, null);
-}
+var _ast = __webpack_require__(1638);
+
+/* 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 getASTLocation(source, symbols, location) {
   if (source.isWasm || !symbols || symbols.loading) {
     return { name: undefined, offset: location };
   }
 
   const functions = [...symbols.functions];
 
-  const scope = findClosestScope(functions, location);
+  const scope = (0, _ast.findClosestFunction)(functions, location);
   if (scope) {
     // we only record the line, but at some point we may
     // also do column offsets
     const line = location.line - scope.location.start.line;
     return {
       name: scope.name,
       offset: { line, column: undefined }
     };
@@ -21925,17 +21912,17 @@ var _reactRedux = __webpack_require__(35
 var _devtoolsContextmenu = __webpack_require__(1413);
 
 var _devtoolsSourceMap = __webpack_require__(1360);
 
 var _clipboard = __webpack_require__(1388);
 
 var _function = __webpack_require__(1597);
 
-var _astBreakpointLocation = __webpack_require__(1416);
+var _ast = __webpack_require__(1638);
 
 var _editor = __webpack_require__(1358);
 
 var _source = __webpack_require__(1356);
 
 var _selectors = __webpack_require__(3590);
 
 var _actions = __webpack_require__(1354);
@@ -22133,18 +22120,18 @@ const {
 
 exports.default = (0, _reactRedux.connect)(state => {
   const selectedSource = (0, _selectors.getSelectedSource)(state);
   return {
     selectedLocation: (0, _selectors.getSelectedLocation)(state),
     selectedSource,
     hasPrettyPrint: !!(0, _selectors.getPrettySource)(state, selectedSource.get("id")),
     contextMenu: (0, _selectors.getContextMenu)(state),
-    getFunctionText: line => (0, _function.findFunctionText)(line, selectedSource.toJS(), (0, _selectors.getSymbols)(state, selectedSource.toJS())),
-    getFunctionLocation: line => (0, _astBreakpointLocation.findClosestScope)((0, _selectors.getSymbols)(state, selectedSource.toJS()).functions, {
+    getFunctionText: line => (0, _function.findFunctionText)(line, selectedSource.toJS(), (0, _selectors.getSymbols)(state, selectedSource)),
+    getFunctionLocation: line => (0, _ast.findClosestFunction)((0, _selectors.getSymbols)(state, selectedSource).functions, {
       line,
       column: Infinity
     })
   };
 }, {
   addExpression,
   evaluateInConsole,
   flashLineRange,
@@ -22162,26 +22149,29 @@ exports.default = (0, _reactRedux.connec
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.findFunctionText = findFunctionText;
 
-var _astBreakpointLocation = __webpack_require__(1416);
+var _ast = __webpack_require__(1638);
 
 var _indentation = __webpack_require__(1438);
 
 /* 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 findFunctionText(line, source, symbols) {
-  const func = (0, _astBreakpointLocation.findClosestScope)(symbols.functions, { line, column: Infinity });
+  const func = (0, _ast.findClosestFunction)(symbols.functions, {
+    line,
+    column: Infinity
+  });
   if (!func) {
     return null;
   }
 
   const { location: { start, end } } = func;
   const lines = source.text.split("\n");
   const firstLine = lines[start.line - 1].slice(start.column);
   const lastLine = lines[end.line - 1].slice(0, end.column);
@@ -22466,33 +22456,35 @@ var _CommandBar2 = _interopRequireDefaul
 var _UtilsBar = __webpack_require__(1609);
 
 var _UtilsBar2 = _interopRequireDefault(_UtilsBar);
 
 var _BreakpointsDropdown = __webpack_require__(1790);
 
 var _BreakpointsDropdown2 = _interopRequireDefault(_BreakpointsDropdown);
 
+var _FrameworkComponent = __webpack_require__(3623);
+
+var _FrameworkComponent2 = _interopRequireDefault(_FrameworkComponent);
+
 var _ChromeScopes = __webpack_require__(1610);
 
 var _ChromeScopes2 = _interopRequireDefault(_ChromeScopes);
 
 var _Scopes2 = __webpack_require__(1611);
 
 var _Scopes3 = _interopRequireDefault(_Scopes2);
 
 __webpack_require__(1342);
 
 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/>. */
-
-const Scopes = _prefs.features.chromeScopes ? _ChromeScopes2.default : _Scopes3.default;
+const Scopes = _prefs.features.chromeScopes ? _ChromeScopes2.default : _Scopes3.default; /* 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 debugBtn(onClick, type, className, tooltip) {
   return _react2.default.createElement(
     "button",
     {
       onClick: onClick,
       className: `${type} ${className}`,
       key: type,
@@ -22553,16 +22545,30 @@ class SecondaryPanes extends _react.Comp
       component: _react2.default.createElement(Scopes, null),
       opened: _prefs.prefs.scopesVisible,
       onToggle: opened => {
         _prefs.prefs.scopesVisible = opened;
       }
     };
   }
 
+  getComponentItem() {
+    const { extra: { react } } = this.props;
+
+    return {
+      header: react.displayName,
+      className: "component-pane",
+      component: _react2.default.createElement(_FrameworkComponent2.default, null),
+      opened: _prefs.prefs.componentVisible,
+      onToggle: opened => {
+        _prefs.prefs.componentVisible = opened;
+      }
+    };
+  }
+
   getWatchItem() {
     return {
       header: L10N.getStr("watchExpressions.header"),
       className: "watch-expressions-pane",
       buttons: this.watchExpressionHeaderButtons(),
       component: _react2.default.createElement(_Expressions2.default, null),
       opened: _prefs.prefs.expressionsVisible,
       onToggle: opened => {
@@ -22620,32 +22626,37 @@ class SecondaryPanes extends _react.Comp
       shouldIgnoreCaughtExceptions,
       isWaitingOnBreak
     } = this.props;
 
     return (0, _BreakpointsDropdown2.default)(breakOnNext, pauseOnExceptions, shouldPauseOnExceptions, shouldIgnoreCaughtExceptions, isWaitingOnBreak);
   }
 
   getStartItems() {
-    const { workers } = this.props;
+    const { extra, workers } = this.props;
 
     const items = [];
     if (this.props.horizontal) {
       if (_prefs.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 (extra && extra.react) {
+          items.push(this.getComponentItem());
+        }
+
         items.push(this.getScopeItem());
       }
     }
 
     if (_prefs.features.eventListeners) {
       items.push({
         header: L10N.getStr("eventListenersHeader"),
         className: "event-listeners-pane",
@@ -22656,30 +22667,34 @@ class SecondaryPanes extends _react.Comp
     return items.filter(item => item);
   }
 
   renderHorizontalLayout() {
     return _react2.default.createElement(_Accordion2.default, { items: this.getItems() });
   }
 
   getEndItems() {
-    const { workers } = this.props;
+    const { extra, workers } = this.props;
 
     let items = [];
 
     if (this.props.horizontal) {
       return [];
     }
 
     if (_prefs.features.workers && workers.size > 0) {
       items.push(this.getWorkersItem());
     }
 
     items.push(this.getWatchItem());
 
+    if (extra && extra.react) {
+      items.push(this.getComponentItem());
+    }
+
     if (this.props.hasFrames) {
       items = [...items, this.getScopeItem()];
     }
 
     return items;
   }
 
   getItems() {
@@ -22723,16 +22738,17 @@ class SecondaryPanes extends _react.Comp
   }
 }
 
 SecondaryPanes.contextTypes = {
   shortcuts: _propTypes2.default.object
 };
 
 exports.default = (0, _reactRedux.connect)(state => ({
+  extra: (0, _selectors.getExtra)(state),
   hasFrames: !!(0, _selectors.getTopFrame)(state),
   breakpoints: (0, _selectors.getBreakpoints)(state),
   breakpointsDisabled: (0, _selectors.getBreakpointsDisabled)(state),
   breakpointsLoading: (0, _selectors.getBreakpointsLoading)(state),
   isWaitingOnBreak: (0, _selectors.getIsWaitingOnBreak)(state),
   shouldPauseOnExceptions: (0, _selectors.getShouldPauseOnExceptions)(state),
   shouldIgnoreCaughtExceptions: (0, _selectors.getShouldIgnoreCaughtExceptions)(state),
   workers: (0, _selectors.getWorkers)(state)
@@ -26370,16 +26386,18 @@ function astCommand(stepType) {
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.findBestMatchExpression = findBestMatchExpression;
 exports.findEmptyLines = findEmptyLines;
+exports.containsPosition = containsPosition;
+exports.findClosestFunction = findClosestFunction;
 
 var _lodash = __webpack_require__(2);
 
 function findBestMatchExpression(symbols, tokenPos) {
   const { memberExpressions, identifiers } = symbols;
   const { line, column } = tokenPos;
   return identifiers.concat(memberExpressions).reduce((found, expression) => {
     const overlaps = expression.location.start.line == line && expression.location.start.column <= column && expression.location.end.column >= column && !expression.computed;
@@ -26405,16 +26423,47 @@ function findEmptyLines(selectedSource, 
   if (!selectedSource.text) {
     return [];
   }
   const lineCount = selectedSource.text.split("\n").length;
   const sourceLines = (0, _lodash.range)(1, lineCount + 1);
   return (0, _lodash.without)(sourceLines, ...breakpointLines);
 }
 
+function containsPosition(a, b) {
+  const startsBefore = a.start.line < b.line || a.start.line === b.line && a.start.column <= b.column;
+  const endsAfter = a.end.line > b.line || a.end.line === b.line && a.end.column >= b.column;
+
+  return startsBefore && endsAfter;
+}
+
+function findClosestFunction(functions, location) {
+  return functions.reduce((found, currNode) => {
+    if (currNode.name === "anonymous" || !containsPosition(currNode.location, {
+      line: location.line,
+      column: location.column || 0
+    })) {
+      return found;
+    }
+
+    if (!found) {
+      return currNode;
+    }
+
+    if (found.location.start.line > currNode.location.start.line) {
+      return found;
+    }
+    if (found.location.start.line === currNode.location.start.line && found.location.start.column > currNode.location.start.column) {
+      return found;
+    }
+
+    return currNode;
+  }, null);
+}
+
 /***/ }),
 
 /***/ 1639:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
@@ -27174,17 +27223,20 @@ class QuickOpenModal extends _react.Comp
     };
 
     this.searchSymbols = query => {
       const { symbols: { functions, variables } } = this.props;
 
       let results = functions;
       if (this.isVariableQuery()) {
         results = variables;
-      }
+      } else {
+        results = results.filter(result => result.title !== "anonymous");
+      }
+
       if (query === "@" || query === "#") {
         return this.setState({ results });
       }
 
       this.setState({ results: filter(results, query.slice(1)) });
     };
 
     this.searchShortcuts = query => {
@@ -27590,36 +27642,43 @@ Object.defineProperty(exports, "__esModu
   value: true
 });
 exports.fetchScopes = fetchScopes;
 
 var _selectors = __webpack_require__(3590);
 
 var _mapScopes = __webpack_require__(1634);
 
+var _preview = __webpack_require__(1786);
+
 var _promise = __webpack_require__(1653);
 
+/* 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 fetchScopes() {
   return async function ({ dispatch, getState, client, sourceMaps }) {
     const frame = (0, _selectors.getSelectedFrame)(getState());
     if (!frame || (0, _selectors.getGeneratedFrameScope)(getState(), frame.id)) {
       return;
     }
 
+    const extra = await dispatch((0, _preview.getExtra)("this;", frame.this, frame));
+
     const scopes = dispatch({
       type: "ADD_SCOPES",
       frame,
+      extra,
       [_promise.PROMISE]: client.getFrameScopes(frame)
     });
 
     await dispatch((0, _mapScopes.mapScopes)(scopes, frame));
   };
-} /* 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/>. */
+}
 
 /***/ }),
 
 /***/ 1657:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
@@ -30163,16 +30222,17 @@ function createLocation({
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 
 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /* 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/>. */
 
+exports.getExtra = getExtra;
 exports.updatePreview = updatePreview;
 exports.setPreview = setPreview;
 exports.clearPreview = clearPreview;
 
 var _ast = __webpack_require__(1638);
 
 var _editor = __webpack_require__(1358);
 
@@ -30237,16 +30297,24 @@ function isInvalidTarget(target) {
   const invalidToken = tokenText === "" || tokenText.match(/[(){}\|&%,.;=<>\+-/\*\s]/);
 
   // exclude codemirror elements that are not tokens
   const invalidTarget = target.parentElement && !target.parentElement.closest(".CodeMirror-line") || cursorPos.top == 0;
 
   return invalidTarget || invalidToken || invaildType;
 }
 
+function getExtra(expression, result, selectedFrame) {
+  return async ({ dispatch, getState, client, sourceMaps }) => {
+    const extra = await getExtraProps(expression, result, expr => client.evaluateInFrame(selectedFrame.id, expr));
+
+    return extra;
+  };
+}
+
 function updatePreview(target, editor) {
   return ({ dispatch, getState, client, sourceMaps }) => {
     const tokenPos = (0, _editor.getTokenLocation)(editor.codeMirror, target);
     const cursorPos = target.getBoundingClientRect();
     const preview = (0, _selectors.getPreview)(getState());
 
     if ((0, _selectors.getCanRewind)(getState())) {
       return;
@@ -30317,17 +30385,17 @@ function setPreview(expression, location
         }
 
         const { result } = await client.evaluateInFrame(selectedFrame.id, expression);
 
         if (result === undefined) {
           return;
         }
 
-        const extra = await getExtraProps(expression, result, expr => client.evaluateInFrame(selectedFrame.id, expr));
+        const extra = await dispatch(getExtra(expression, result, selectedFrame));
 
         return {
           expression,
           result,
           location,
           tokenPos,
           cursorPos,
           extra
@@ -30745,37 +30813,71 @@ function isDebugLine(selectedFrame, sele
 
   return selectedFrame.location.sourceId == selectedLocation.sourceId && selectedFrame.location.line == selectedLocation.line;
 }
 
 function isDocumentReady(selectedSource, selectedLocation) {
   return selectedLocation && (0, _source.isLoaded)(selectedSource) && (0, _sourceDocuments.hasDocument)(selectedLocation.sourceId);
 }
 
-class HighlightLine extends _react.PureComponent {
+class HighlightLine extends _react.Component {
+  constructor(...args) {
+    var _temp;
+
+    return _temp = super(...args), this.isStepping = false, this.previousEditorLine = null, _temp;
+  }
+
+  shouldComponentUpdate(nextProps) {
+    const { selectedLocation, selectedSource } = nextProps;
+    return this.shouldSetHighlightLine(selectedLocation, selectedSource);
+  }
+
+  shouldSetHighlightLine(selectedLocation, selectedSource) {
+    const { sourceId, line } = selectedLocation;
+    const editorLine = (0, _editor.toEditorLine)(sourceId, line);
+
+    if (!isDocumentReady(selectedSource, selectedLocation)) {
+      return false;
+    }
+
+    if (this.isStepping && editorLine === this.previousEditorLine) {
+      return false;
+    }
+
+    return true;
+  }
+
   componentDidUpdate(prevProps) {
-    const { selectedLocation, selectedFrame, selectedSource } = this.props;
+    const {
+      pauseCommand,
+      selectedLocation,
+      selectedFrame,
+      selectedSource
+    } = this.props;
+    if (pauseCommand) {
+      this.isStepping = true;
+    }
 
     this.clearHighlightLine(prevProps.selectedLocation, prevProps.selectedSource);
-
     this.setHighlightLine(selectedLocation, selectedFrame, selectedSource);
   }
 
   setHighlightLine(selectedLocation, selectedFrame, selectedSource) {
-    if (!isDocumentReady(selectedSource, selectedLocation)) {
-      return;
-    }
-
     const { sourceId, line } = selectedLocation;
+    if (!this.shouldSetHighlightLine(selectedLocation, selectedSource)) {
+      return;
+    }
+    this.isStepping = false;
+    const editorLine = (0, _editor.toEditorLine)(sourceId, line);
+    this.previousEditorLine = editorLine;
 
     if (!line || isDebugLine(selectedFrame, selectedLocation)) {
       return;
     }
 
-    const editorLine = (0, _editor.toEditorLine)(sourceId, line);
     const doc = (0, _sourceDocuments.getDocument)(sourceId);
     doc.addLineClass(editorLine, "line", "highlight-line");
   }
 
   clearHighlightLine(selectedLocation, selectedSource) {
     if (!isDocumentReady(selectedSource, selectedLocation)) {
       return;
     }
@@ -30788,16 +30890,17 @@ class HighlightLine extends _react.PureC
 
   render() {
     return null;
   }
 }
 
 exports.HighlightLine = HighlightLine;
 exports.default = (0, _reactRedux.connect)(state => ({
+  pauseCommand: (0, _selectors.getPauseCommand)(state),
   selectedFrame: (0, _selectors.getVisibleSelectedFrame)(state),
   selectedLocation: (0, _selectors.getSelectedLocation)(state),
   selectedSource: (0, _selectors.getSelectedSource)(state)
 }))(HighlightLine);
 
 /***/ }),
 
 /***/ 1797:
@@ -31576,52 +31679,76 @@ function jumpToMappedSelectedLocation() 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 
 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 
 exports.updateFrameLocation = updateFrameLocation;
+exports.mapDisplayNames = mapDisplayNames;
 exports.mapFrames = mapFrames;
 
 var _selectors = __webpack_require__(3590);
 
+var _ast = __webpack_require__(1638);
+
 function updateFrameLocation(frame, sourceMaps) {
   return sourceMaps.getOriginalLocation(frame.location).then(loc => _extends({}, frame, {
     location: loc,
     generatedLocation: frame.generatedLocation || frame.location
   }));
 }
 
 function updateFrameLocations(frames, sourceMaps) {
   if (!frames || frames.length == 0) {
     return Promise.resolve(frames);
   }
 
   return Promise.all(frames.map(frame => updateFrameLocation(frame, sourceMaps)));
 }
 
-/**
- * Map call stack frame locations to original locations.
+function mapDisplayNames(frames, getState) {
+  return frames.map(frame => {
+    const source = (0, _selectors.getSource)(getState(), frame.location.sourceId);
+    const symbols = (0, _selectors.getSymbols)(getState(), source);
+
+    if (!symbols || !symbols.functions) {
+      return frame;
+    }
+
+    const originalFunction = (0, _ast.findClosestFunction)(symbols.functions, frame.location);
+
+    if (!originalFunction) {
+      return frame;
+    }
+
+    const originalDisplayName = originalFunction.name;
+    return _extends({}, frame, { originalDisplayName });
+  });
+}
+
+/**
+ * Map call stack frame locations and display names to originals.
  * e.g.
  * 1. When the debuggee pauses
  * 2. When a source is pretty printed
- *
+ * 3. When symbols are loaded
  * @memberof actions/pause
  * @static
  */
 function mapFrames() {
   return async function ({ dispatch, getState, sourceMaps }) {
     const frames = (0, _selectors.getFrames)(getState());
     if (!frames) {
       return;
     }
 
-    const mappedFrames = await updateFrameLocations(frames, sourceMaps);
+    let mappedFrames = await updateFrameLocations(frames, sourceMaps);
+    mappedFrames = mapDisplayNames(mappedFrames, getState);
 
     dispatch({
       type: "MAP_FRAMES",
       frames: mappedFrames
     });
   };
 }
 
@@ -33921,16 +34048,17 @@ const pref = Services.pref;
 if (isDevelopment()) {
   pref("devtools.debugger.alphabetize-outline", false);
   pref("devtools.debugger.auto-pretty-print", true);
   pref("devtools.source-map.client-service.enabled", true);
   pref("devtools.debugger.pause-on-exceptions", false);
   pref("devtools.debugger.ignore-caught-exceptions", false);
   pref("devtools.debugger.call-stack-visible", true);
   pref("devtools.debugger.scopes-visible", true);
+  pref("devtools.debugger.component-visible", true);
   pref("devtools.debugger.workers-visible", true);
   pref("devtools.debugger.expressions-visible", true);
   pref("devtools.debugger.breakpoints-visible", true);
   pref("devtools.debugger.start-panel-collapsed", false);
   pref("devtools.debugger.end-panel-collapsed", false);
   pref("devtools.debugger.tabs", "[]");
   pref("devtools.debugger.tabsBlackBoxed", "[]");
   pref("devtools.debugger.ui.framework-grouping-on", true);
@@ -33963,16 +34091,17 @@ if (isDevelopment()) {
 const 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"],
   ignoreCaughtExceptions: ["Bool", "debugger.ignore-caught-exceptions"],
   callStackVisible: ["Bool", "debugger.call-stack-visible"],
   scopesVisible: ["Bool", "debugger.scopes-visible"],
+  componentVisible: ["Bool", "debugger.component-visible"],
   workersVisible: ["Bool", "debugger.workers-visible"],
   breakpointsVisible: ["Bool", "debugger.breakpoints-visible"],
   expressionsVisible: ["Bool", "debugger.expressions-visible"],
   startPanelCollapsed: ["Bool", "debugger.start-panel-collapsed"],
   endPanelCollapsed: ["Bool", "debugger.end-panel-collapsed"],
   frameworkGroupingOn: ["Bool", "debugger.ui.framework-grouping-on"],
   tabs: ["Json", "debugger.tabs", []],
   tabsBlackBoxed: ["Json", "debugger.tabsBlackBoxed", []],
@@ -37216,34 +37345,43 @@ function annotateBabelAsyncFrames(frames
   const isBabelFrame = frameIndex => babelFrameIndexes.includes(frameIndex);
 
   return frames.map((frame, frameIndex) => isBabelFrame(frameIndex) ? _extends({}, frame, { library: "Babel" }) : frame);
 }
 
 // Receives an array of frames and looks for babel async
 // call stack groups.
 function getBabelFrameIndexes(frames) {
-  const startIndexes = getFrameIndices(frames, (displayName, url) => url.match(/regenerator-runtime/i) && displayName === "tryCatch");
-
-  const endIndexes = getFrameIndices(frames, (displayName, url) => displayName === "_asyncToGenerator/<" || url.match(/_microtask/i) && displayName === "flush");
+  const startIndexes = frames.reduce((accumulator, frame, index) => {
+    if ((0, _getFrameUrl.getFrameUrl)(frame).match(/regenerator-runtime/i) && frame.displayName === "tryCatch") {
+      return [...accumulator, index];
+    }
+    return accumulator;
+  }, []);
+
+  const endIndexes = frames.reduce((accumulator, frame, index) => {
+    if ((0, _getFrameUrl.getFrameUrl)(frame).match(/_microtask/i) && frame.displayName === "flush") {
+      return [...accumulator, index];
+    }
+    if (frame.displayName === "_asyncToGenerator/<") {
+      return [...accumulator, index + 1];
+    }
+    return accumulator;
+  }, []);
 
   if (startIndexes.length != endIndexes.length || startIndexes.length === 0) {
     return frames;
   }
 
   // Receives an array of start and end index tuples and returns
   // an array of async call stack index ranges.
   // e.g. [[1,3], [5,7]] => [[1,2,3], [5,6,7]]
   return (0, _lodash.flatMap)((0, _lodash.zip)(startIndexes, endIndexes), ([startIndex, endIndex]) => (0, _lodash.range)(startIndex, endIndex + 1));
 }
 
-function getFrameIndices(frames, predicate) {
-  return frames.reduce((accumulator, frame, index) => predicate(frame.displayName, (0, _getFrameUrl.getFrameUrl)(frame)) ? [...accumulator, index] : accumulator, []);
-}
-
 /***/ }),
 
 /***/ 3609:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
@@ -37397,17 +37535,18 @@ const displayNameMap = {
 };
 
 function mapDisplayNames(frame, library) {
   const { displayName } = frame;
   return displayNameMap[library] && displayNameMap[library][displayName] || displayName;
 }
 
 function formatDisplayName(frame, { shouldMapDisplayName = true } = {}) {
-  let { displayName, library } = frame;
+  let { displayName, originalDisplayName, library } = frame;
+  displayName = originalDisplayName || displayName;
   if (library && shouldMapDisplayName) {
     displayName = mapDisplayNames(frame, library);
   }
 
   displayName = simplifyDisplayName(displayName);
   return (0, _utils.endTruncateStr)(displayName, 25);
 }
 
@@ -38301,16 +38440,121 @@ function formatPausePoints(text, nodes) 
     lines[line - 1] = insertStrtAt(lines[line - 1], column, `/*${types} ${num}*/`);
   });
 
   return lines.join("\n");
 }
 
 /***/ }),
 
+/***/ 3623:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _react = __webpack_require__(0);
+
+var _react2 = _interopRequireDefault(_react);
+
+var _redux = __webpack_require__(3593);
+
+var _reactRedux = __webpack_require__(3592);
+
+var _actions = __webpack_require__(1354);
+
+var _actions2 = _interopRequireDefault(_actions);
+
+var _firefox = __webpack_require__(1500);
+
+var _selectors = __webpack_require__(3590);
+
+var _devtoolsReps = __webpack_require__(1408);
+
+var _preview = __webpack_require__(1807);
+
+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/>. */
+
+const { createNode, getChildren } = _devtoolsReps.ObjectInspectorUtils.node;
+const { loadItemProperties } = _devtoolsReps.ObjectInspectorUtils.loadProperties;
+
+class FrameworkComponent extends _react.PureComponent {
+  async componentWillMount() {
+    const expression = "this;";
+    const { selectedFrame, setPopupObjectProperties } = this.props;
+    const value = selectedFrame.this;
+
+    const root = createNode(null, expression, expression, { value });
+    const properties = await loadItemProperties(root, _firefox.createObjectClient);
+    if (properties) {
+      setPopupObjectProperties(value, properties);
+    }
+  }
+
+  renderReactComponent() {
+    const { selectedFrame, popupObjectProperties } = this.props;
+    const expression = "this;";
+    const value = selectedFrame.this;
+    const root = {
+      name: expression,
+      path: expression,
+      contents: { value }
+    };
+
+    const loadedRootProperties = popupObjectProperties[value.actor];
+
+    let roots = getChildren({
+      item: root,
+      loadedProperties: new Map([[root.path, loadedRootProperties]])
+    });
+
+    roots = roots.filter(r => ["state", "props"].includes(r.name));
+
+    return _react2.default.createElement(
+      "div",
+      { className: "pane framework-component" },
+      _react2.default.createElement(_devtoolsReps.ObjectInspector, {
+        roots: roots,
+        autoExpandAll: false,
+        autoExpandDepth: 0,
+        disableWrap: true,
+        disabledFocus: true,
+        dimTopLevelWindow: true,
+        createObjectClient: grip => (0, _firefox.createObjectClient)(grip)
+      })
+    );
+  }
+
+  render() {
+    const { selectedFrame } = this.props;
+    if ((0, _preview.isReactComponent)(selectedFrame.this)) {
+      return this.renderReactComponent();
+    }
+
+    return null;
+  }
+}
+
+exports.default = (0, _reactRedux.connect)(state => {
+  return {
+    selectedFrame: (0, _selectors.getSelectedFrame)(state),
+    popupObjectProperties: (0, _selectors.getAllPopupObjectProperties)(state)
+  };
+}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(FrameworkComponent);
+
+/***/ }),
+
 /***/ 363:
 /***/ (function(module, exports) {
 
 module.exports = "<!-- 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:svg=\"http://www.w3.org/2000/svg\"><path fill=\"black\" id=\"svg_1\" fill-rule=\"evenodd\" d=\"m4.55195,12.97461l7.4,-5l-7.4,-5l0,10zm-0.925,0l0,-10c0,-0.785 0.8,-1.264 1.415,-0.848l7.4,5c0.58,0.392 0.58,1.304 0,1.696l-7.4,5c-0.615,0.416 -1.415,-0.063 -1.415,-0.848z\"></path></svg>"
 
 /***/ }),
 
 /***/ 364:
--- a/devtools/client/debugger/new/parser-worker.js
+++ b/devtools/client/debugger/new/parser-worker.js
@@ -992,20 +992,17 @@ let ASTs = new Map();
 function _parse(code, opts) {
   return babylon.parse(code, _extends({}, opts, {
     tokens: true
   }));
 }
 
 const sourceOptions = {
   generated: {
-    tokens: true,
-    plugins: [
-      "objectRestSpread"
-    ]
+    tokens: true
   },
   original: {
     sourceType: "unambiguous",
     tokens: true,
     plugins: ["jsx", "flow", "doExpressions", "objectRestSpread", "classProperties", "exportDefaultFrom", "exportNamespaceFrom", "asyncGenerators", "functionBind", "functionSent", "dynamicImport"]
   }
 };
 
@@ -40560,9 +40557,9 @@ function listCacheHas(key) {
 }
 
 module.exports = listCacheHas;
 
 
 /***/ })
 
 /******/ });
-});
+});
\ No newline at end of file
--- a/devtools/client/preferences/debugger.js
+++ b/devtools/client/preferences/debugger.js
@@ -24,16 +24,17 @@ pref("devtools.debugger.ui.panes-workers
 pref("devtools.debugger.ui.panes-instruments-width", 300);
 pref("devtools.debugger.ui.panes-visible-on-startup", false);
 pref("devtools.debugger.ui.variables-sorting-enabled", true);
 pref("devtools.debugger.ui.variables-only-enum-visible", false);
 pref("devtools.debugger.ui.variables-searchbox-visible", false);
 pref("devtools.debugger.ui.framework-grouping-on", true);
 pref("devtools.debugger.call-stack-visible", true);
 pref("devtools.debugger.scopes-visible", true);
+pref("devtools.debugger.component-visible", true);
 pref("devtools.debugger.workers-visible", true);
 pref("devtools.debugger.breakpoints-visible", true);
 pref("devtools.debugger.expressions-visible", true);
 pref("devtools.debugger.start-panel-collapsed", false);
 pref("devtools.debugger.end-panel-collapsed", false);
 pref("devtools.debugger.tabs", "[]");
 pref("devtools.debugger.tabsBlackBoxed", "[]");
 pref("devtools.debugger.pending-selected-location", "{}");