Bug 1456472 - Update Debugger Frontend v44. r=jdescottes
authorJason Laster <jason.laster.11@gmail.com>
Wed, 25 Apr 2018 14:29:57 +0200
changeset 471708 972a371de2cace6aa6a0e7ae43aa68b92bcfe160
parent 471707 e19dc6140f5c4108fddddee62c9a977b97f08054
child 471709 30ef2759d59356ab199342927eb6e450199cb024
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdescottes
bugs1456472
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 1456472 - Update Debugger Frontend v44. r=jdescottes MozReview-Commit-ID: K5UpDpDfzUb
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
--- 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 43.0
+Version 44.0
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-42...release-43
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-43...release-44
 
 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
@@ -3087,41 +3087,45 @@ html[dir="rtl"] .breakpoints-list .break
     transform: translateX(-10px);
   }
   40%,
   80% {
     transform: translateX(10px);
   }
 }
 
-.input-expression.error {
-  border: 1px solid red;
-  animation: 150ms cubic-bezier(0.07, 0.95, 0, 1) shake;
-}
-
 .input-expression::placeholder {
   font-style: italic;
   color: var(--theme-comment);
 }
 
 .input-expression:focus {
   outline: none;
   cursor: text;
 }
 
 .expressions-list {
   /* TODO: add normalize */
   margin: 0;
   padding: 0;
 }
+
 .expression-input-container {
   display: flex;
+  border: 1px solid transparent;
+}
+
+.expression-input-container.focused {
   border: 1px solid var(--theme-highlight-blue);
 }
 
+.expression-input-container.error {
+  border: 1px solid red;
+}
+
 .expression-container {
   border: 1px;
   padding: 0.25em 1em 0.25em 0.5em;
   width: 100%;
   color: var(--theme-body-color);
   background-color: var(--theme-body-background);
   display: block;
   position: relative;
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -5280,19 +5280,22 @@ function clearProjectDirectoryRoot() {
     url: ""
   };
 }
 
 function setProjectDirectoryRoot(newRoot) {
   return ({ dispatch, getState }) => {
     const curRoot = (0, _ui.getProjectDirectoryRoot)(getState());
     if (newRoot && curRoot) {
-      const temp = newRoot.split("/");
-      temp.splice(0, 2);
-      newRoot = `${curRoot}/${temp.join("/")}`;
+      const newRootArr = newRoot.replace(/\/+/g, "/").split("/");
+      const curRootArr = curRoot.replace(/^\//, "").replace(/\/+/g, "/").split("/");
+      if (newRootArr[0] !== curRootArr[0]) {
+        newRootArr.splice(0, 2);
+        newRoot = `${curRoot}/${newRootArr.join("/")}`;
+      }
     }
 
     dispatch({
       type: "SET_PROJECT_DIRECTORY_ROOT",
       url: newRoot
     });
   };
 }
@@ -17017,19 +17020,20 @@ class SourcesTree extends _react.Compone
     let roots = () => sourceTree.contents;
 
     let clearProjectRootButton = null;
 
     // The "sourceTree.contents[0]" check ensures that there are contents
     // A custom root with no existing sources will be ignored
     if (isCustomRoot) {
       let rootLabel = projectRoot.split("/").pop();
-      if (sourceTree.contents[0]) {
-        rootLabel = sourceTree.contents[0].name;
-        roots = () => sourceTree.contents[0].contents;
+      const sourceContents = sourceTree.contents[0];
+      if (sourceContents) {
+        rootLabel = sourceContents.contents[0].name;
+        roots = () => sourceContents.contents[0].contents;
       }
 
       clearProjectRootButton = _react2.default.createElement(
         "button",
         {
           className: "sources-clear-root",
           onClick: () => this.props.clearProjectDirectoryRoot(),
           title: L10N.getStr("removeDirectoryRoot.label")
@@ -21517,17 +21521,17 @@ class Breakpoint extends _react.Componen
       }
 
       // NOTE: we need to wait for the breakpoint to be loaded
       // to get the generated location
       if (!selectedSource || breakpoint.loading) {
         return;
       }
 
-      const sourceId = selectedSource.get("id");
+      const sourceId = selectedSource.id;
       const line = (0, _editor.toEditorLine)(sourceId, breakpoint.location.line);
 
       editor.codeMirror.setGutterMarker(line, "breakpoints", makeMarker(breakpoint.disabled));
 
       editor.codeMirror.addLineClass(line, "line", "new-breakpoint");
       if (breakpoint.condition) {
         editor.codeMirror.addLineClass(line, "line", "has-condition");
       } else {
@@ -21555,17 +21559,17 @@ class Breakpoint extends _react.Componen
     if (!selectedSource) {
       return;
     }
 
     if (breakpoint.loading) {
       return;
     }
 
-    const sourceId = selectedSource.get("id");
+    const sourceId = selectedSource.id;
     const doc = (0, _editor.getDocument)(sourceId);
     if (!doc) {
       return;
     }
 
     const line = (0, _editor.toEditorLine)(sourceId, breakpoint.location.line);
 
     // NOTE: when we upgrade codemirror we can use `doc.setGutterMarker`
@@ -23315,19 +23319,22 @@ class Breakpoints extends _react.Compone
   }
 
   removeBreakpoint(event, breakpoint) {
     event.stopPropagation();
     this.props.removeBreakpoint(breakpoint.location);
   }
 
   renderBreakpoint(breakpoint) {
+    const { selectedSource } = this.props;
+
     return _react2.default.createElement(_Breakpoint2.default, {
       key: breakpoint.locationId,
       breakpoint: breakpoint,
+      selectedSource: selectedSource,
       onClick: () => this.selectBreakpoint(breakpoint),
       onContextMenu: e => (0, _BreakpointsContextMenu2.default)(_extends({}, this.props, { breakpoint, contextMenuEvent: e })),
       onChange: () => this.handleBreakpointCheckbox(breakpoint),
       onCloseClick: ev => this.removeBreakpoint(ev, breakpoint)
     });
   }
 
   renderExceptionsOptions() {
@@ -23389,17 +23396,20 @@ function updateLocation(sources, frame, 
   const locationId = (0, _breakpoint.makeLocationId)(bp.location);
   const localBP = _extends({}, bp, { locationId, isCurrentlyPaused, source });
 
   return localBP;
 }
 
 const _getBreakpoints = (0, _reselect.createSelector)(_selectors.getBreakpoints, _selectors.getSources, _selectors.getTopFrame, _selectors.getPauseReason, (breakpoints, sources, frame, why) => breakpoints.map(bp => updateLocation(sources, frame, why, bp)).filter(bp => bp.source && !bp.source.isBlackBoxed));
 
-exports.default = (0, _reactRedux.connect)((state, props) => ({ breakpoints: _getBreakpoints(state) }), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Breakpoints);
+exports.default = (0, _reactRedux.connect)((state, props) => ({
+  breakpoints: _getBreakpoints(state),
+  selectedSource: (0, _selectors.getSelectedSource)(state)
+}), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Breakpoints);
 
 /***/ }),
 
 /***/ 1601:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
@@ -23460,19 +23470,24 @@ class Expressions extends _react.Compone
 
     this.handleKeyDown = e => {
       if (e.key === "Escape") {
         this.clear();
       }
     };
 
     this.hideInput = () => {
+      this.setState({ focused: false });
       this.props.onExpressionAdded();
     };
 
+    this.onFocus = () => {
+      this.setState({ focused: true });
+    };
+
     this.handleExistingSubmit = async (e, expression) => {
       e.preventDefault();
       e.stopPropagation();
 
       this.props.updateExpression(this.state.inputValue, expression);
       this.hideInput();
     };
 
@@ -23542,17 +23557,18 @@ class Expressions extends _react.Compone
           )
         )
       );
     };
 
     this.state = {
       editing: false,
       editIndex: -1,
-      inputValue: ""
+      inputValue: "",
+      focused: false
     };
   }
 
   componentDidMount() {
     const { expressions, evaluateExpressions } = this.props;
     if (expressions.size > 0) {
       evaluateExpressions();
     }
@@ -23560,20 +23576,20 @@ class Expressions extends _react.Compone
 
   componentWillReceiveProps(nextProps) {
     if (this.state.editing && !nextProps.expressionError) {
       this.clear();
     }
   }
 
   shouldComponentUpdate(nextProps, nextState) {
-    const { editing, inputValue } = this.state;
+    const { editing, inputValue, focused } = this.state;
     const { expressions, expressionError, showInput } = this.props;
 
-    return expressions !== nextProps.expressions || expressionError !== nextProps.expressionError || editing !== nextState.editing || inputValue !== nextState.inputValue || nextProps.showInput !== showInput;
+    return expressions !== nextProps.expressions || expressionError !== nextProps.expressionError || editing !== nextState.editing || inputValue !== nextState.inputValue || nextProps.showInput !== showInput || focused !== nextState.focused;
   }
 
   componentDidUpdate(prevProps, prevState) {
     if (this._input && !prevState.editing) {
       const input = this._input;
       input.setSelectionRange(0, input.value.length);
       input.focus();
     }
@@ -23594,60 +23610,70 @@ class Expressions extends _react.Compone
   }
 
   onBlur() {
     this.clear();
     this.hideInput();
   }
 
   renderNewExpressionInput() {
-    const { expressionError } = this.props;
-    const { editing, inputValue } = this.state;
+    const { expressionError, expressions } = this.props;
+    const { editing, inputValue, focused } = this.state;
     const error = editing === false && expressionError === true;
     const placeholder = error ? L10N.getStr("expressions.errorMsg") : L10N.getStr("expressions.placeholder");
+    const autoFocus = expressions.size > 0;
+
     return _react2.default.createElement(
       "li",
-      { className: "expression-input-container" },
+      {
+        className: (0, _classnames2.default)("expression-input-container", { focused, error })
+      },
       _react2.default.createElement(
         "form",
         { className: "expression-input-form", onSubmit: this.handleNewSubmit },
         _react2.default.createElement("input", {
-          className: (0, _classnames2.default)("input-expression", { error }),
+          className: "input-expression",
           type: "text",
           placeholder: placeholder,
           onChange: this.handleChange,
           onBlur: this.hideInput,
           onKeyDown: this.handleKeyDown,
-          autoFocus: "true",
+          onFocus: this.onFocus,
+          autoFocus: autoFocus,
           value: !editing ? inputValue : ""
         }),
         _react2.default.createElement("input", { type: "submit", style: { display: "none" } })
       )
     );
   }
 
   renderExpressionEditInput(expression) {
     const { expressionError } = this.props;
-    const { inputValue, editing } = this.state;
+    const { inputValue, editing, focused } = this.state;
     const error = editing === true && expressionError === true;
+
     return _react2.default.createElement(
       "span",
-      { className: "expression-input-container", key: expression.input },
+      {
+        className: (0, _classnames2.default)("expression-input-container", { focused, error }),
+        key: expression.input
+      },
       _react2.default.createElement(
         "form",
         {
           className: "expression-input-form",
           onSubmit: e => this.handleExistingSubmit(e, expression)
         },
         _react2.default.createElement("input", {
           className: (0, _classnames2.default)("input-expression", { error }),
           type: "text",
           onChange: this.handleChange,
           onBlur: this.clear,
           onKeyDown: this.handleKeyDown,
+          onFocus: this.onFocus,
           value: editing ? inputValue : expression.input,
           ref: c => this._input = c
         }),
         _react2.default.createElement("input", { type: "submit", style: { display: "none" } })
       )
     );
   }
 
@@ -31458,32 +31484,36 @@ function createOriginalSource(originalUr
     isPrettyPrinted: false,
     isWasm: false,
     isBlackBoxed: false,
     loadedState: "unloaded"
   };
 }
 
 function loadSourceMaps(sources) {
-  return async function ({ dispatch, getState, sourceMaps }) {
+  return async function ({ dispatch, sourceMaps }) {
+    if (!sourceMaps) {
+      return;
+    }
+
     const originalSources = await Promise.all(sources.map(source => dispatch(loadSourceMap(source.id))));
 
     await dispatch(newSources((0, _lodash.flatten)(originalSources)));
   };
 }
 
 /**
  * @memberof actions/sources
  * @static
  */
 function loadSourceMap(sourceId) {
   return async function ({ dispatch, getState, sourceMaps }) {
     const source = (0, _selectors.getSource)(getState(), sourceId).toJS();
 
-    if (!(0, _devtoolsSourceMap.isGeneratedId)(sourceId) || !source.sourceMapURL) {
+    if (!sourceMaps || !(0, _devtoolsSourceMap.isGeneratedId)(sourceId) || !source.sourceMapURL) {
       return;
     }
 
     let urls = null;
     try {
       urls = await sourceMaps.getOriginalURLs(source);
     } catch (e) {
       console.error(e);
@@ -39094,72 +39124,91 @@ var _react2 = _interopRequireDefault(_re
 var _reactDom = __webpack_require__(4);
 
 var _reactDom2 = _interopRequireDefault(_reactDom);
 
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
+var _devtoolsSourceMap = __webpack_require__(1360);
+
 var _Close = __webpack_require__(1374);
 
 var _Close2 = _interopRequireDefault(_Close);
 
 var _breakpoint = __webpack_require__(1364);
 
 var _prefs = __webpack_require__(226);
 
 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 getBreakpointLocation(source, line, column) {
   const isWasm = source && source.isWasm;
   const columnVal = _prefs.features.columnBreakpoints && column ? `:${column}` : "";
   const bpLocation = isWasm ? `0x${line.toString(16).toUpperCase()}` : `${line}${columnVal}`;
 
   return bpLocation;
+} /* 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 getBreakpointText(selectedSource, breakpoint) {
+  const { condition, text, originalText } = breakpoint;
+
+  if (condition) {
+    return condition;
+  }
+
+  if (!selectedSource || (0, _devtoolsSourceMap.isGeneratedId)(selectedSource.id) || originalText.length == 0) {
+    return text;
+  }
+
+  return originalText;
 }
 
 class Breakpoint extends _react.Component {
 
   componentDidMount() {
     this.setupEditor();
   }
 
-  componentDidUpdate() {
+  componentDidUpdate(prevProps) {
+    if (getBreakpointText(this.props.selectedSource, this.props.breakpoint) != getBreakpointText(prevProps.selectedSource, prevProps.breakpoint)) {
+      this.destroyEditor();
+    }
     this.setupEditor();
   }
 
   componentWillUnmount() {
-    if (this.editor) {
-      this.editor.destroy();
-    }
+    this.destroyEditor();
   }
 
   shouldComponentUpdate(nextProps) {
     const prevBreakpoint = this.props.breakpoint;
     const nextBreakpoint = nextProps.breakpoint;
 
-    return !prevBreakpoint || prevBreakpoint.text != nextBreakpoint.text || prevBreakpoint.disabled != nextBreakpoint.disabled || prevBreakpoint.condition != nextBreakpoint.condition || prevBreakpoint.hidden != nextBreakpoint.hidden || prevBreakpoint.isCurrentlyPaused != nextBreakpoint.isCurrentlyPaused;
-  }
-
-  getBreakpointText() {
-    const { breakpoint } = this.props;
-    return breakpoint.condition || breakpoint.text;
+    return !prevBreakpoint || this.props.selectedSource != nextProps.selectedSource || prevBreakpoint.text != nextBreakpoint.text || prevBreakpoint.disabled != nextBreakpoint.disabled || prevBreakpoint.condition != nextBreakpoint.condition || prevBreakpoint.hidden != nextBreakpoint.hidden || prevBreakpoint.isCurrentlyPaused != nextBreakpoint.isCurrentlyPaused;
+  }
+
+  destroyEditor() {
+    if (this.editor) {
+      this.editor.destroy();
+      this.editor = null;
+    }
   }
 
   setupEditor() {
     if (this.editor) {
       return;
     }
 
-    this.editor = (0, _breakpoint.createEditor)(this.getBreakpointText());
+    const { selectedSource, breakpoint } = this.props;
+
+    this.editor = (0, _breakpoint.createEditor)(getBreakpointText(selectedSource, breakpoint));
 
     // disables the default search shortcuts
     // $FlowIgnore
     this.editor._initShortcuts = () => {};
 
     const node = _reactDom2.default.findDOMNode(this);
     if (node instanceof HTMLElement) {
       const mountNode = node.querySelector(".breakpoint-label");
@@ -39181,17 +39230,18 @@ class Breakpoint extends _react.Componen
       className: "breakpoint-checkbox",
       checked: !disabled,
       onChange: onChange,
       onClick: ev => ev.stopPropagation()
     });
   }
 
   renderText() {
-    const text = this.getBreakpointText();
+    const { selectedSource, breakpoint } = this.props;
+    const text = getBreakpointText(selectedSource, breakpoint);
 
     return _react2.default.createElement(
       "label",
       { className: "breakpoint-label", title: text },
       text
     );
   }
 
--- a/devtools/client/debugger/new/parser-worker.js
+++ b/devtools/client/debugger/new/parser-worker.js
@@ -21390,25 +21390,33 @@ function mapOriginalExpression(expressio
   const ast = (0, _ast.parseScript)(expression);
   t.traverse(ast, (node, ancestors) => {
     const parent = ancestors[ancestors.length - 1];
     if (!parent) {
       return;
     }
 
     const parentNode = parent.node;
+
+    let name = null;
     if (t.isIdentifier(node) && t.isReferenced(node, parentNode)) {
-      if (mappings.hasOwnProperty(node.name)) {
-        const mapping = mappings[node.name];
-        if (mapping && mapping !== node.name) {
-          const mappingNode = getFirstExpression((0, _ast.parseScript)(mapping));
-          replaceNode(ancestors, mappingNode);
-
-          didReplace = true;
-        }
+      name = node.name;
+    } else if (t.isThisExpression(node)) {
+      name = "this";
+    } else {
+      return;
+    }
+
+    if (mappings.hasOwnProperty(name)) {
+      const mapping = mappings[name];
+      if (mapping && mapping !== name) {
+        const mappingNode = getFirstExpression((0, _ast.parseScript)(mapping));
+        replaceNode(ancestors, mappingNode);
+
+        didReplace = true;
       }
     }
   });
 
   if (!didReplace) {
     // Avoid the extra code generation work and also avoid potentially
     // reformatting the user's code unnecessarily.
     return expression;