Bug 1517349 - [release 115] Log points (#7592). r=dwalsh
authorJason Laster <jlaster@mozilla.com>
Wed, 02 Jan 2019 16:58:30 -0500
changeset 509432 2dbeeb593cbfb5aecd4f5fea1c404e5d7cb2dd5f
parent 509431 70c4fd323f9c5b46c544c17b832718c4d0cd98e5
child 509433 0340904ea2bc20c873c977d2f93e5124bd4dedef
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdwalsh
bugs1517349
milestone66.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 1517349 - [release 115] Log points (#7592). r=dwalsh
devtools/client/debugger/new/src/actions/ui.js
devtools/client/debugger/new/src/components/Editor/ConditionalPanel.js
devtools/client/debugger/new/src/components/Editor/GutterMenu.js
devtools/client/debugger/new/src/reducers/ui.js
devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-columns.js
devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-cond.js
devtools/client/debugger/new/test/mochitest/helpers.js
devtools/client/locales/en-US/debugger.properties
--- a/devtools/client/debugger/new/src/actions/ui.js
+++ b/devtools/client/debugger/new/src/actions/ui.js
@@ -151,24 +151,28 @@ export function flashLineRange(location:
  * @static
  */
 export function clearHighlightLineRange() {
   return {
     type: "CLEAR_HIGHLIGHT_LINES"
   };
 }
 
-export function openConditionalPanel(location: ?SourceLocation) {
+export function openConditionalPanel(
+  location: ?SourceLocation,
+  log: boolean = false
+) {
   if (!location) {
     return;
   }
 
   return {
     type: "OPEN_CONDITIONAL_PANEL",
-    location
+    location,
+    log
   };
 }
 
 export function closeConditionalPanel() {
   return {
     type: "CLOSE_CONDITIONAL_PANEL"
   };
 }
--- a/devtools/client/debugger/new/src/components/Editor/ConditionalPanel.js
+++ b/devtools/client/debugger/new/src/components/Editor/ConditionalPanel.js
@@ -1,31 +1,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 import React, { PureComponent } from "react";
 import ReactDOM from "react-dom";
 import { connect } from "../../utils/connect";
+import classNames from "classnames";
 import "./ConditionalPanel.css";
 import { toEditorLine } from "../../utils/editor";
 import actions from "../../actions";
 
 import {
   getBreakpointForLocation,
-  getConditionalPanelLocation
+  getConditionalPanelLocation,
+  getLogPointStatus
 } from "../../selectors";
 
 import type { SourceLocation } from "../../types";
 
 type Props = {
   breakpoint: ?Object,
   setBreakpointCondition: Function,
   location: SourceLocation,
+  log: boolean,
   editor: Object,
   openConditionalPanel: typeof actions.openConditionalPanel,
   closeConditionalPanel: typeof actions.closeConditionalPanel
 };
 
 export class ConditionalPanel extends PureComponent<Props> {
   cbPanel: null | Object;
   input: ?HTMLInputElement;
@@ -55,17 +58,20 @@ export class ConditionalPanel extends Pu
     if (e.key === "Enter") {
       this.saveAndClose();
     } else if (e.key === "Escape") {
       this.props.closeConditionalPanel();
     }
   };
 
   setBreakpoint(condition: string) {
-    const { location } = this.props;
+    const { location, log } = this.props;
+    if (log) {
+      condition = `console.log(${condition})`;
+    }
     return this.props.setBreakpointCondition(location, { condition });
   }
 
   clearConditionalPanel() {
     if (this.cbPanel) {
       this.cbPanel.clear();
       this.cbPanel = null;
     }
@@ -132,30 +138,43 @@ export class ConditionalPanel extends Pu
       if (this.scrollParent) {
         this.scrollParent.addEventListener("scroll", this.repositionOnScroll);
         this.repositionOnScroll();
       }
     }
   }
 
   renderConditionalPanel(props: Props) {
-    const { breakpoint } = props;
-    const condition = breakpoint ? breakpoint.condition : "";
+    const { breakpoint, log } = props;
+    let condition = breakpoint ? breakpoint.condition : "";
+
+    if (log) {
+      if (condition && condition.match(/^console.log\(.*\)$/)) {
+        condition = condition.match(/^console.log\((.*)\)/)[1];
+      }
+    }
+
     const panel = document.createElement("div");
     ReactDOM.render(
       <div
-        className="conditional-breakpoint-panel"
+        className={classNames("conditional-breakpoint-panel", {
+          "log-point": log
+        })}
         onClick={() => this.keepFocusOnInput()}
         onBlur={this.props.closeConditionalPanel}
         ref={node => (this.panelNode = node)}
       >
         <div className="prompt">ยป</div>
         <input
           defaultValue={condition}
-          placeholder={L10N.getStr("editor.conditionalPanel.placeholder")}
+          placeholder={L10N.getStr(
+            log
+              ? "editor.conditionalPanel.logPoint.placeholder"
+              : "editor.conditionalPanel.placeholder"
+          )}
           onKeyDown={this.onKey}
           ref={input => {
             this.input = input;
             this.keepFocusOnInput();
           }}
         />
       </div>,
       panel
@@ -165,20 +184,21 @@ export class ConditionalPanel extends Pu
 
   render() {
     return null;
   }
 }
 
 const mapStateToProps = state => {
   const location = getConditionalPanelLocation(state);
-
+  const log = getLogPointStatus(state);
   return {
     breakpoint: getBreakpointForLocation(state, location),
-    location
+    location,
+    log
   };
 };
 
 const {
   setBreakpointCondition,
   openConditionalPanel,
   closeConditionalPanel
 } = actions;
--- a/devtools/client/debugger/new/src/components/Editor/GutterMenu.js
+++ b/devtools/client/debugger/new/src/components/Editor/GutterMenu.js
@@ -40,27 +40,35 @@ export function gutterMenu({
   event.stopPropagation();
   event.preventDefault();
 
   const gutterItems = {
     addBreakpoint: {
       id: "node-menu-add-breakpoint",
       label: L10N.getStr("editor.addBreakpoint")
     },
+    addLogPoint: {
+      id: "node-menu-add-log-point",
+      label: L10N.getStr("editor.addLogPoint")
+    },
     addConditional: {
       id: "node-menu-add-conditional-breakpoint",
       label: L10N.getStr("editor.addConditionalBreakpoint")
     },
     removeBreakpoint: {
       id: "node-menu-remove-breakpoint",
       label: L10N.getStr("editor.removeBreakpoint")
     },
+    editLogPoint: {
+      id: "node-menu-edit-log-point",
+      label: L10N.getStr("editor.editLogPoint")
+    },
     editConditional: {
       id: "node-menu-edit-conditional-breakpoint",
-      label: L10N.getStr("editor.editBreakpoint")
+      label: L10N.getStr("editor.editConditionalBreakpoint")
     },
     enableBreakpoint: {
       id: "node-menu-enable-breakpoint",
       label: L10N.getStr("editor.enableBreakpoint")
     },
     disableBreakpoint: {
       id: "node-menu-disable-breakpoint",
       label: L10N.getStr("editor.disableBreakpoint")
@@ -79,31 +87,45 @@ export function gutterMenu({
       if (isCbPanelOpen) {
         closeConditionalPanel();
       }
     },
     accelerator: L10N.getStr("toggleBreakpoint.key"),
     ...(breakpoint ? gutterItems.removeBreakpoint : gutterItems.addBreakpoint)
   };
 
+  const logPoint = {
+    accesskey: L10N.getStr("editor.addLogPoint.accesskey"),
+    disabled: false,
+    click: () =>
+      openConditionalPanel(
+        breakpoint ? breakpoint.location : { line, column, sourceId },
+        true
+      ),
+    accelerator: L10N.getStr("toggleCondPanel.key"),
+    ...(breakpoint && breakpoint.condition
+      ? gutterItems.editLogPoint
+      : gutterItems.addLogPoint)
+  };
+
   const conditionalBreakpoint = {
     accesskey: L10N.getStr("editor.addConditionalBreakpoint.accesskey"),
     disabled: false,
     // Leaving column undefined so pause points can be detected
     click: () =>
       openConditionalPanel(
         breakpoint ? breakpoint.location : { line, column, sourceId }
       ),
     accelerator: L10N.getStr("toggleCondPanel.key"),
     ...(breakpoint && breakpoint.condition
       ? gutterItems.editConditional
       : gutterItems.addConditional)
   };
 
-  const items = [toggleBreakpointItem, conditionalBreakpoint];
+  const items = [toggleBreakpointItem, conditionalBreakpoint, logPoint];
 
   if (isPaused) {
     const continueToHereItem = {
       accesskey: L10N.getStr("editor.continueToHere.accesskey"),
       disabled: false,
       click: () => continueToHere(line, column),
       ...gutterItems.continueToHere
     };
--- a/devtools/client/debugger/new/src/reducers/ui.js
+++ b/devtools/client/debugger/new/src/reducers/ui.js
@@ -35,29 +35,31 @@ export type UIState = {
   frameworkGroupingOn: boolean,
   orientation: OrientationType,
   viewport: ?Viewport,
   highlightedLineRange?: {
     start?: number,
     end?: number,
     sourceId?: number
   },
-  conditionalPanelLocation: null | SourceLocation
+  conditionalPanelLocation: null | SourceLocation,
+  isLogPoint: boolean
 };
 
 export const createUIState: () => Record<UIState> = makeRecord({
   selectedPrimaryPaneTab: "sources",
   activeSearch: null,
   contextMenu: {},
   shownSource: null,
   startPanelCollapsed: prefs.startPanelCollapsed,
   endPanelCollapsed: prefs.endPanelCollapsed,
   frameworkGroupingOn: prefs.frameworkGroupingOn,
   highlightedLineRange: undefined,
   conditionalPanelLocation: null,
+  isLogPoint: false,
   orientation: "horizontal",
   viewport: null
 });
 
 function update(
   state: Record<UIState> = createUIState(),
   action: Action
 ): Record<UIState> {
@@ -103,17 +105,19 @@ function update(
 
       return state.set("highlightedLineRange", lineRange);
 
     case "CLOSE_QUICK_OPEN":
     case "CLEAR_HIGHLIGHT_LINES":
       return state.set("highlightedLineRange", {});
 
     case "OPEN_CONDITIONAL_PANEL":
-      return state.set("conditionalPanelLocation", action.location);
+      return state
+        .set("conditionalPanelLocation", action.location)
+        .set("isLogPoint", action.log);
 
     case "CLOSE_CONDITIONAL_PANEL":
       return state.set("conditionalPanelLocation", null);
 
     case "SET_PRIMARY_PANE_TAB":
       return state.set("selectedPrimaryPaneTab", action.tabName);
 
     case "CLOSE_PROJECT_SEARCH": {
@@ -179,16 +183,20 @@ export function getHighlightedLineRange(
 }
 
 export function getConditionalPanelLocation(
   state: OuterState
 ): null | SourceLocation {
   return state.ui.get("conditionalPanelLocation");
 }
 
+export function getLogPointStatus(state: OuterState): boolean {
+  return state.ui.get("isLogPoint");
+}
+
 export function getOrientation(state: OuterState): OrientationType {
   return state.ui.get("orientation");
 }
 
 export function getViewport(state: OuterState) {
   return state.ui.get("viewport");
 }
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-columns.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-columns.js
@@ -17,20 +17,20 @@ function waitForElementFocus(dbg, el) {
 
 function hasCondition(marker) {
   return marker.classList.contains("has-condition");
 }
 
 async function setConditionalBreakpoint(dbg, index, condition) {
   const {
       addConditionalBreakpoint,
-      editBreakpoint
+      editConditionalBreakpoint
   } = selectors.gutterContextMenu;
   // Make this work with either add or edit menu items
-  const selector = `${addConditionalBreakpoint},${editBreakpoint}`;
+  const selector = `${addConditionalBreakpoint},${editConditionalBreakpoint}`;
 
   rightClickElement(dbg, "breakpointItem", index);
   selectContextMenuItem(dbg, selector);
   await waitForElement(dbg, "conditionalPanelInput");
   await assertConditionalBreakpointIsFocused(dbg);
 
   // Position cursor reliably at the end of the text.
   pressKey(dbg, "End");
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-cond.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-cond.js
@@ -36,20 +36,20 @@ function waitForElementFocus(dbg, el) {
 async function assertConditionalBreakpointIsFocused(dbg) {
   const input = findElement(dbg, "conditionalPanelInput");
   await waitForElementFocus(dbg, input);
 }
 
 async function setConditionalBreakpoint(dbg, index, condition) {
   const {
     addConditionalBreakpoint,
-    editBreakpoint
+    editConditionalBreakpoint
   } = selectors.gutterContextMenu;
   // Make this work with either add or edit menu items
-  const selector = `${addConditionalBreakpoint},${editBreakpoint}`;
+  const selector = `${addConditionalBreakpoint},${editConditionalBreakpoint}`;
 
   rightClickElement(dbg, "gutter", index);
   selectContextMenuItem(dbg, selector);
   await waitForElement(dbg, "conditionalPanelInput");
   await assertConditionalBreakpointIsFocused(dbg);
 
   // Position cursor reliably at the end of the text.
   pressKey(dbg, "End");
--- a/devtools/client/debugger/new/test/mochitest/helpers.js
+++ b/devtools/client/debugger/new/test/mochitest/helpers.js
@@ -1096,17 +1096,17 @@ const selectors = {
   scopeValue: i =>
     `.scopes-list .tree-node:nth-child(${i}) .object-delimiter + *`,
   frame: i => `.frames ul li:nth-child(${i})`,
   frames: ".frames ul li",
   gutter: i => `.CodeMirror-code *:nth-child(${i}) .CodeMirror-linenumber`,
   // These work for bobth the breakpoint listing and gutter marker
   gutterContextMenu: {
     addConditionalBreakpoint: "#node-menu-add-condition, #node-menu-add-conditional-breakpoint",
-    editBreakpoint: "#node-menu-edit-condition, #node-menu-edit-conditional-breakpoint"
+    editConditionalBreakpoint: "#node-menu-edit-condition, #node-menu-edit-conditional-breakpoint"
   },
   menuitem: i => `menupopup menuitem:nth-child(${i})`,
   pauseOnExceptions: ".pause-exceptions",
   breakpoint: ".CodeMirror-code > .new-breakpoint",
   highlightLine: ".CodeMirror-code > .highlight-line",
   debugLine: ".new-debug-line",
   debugErrorLine: ".new-debug-line-error",
   codeMirror: ".CodeMirror",
--- a/devtools/client/locales/en-US/debugger.properties
+++ b/devtools/client/locales/en-US/debugger.properties
@@ -489,23 +489,40 @@ editor.enableBreakpoint=Enable breakpoin
 editor.removeBreakpoint=Remove breakpoint
 
 # LOCALIZATION NOTE (editor.editBreakpoint): Editor gutter context menu item
 # for setting a breakpoint condition on a line.
 editor.editBreakpoint=Edit breakpoint
 
 # LOCALIZATION NOTE (editor.addConditionalBreakpoint): Editor gutter context
 # menu item for adding a breakpoint condition on a line.
-editor.addConditionalBreakpoint=Add conditional breakpoint
+editor.addConditionalBreakpoint=Add condition
 editor.addConditionalBreakpoint.accesskey=c
 
+# LOCALIZATION NOTE (editor.editBreakpoint): Editor gutter context menu item
+# for setting a breakpoint condition on a line.
+editor.editConditionalBreakpoint=Edit condition
+
+# LOCALIZATION NOTE (editor.addLogPoint): Editor gutter context
+# menu item for adding a log point on a line.
+editor.addLogPoint=Add log
+editor.addLogPoint.accesskey=l
+
+# LOCALIZATION NOTE (editor.editLogpoint): Editor gutter context menu item
+# for setting a log point on a line.
+editor.editLogPoint=Edit log
+
 # LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Placeholder text for
 # input element inside ConditionalPanel component
 editor.conditionalPanel.placeholder=This breakpoint will pause when the expression is true
 
+# LOCALIZATION NOTE (editor.conditionalPanel.logPoint.placeholder): Placeholder text for
+# input element inside ConditionalPanel component when a log point is set
+editor.conditionalPanel.logPoint.placeholder=This breakpoint will log the result of the expression
+
 # LOCALIZATION NOTE (editor.conditionalPanel.close): Tooltip text for
 # close button inside ConditionalPanel component
 editor.conditionalPanel.close=Cancel edit breakpoint and close
 
 # LOCALIZATION NOTE (editor.jumpToMappedLocation1): Context menu item
 # for navigating to a source mapped location
 editor.jumpToMappedLocation1=Jump to %S location
 editor.jumpToMappedLocation1.accesskey=m