Bug 1510629 - Update Debugger Frontend v105. r=dwalsh
authorJason Laster <jlaster@mozilla.com>
Wed, 28 Nov 2018 10:13:35 -0500
changeset 507835 f97a3d0a30b7b94ca82cb97138a40abdeb18043a
parent 507834 14624418e02365e7839c6ab3a17a950a6f065ea8
child 507836 5e5174a6ca61076e60edb00fa4223818ecd0c2fc
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdwalsh
bugs1510629
milestone65.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 1510629 - Update Debugger Frontend v105. r=dwalsh
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.css
devtools/client/debugger/new/dist/vendors.js
devtools/client/debugger/new/images/moz.build
devtools/client/debugger/new/images/next.svg
devtools/client/debugger/new/src/actions/ui.js
devtools/client/debugger/new/src/components/Editor/ColumnBreakpoint.js
devtools/client/debugger/new/src/components/Editor/ColumnBreakpoints.js
devtools/client/debugger/new/src/components/Editor/Tabs.js
devtools/client/debugger/new/src/components/Editor/index.js
devtools/client/debugger/new/src/components/Editor/moz.build
devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
devtools/client/debugger/new/src/reducers/ui.js
devtools/client/debugger/new/src/selectors/index.js
devtools/client/debugger/new/src/selectors/moz.build
devtools/client/debugger/new/src/selectors/visibleColumnBreakpoints.js
devtools/client/debugger/new/src/selectors/visiblePausePoints.js
devtools/client/debugger/new/src/utils/breakpoint/index.js
devtools/client/debugger/new/src/utils/dbg.js
devtools/client/debugger/new/src/utils/editor/index.js
devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
devtools/client/debugger/new/test/mochitest/helpers.js
devtools/client/shared/components/reps/images/input.svg
devtools/client/shared/components/reps/images/moz.build
devtools/client/shared/components/reps/reps.css
devtools/client/shared/components/reps/reps.js
devtools/client/shared/vendor/WASMPARSER_UPGRADING
devtools/client/shared/vendor/WasmDis.js
devtools/client/shared/vendor/WasmParser.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 104
+Version 105
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-103...release-104
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-104...release-105
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.2
 - babel-preset-react @6.24.1
 - react @16.4.1
 - react-dom @16.4.1
 - webpack @3.12.0
--- a/devtools/client/debugger/new/dist/debugger.css
+++ b/devtools/client/debugger/new/dist/debugger.css
@@ -272,16 +272,32 @@ button.jump-definition {
   vertical-align: middle;
 }
 
 .jump-definition:hover {
   background-color: var(--theme-highlight-blue);
 }
 
 /******************************************************************************/
+/* Invoke getter button */
+
+button.invoke-getter {
+  mask: url("resource://devtools/client/shared/components/reps/images/input.svg") no-repeat;
+  display: inline-block;
+  background-color: var(--comment-node-color);
+  height: 12px;
+  vertical-align:bottom;
+  border:none;
+}
+
+.invoke-getter:hover {
+  background-color: var(--theme-highlight-blue);
+}
+
+/******************************************************************************/
 /* "more…" ellipsis */
 .more-ellipsis {
   color: var(--comment-node-color);
 }
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
@@ -332,44 +348,49 @@ button.jump-definition {
 .tree-node[data-expandable="false"][aria-level="1"] {
   padding-inline-start: 15px
 }
 
 .tree .tree-node[data-expandable="true"] {
   cursor: default;
 }
 
-.tree-node img.arrow {
-  mask: url("resource://devtools/client/debugger/new/images/arrow.svg") no-repeat;
-  mask-size: 100%;
+.tree-node button.arrow {
+  background:url("resource://devtools/client/debugger/new/images/arrow.svg") no-repeat;
+  background-size:contain;
+  background-position:center center;
   width: 9px;
   height: 9px;
+  border:0;
+  padding:0;
   margin-inline-start: 1px;
   margin-inline-end: 4px;
-  background-color: var(--theme-splitter-color, #9B9B9B);
   transform: rotate(-90deg);
+  transform-origin: center center;
   transition: transform 0.125s ease;
   align-self: center;
-}
-
-html[dir="rtl"] .tree-node img.arrow {
+  -moz-context-properties: fill;
+  fill: var(--theme-splitter-color, #9B9B9B);
+}
+
+html[dir="rtl"] .tree-node button.arrow {
   transform: rotate(90deg);
 }
 
-.tree-node img.arrow.expanded.expanded {
+.tree-node button.arrow.expanded.expanded {
   transform: rotate(0deg);
  }
 
 .tree .tree-node.focused {
   color: white;
   background-color: var(--theme-selection-background, #0a84ff);
 }
 
-.tree-node.focused img.arrow {
-  background-color: currentColor;
+.tree-node.focused button.arrow {
+  fill: currentColor;
 }
 /* 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/. */
 
 .tree.object-inspector .node.object-node {
   display: inline-block;
 }
@@ -399,17 +420,17 @@ html[dir="rtl"] .tree-node img.arrow {
   content: "\2632   ";
   font-size: 1.1em;
 }
 
 .object-inspector .object-delimiter {
   color: var(--theme-comment);
 }
 
-.object-inspector .tree-node img.arrow {
+.object-inspector .tree-node .arrow {
   display: inline-block;
   vertical-align: middle;
 }
 
 /* Focused styles */
 .tree.object-inspector .tree-node.focused * {
   color: inherit;
 }
@@ -2660,45 +2681,33 @@ menuseparator {
   color: var(--theme-comment);
 }
 /* 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/>. */
 
 .call-site {
   position: relative;
-  border-bottom: 2px solid lightgrey;
+  display: inline;
   cursor: pointer;
 }
 
 .call-site::before {
   content: "";
   mask: url("resource://devtools/client/debugger/new/images/column-marker.svg") no-repeat 100% 100%;
   mask-size: contain;
   display: inline-block;
   background-color: var(--blue-55);
   opacity: 0.5;
   width: 9px;
   height: 12px;
 }
 
-.call-site-bp {
-  position: relative;
-  border-bottom: 2px solid #aed3ef;
-  cursor: pointer;
-}
-
-.call-site-bp::before {
-  content: "";
-  mask: url("resource://devtools/client/debugger/new/images/column-marker.svg") no-repeat 100% 100%;
-  mask-size: contain;
-  display: inline-block;
-  background-color: var(--blue-55);
-  width: 9px;
-  height: 12px;
+.call-site.active::before {
+  opacity: 1;
 }
 
 .theme-dark .call-site {
   border-bottom: none;
 }
 
 .theme-dark .call-site-bp {
   border-bottom: none;
@@ -2945,17 +2954,18 @@ html[dir="rtl"] .editor-mount {
 .editor-wrapper .CodeMirror-line {
   font-size: 11px;
 }
 
 .theme-dark .editor-wrapper .CodeMirror-line .cm-comment {
   color: var(--theme-comment);
 }
 
-.debug-expression {
+.debug-expression,
+.new-debug-line .call-site {
   background-color: var(--debug-expression-background);
 }
 
 debug-expression-error {
   background-color: var(--debug-expression-error-background);
 }
 
 .new-debug-line .CodeMirror-line {
@@ -3607,17 +3617,17 @@ html[dir="rtl"] .breakpoints-list .break
 }
 
 .annotation-logo {
   display: inline-block;
   width: 12px;
   margin-inline-start: 4px;
 }
 
-:root.theme-dark .annotation-logo svg path {
+:root.theme-dark .annotation-logo:not(.angular) svg path {
   fill: var(--theme-highlight-blue);
 }
 /* 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/>. */
 
 .event-listeners {
   list-style: none;
--- a/devtools/client/debugger/new/dist/parser-worker.js
+++ b/devtools/client/debugger/new/dist/parser-worker.js
@@ -1010,16 +1010,17 @@ exports.getObjectExpressionValue = getOb
 exports.getCode = getCode;
 exports.getVariableNames = getVariableNames;
 exports.getComments = getComments;
 exports.getSpecifiers = getSpecifiers;
 exports.isVariable = isVariable;
 exports.isComputedExpression = isComputedExpression;
 exports.getMemberExpression = getMemberExpression;
 exports.getVariables = getVariables;
+exports.getPatternIdentifiers = getPatternIdentifiers;
 exports.isTopLevel = isTopLevel;
 
 var _types = __webpack_require__(2268);
 
 var t = _interopRequireWildcard(_types);
 
 var _generator = __webpack_require__(2365);
 
@@ -1159,32 +1160,63 @@ function getVariables(dec) {
     return [];
   }
 
   if (t.isArrayPattern(dec.id)) {
     if (!dec.id.elements) {
       return [];
     }
 
-    // NOTE: it's possible that an element is empty
+    // NOTE: it's possible that an element is empty or has several variables
     // e.g. const [, a] = arr
+    // e.g. const [{a, b }] = 2
     return dec.id.elements.filter(element => element).map(element => {
       return {
-        name: t.isAssignmentPattern(element) ? element.left.name : element.name || element.argument.name,
+        name: t.isAssignmentPattern(element) ? element.left.name : element.name || element.argument && element.argument.name,
         location: element.loc
       };
-    });
+    }).filter(({ name }) => name);
   }
 
   return [{
     name: dec.id.name,
     location: dec.loc
   }];
 }
 
+function getPatternIdentifiers(pattern) {
+  let items = [];
+  if (t.isObjectPattern(pattern)) {
+    items = pattern.properties.map(({ value }) => value);
+  }
+
+  if (t.isArrayPattern(pattern)) {
+    items = pattern.elements;
+  }
+
+  return getIdentifiers(items);
+}
+
+function getIdentifiers(items) {
+  let ids = [];
+  items.forEach(function (item) {
+    if (t.isObjectPattern(item) || t.isArrayPattern(item)) {
+      ids = ids.concat(getPatternIdentifiers(item));
+    } else if (t.isIdentifier(item)) {
+      const { start, end } = item.loc;
+      ids.push({
+        name: item.name,
+        expression: item.name,
+        location: { start, end }
+      });
+    }
+  });
+  return ids;
+}
+
 // Top Level checks the number of "body" nodes in the ancestor chain
 // if the node is top-level, then it shoul only have one body.
 function isTopLevel(ancestors) {
   return ancestors.filter(ancestor => ancestor.key == "body").length == 1;
 }
 
 /***/ }),
 
@@ -1445,17 +1477,17 @@ function extractSymbol(path, symbols) {
     const { start, end } = path.node.loc;
     return symbols.identifiers.push({
       name: path.node.value,
       expression: (0, _helpers.getObjectExpressionValue)(path.parent),
       location: { start, end }
     });
   }
 
-  if (t.isIdentifier(path) && !t.isGenericTypeAnnotation(path.parent)) {
+  if (t.isIdentifier(path) && !t.isGenericTypeAnnotation(path.parent) && !t.isObjectProperty(path.parent) && !t.isArrayPattern(path.parent)) {
     let { start, end } = path.node.loc;
 
     // We want to include function params, but exclude the function name
     if (t.isClassMethod(path.parent) && !path.inList) {
       return;
     }
 
     if (t.isProperty(path.parentPath) && !(0, _helpers.isObjectShorthand)(path.parent)) {
@@ -1485,35 +1517,21 @@ function extractSymbol(path, symbols) {
       location: { start, end },
       expression: "this"
     });
   }
 
   if (t.isVariableDeclarator(path)) {
     const nodeId = path.node.id;
 
-    if (t.isArrayPattern(nodeId)) {
-      return;
-    }
-
-    const properties = nodeId.properties && t.objectPattern(nodeId.properties) ? nodeId.properties : [{
-      value: { name: nodeId.name },
-      loc: path.node.loc
-    }];
-
-    properties.forEach(function (property) {
-      const { start, end } = property.loc;
-      symbols.identifiers.push({
-        name: property.value.name,
-        expression: property.value.name,
-        location: { start, end }
-      });
-    });
-  }
-}
+    const ids = (0, _helpers.getPatternIdentifiers)(nodeId);
+    symbols.identifiers = [...symbols.identifiers, ...ids];
+  }
+}
+
 /* eslint-enable complexity */
 
 function extractSymbols(sourceId) {
   const symbols = {
     functions: [],
     variables: [],
     callExpressions: [],
     memberExpressions: [],
--- a/devtools/client/debugger/new/dist/vendors.css
+++ b/devtools/client/debugger/new/dist/vendors.css
@@ -49,44 +49,49 @@
 .tree-node[data-expandable="false"][aria-level="1"] {
   padding-inline-start: 15px
 }
 
 .tree .tree-node[data-expandable="true"] {
   cursor: default;
 }
 
-.tree-node img.arrow {
-  mask: url("resource://devtools/client/debugger/new/images/arrow.svg") no-repeat;
-  mask-size: 100%;
+.tree-node button.arrow {
+  background:url("resource://devtools/client/debugger/new/images/arrow.svg") no-repeat;
+  background-size:contain;
+  background-position:center center;
   width: 9px;
   height: 9px;
+  border:0;
+  padding:0;
   margin-inline-start: 1px;
   margin-inline-end: 4px;
-  background-color: var(--theme-splitter-color, #9B9B9B);
   transform: rotate(-90deg);
+  transform-origin: center center;
   transition: transform 0.125s ease;
   align-self: center;
+  -moz-context-properties: fill;
+  fill: var(--theme-splitter-color, #9B9B9B);
 }
 
-html[dir="rtl"] .tree-node img.arrow {
+html[dir="rtl"] .tree-node button.arrow {
   transform: rotate(90deg);
 }
 
-.tree-node img.arrow.expanded.expanded {
+.tree-node button.arrow.expanded.expanded {
   transform: rotate(0deg);
  }
 
 .tree .tree-node.focused {
   color: white;
   background-color: var(--theme-selection-background, #0a84ff);
 }
 
-.tree-node.focused img.arrow {
-  background-color: currentColor;
+.tree-node.focused button.arrow {
+  fill: currentColor;
 }
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* 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/. */
 
 .split-box {
   display: flex;
--- a/devtools/client/debugger/new/dist/vendors.js
+++ b/devtools/client/debugger/new/dist/vendors.js
@@ -4617,17 +4617,17 @@ module.exports = "<!-- This Source Code 
 
 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\" viewBox=\"0 0 16 16\"><path class=\"st0\" d=\"M9 9.3l3.6 3.6\"></path><ellipse fill=\"transparent\" cx=\"5.9\" cy=\"6.2\" rx=\"4.5\" ry=\"4.5\"></ellipse></svg>"
 
 /***/ }),
 
 /***/ 358:
 /***/ (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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><g fill-rule=\"evenodd\"><path d=\"M5 12.503l.052-9a.5.5 0 0 0-1-.006l-.052 9a.5.5 0 0 0 1 .006zM12 12.497l-.05-9A.488.488 0 0 0 11.474 3a.488.488 0 0 0-.473.503l.05 9a.488.488 0 0 0 .477.497.488.488 0 0 0 .473-.503z\"></path></g></svg>"
+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 version=\"1.1\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 16 16\"><path d=\"M5,13.5C5,13.8,4.7,14,4.5,14C4.2,14,4,13.8,4,13.5V2.6c0-0.3,0.2-0.5,0.5-0.5C4.7,2.1,5,2.3,5,2.6V13.5z\"></path><path d=\"M11.9,13.5c0,0.3-0.2,0.5-0.5,0.5s-0.5-0.2-0.5-0.5V2.6c0-0.3,0.2-0.5,0.5-0.5s0.5,0.2,0.5,0.5V13.5z\"></path></svg>"
 
 /***/ }),
 
 /***/ 359:
 /***/ (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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M10.483 13.995H5.517l-3.512-3.512V5.516l3.512-3.512h4.966l3.512 3.512v4.967l-3.512 3.512zm4.37-9.042l-3.807-3.805A.503.503 0 0 0 10.691 1H5.309a.503.503 0 0 0-.356.148L1.147 4.953A.502.502 0 0 0 1 5.308v5.383c0 .134.053.262.147.356l3.806 3.806a.503.503 0 0 0 .356.147h5.382a.503.503 0 0 0 .355-.147l3.806-3.806A.502.502 0 0 0 15 10.69V5.308a.502.502 0 0 0-.147-.355z\"></path><path d=\"M10 10.5a.5.5 0 1 0 1 0v-5a.5.5 0 1 0-1 0v5zM5 10.5a.5.5 0 1 0 1 0v-5a.5.5 0 0 0-1 0v5z\"></path></svg>"
 
@@ -4673,17 +4673,17 @@ module.exports = "<!-- This Source Code 
 
 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:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 20 16\" stroke=\"none\" fillrule=\"evenodd\"><rect x=\"3\" y=\"10\" width=\"3\" height=\"3\" rx=\"1\"></rect><rect x=\"12\" y=\"3\" width=\"2\" height=\"9\" rx=\"1\"></rect><rect transform=\"translate(13.000000, 7.500000) rotate(60.000000) translate(-13.000000, -7.500000) \" x=\"12\" y=\"3\" width=\"2\" height=\"9\" rx=\"1\"></rect><rect transform=\"translate(13.000000, 7.500000) rotate(-60.000000) translate(-13.000000, -7.500000) \" x=\"12\" y=\"3\" width=\"2\" height=\"9\" rx=\"1\"></rect></svg>"
 
 /***/ }),
 
 /***/ 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>"
+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\" viewBox=\"0 0 16 16\"><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>"
 
 /***/ }),
 
 /***/ 3631:
 /***/ (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\" viewBox=\"0 0 28 28\"><path fill=\"context-fill\" d=\"M15 11h-1V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v6H1a1 1 0 0 0 0 2h14a1 1 0 1 0 0-2z\"></path></svg>"
 
@@ -6007,17 +6007,17 @@ class ArrowExpander extends Component {
 
   render() {
     const { expanded } = this.props;
 
     const classNames = ["arrow"];
     if (expanded) {
       classNames.push("expanded");
     }
-    return _reactDomFactories2.default.img({
+    return _reactDomFactories2.default.button({
       className: classNames.join(" ")
     });
   }
 }
 
 const treeIndent = _reactDomFactories2.default.span({ className: "tree-indent" }, "\u200B");
 
 class TreeNode extends Component {
--- a/devtools/client/debugger/new/images/moz.build
+++ b/devtools/client/debugger/new/images/moz.build
@@ -16,17 +16,16 @@ DevToolsModules(
     'command-chevron.svg',
     'disable-pausing.svg',
     'domain.svg',
     'extension.svg',
     'file.svg',
     'folder.svg',
     'help.svg',
     'javascript.svg',
-    'next.svg',
     'pause.svg',
     'prettyPrint.svg',
     'react.svg',
     'resume.svg',
     'stepIn.svg',
     'stepOut.svg',
     'stepOver.svg',
     'tab.svg',
deleted file mode 100644
--- a/devtools/client/debugger/new/images/next.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-<!-- 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 version="1.1" xmlns:svg="http://www.w3.org/2000/svg"
-     xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16">
-<path d="M12.4,2.1c-0.3,0-0.5,0.2-0.5,0.5v4.8c0,0-0.1-0.1-0.1-0.1l-7.4-5C3.8,1.8,3,2.2,3,3v10c0,0.8,0.8,1.3,1.4,0.8l7.4-5
-    c0.1,0,0.1-0.1,0.1-0.1v4.8c0,0.3,0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5v-11C12.9,2.3,12.7,2.1,12.4,2.1z M3.9,13V3l7.4,5L3.9,13z"/>
-</svg>
\ No newline at end of file
--- a/devtools/client/debugger/new/src/actions/ui.js
+++ b/devtools/client/debugger/new/src/actions/ui.js
@@ -9,17 +9,17 @@ import {
   getPaneCollapse,
   getQuickOpenEnabled,
   getSource,
   getFileSearchQuery,
   getProjectDirectoryRoot
 } from "../selectors";
 import { selectSource } from "../actions/sources/select";
 import type { ThunkArgs, panelPositionType } from "./types";
-import { getEditor } from "../utils/editor";
+import { getEditor, getLocationsInViewport } from "../utils/editor";
 import { searchContents } from "./file-search";
 
 import type {
   ActiveSearchType,
   OrientationType,
   SelectedPrimaryPaneTabType
 } from "../reducers/ui";
 
@@ -196,11 +196,18 @@ export function setProjectDirectoryRoot(
 
     dispatch({
       type: "SET_PROJECT_DIRECTORY_ROOT",
       url: newRoot
     });
   };
 }
 
+export function updateViewport() {
+  return {
+    type: "SET_VIEWPORT",
+    viewport: getLocationsInViewport(getEditor())
+  };
+}
+
 export function setOrientation(orientation: OrientationType) {
   return { type: "SET_ORIENTATION", orientation };
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoint.js
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+// @flow
+import { PureComponent } from "react";
+import classnames from "classnames";
+import { getDocument } from "../../utils/editor";
+
+// eslint-disable-next-line max-len
+import type { ColumnBreakpoint as ColumnBreakpointType } from "../../selectors/visibleColumnBreakpoints";
+
+type Bookmark = {
+  clear: Function
+};
+
+type Props = {
+  callSite: Object,
+  editor: Object,
+  source: Object,
+  enabled: boolean,
+  toggleBreakpoint: (number, number) => void,
+  columnBreakpoint: ColumnBreakpointType
+};
+
+const breakpointImg = document.createElement("div");
+function makeBookmark(isActive, { onClick }) {
+  const bp = breakpointImg.cloneNode(true);
+  bp.className = classnames("call-site", {
+    active: isActive
+  });
+  bp.onclick = onClick;
+  return bp;
+}
+
+export default class CallSite extends PureComponent<Props> {
+  addCallSite: Function;
+  bookmark: ?Bookmark;
+
+  addCallSite = (nextProps: ?Props) => {
+    const { columnBreakpoint, source } = nextProps || this.props;
+    const sourceId = source.id;
+    const { line, column } = columnBreakpoint.location;
+    const widget = makeBookmark(columnBreakpoint.enabled, {
+      onClick: this.toggleBreakpoint
+    });
+    const doc = getDocument(sourceId);
+    this.bookmark = doc.setBookmark({ line: line - 1, ch: column }, { widget });
+  };
+
+  clearCallSite = () => {
+    if (this.bookmark) {
+      this.bookmark.clear();
+      this.bookmark = null;
+    }
+  };
+
+  toggleBreakpoint = () => {
+    const { columnBreakpoint, toggleBreakpoint } = this.props;
+    const { line, column } = columnBreakpoint.location;
+    toggleBreakpoint(line, column);
+  };
+
+  componentDidMount() {
+    this.addCallSite();
+  }
+
+  componentWillUnmount() {
+    this.clearCallSite();
+  }
+
+  componentDidUpdate() {
+    this.clearCallSite();
+    this.addCallSite();
+  }
+
+  render() {
+    return null;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoints.js
@@ -0,0 +1,67 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+import React, { Component } from "react";
+import { connect } from "react-redux";
+
+import ColumnBreakpoint from "./ColumnBreakpoint";
+import "./ColumnBreakpoints.css";
+
+import { getSelectedSource, visibleColumnBreakpoints } from "../../selectors";
+import { makeLocationId } from "../../utils/breakpoint";
+import actions from "../../actions";
+
+import type { Source } from "../../types";
+// eslint-disable-next-line max-len
+import type { ColumnBreakpoint as ColumnBreakpointType } from "../../selectors/visibleColumnBreakpoints";
+
+class ColumnBreakpoints extends Component {
+  props: {
+    editor: Object,
+    selectedSource: Source,
+    columnBreakpoints: ColumnBreakpointType[]
+  };
+
+  render() {
+    const {
+      editor,
+      columnBreakpoints,
+      selectedSource,
+      toggleBreakpoint
+    } = this.props;
+
+    if (!selectedSource || selectedSource.isBlackBoxed) {
+      return null;
+    }
+
+    let breakpoints;
+    editor.codeMirror.operation(() => {
+      breakpoints = columnBreakpoints.map(breakpoint => (
+        <ColumnBreakpoint
+          key={makeLocationId(breakpoint.location)}
+          columnBreakpoint={breakpoint}
+          editor={editor}
+          source={selectedSource}
+          toggleBreakpoint={toggleBreakpoint}
+        />
+      ));
+    });
+    return <div>{breakpoints}</div>;
+  }
+}
+
+const mapStateToProps = state => {
+  return {
+    selectedSource: getSelectedSource(state),
+    columnBreakpoints: visibleColumnBreakpoints(state)
+  };
+};
+
+const { toggleBreakpoint } = actions;
+const mapDispatchToProps = { toggleBreakpoint };
+
+export default connect(
+  mapStateToProps,
+  mapDispatchToProps
+)(ColumnBreakpoints);
--- a/devtools/client/debugger/new/src/components/Editor/Tabs.js
+++ b/devtools/client/debugger/new/src/components/Editor/Tabs.js
@@ -71,22 +71,26 @@ class Tabs extends PureComponent<Props, 
     if (!(prevProps === this.props)) {
       this.updateHiddenTabs();
     }
   }
 
   componentDidMount() {
     window.requestIdleCallback(this.updateHiddenTabs);
     window.addEventListener("resize", this.onResize);
-    window.document.querySelector(".editor-pane").addEventListener("resizeend", this.onResize);
+    window.document
+      .querySelector(".editor-pane")
+      .addEventListener("resizeend", this.onResize);
   }
 
   componentWillUnmount() {
     window.removeEventListener("resize", this.onResize);
-    window.document.querySelector(".editor-pane").removeEventListener("resizeend", this.onResize);
+    window.document
+      .querySelector(".editor-pane")
+      .removeEventListener("resizeend", this.onResize);
   }
 
   /*
    * Updates the hiddenSourceTabs state, by
    * finding the source tabs which are wrapped and are not on the top row.
    */
   updateHiddenTabs = () => {
     if (!this.refs.sourceTabs) {
--- a/devtools/client/debugger/new/src/components/Editor/index.js
+++ b/devtools/client/debugger/new/src/components/Editor/index.js
@@ -4,16 +4,18 @@
 
 // @flow
 
 import PropTypes from "prop-types";
 import React, { PureComponent } from "react";
 import ReactDOM from "react-dom";
 import { connect } from "react-redux";
 import classnames from "classnames";
+import { debounce } from "lodash";
+
 import { isLoaded } from "../../utils/source";
 import { isFirefox } from "devtools-environment";
 import { features } from "../../utils/prefs";
 import { getIndentation } from "../../utils/indentation";
 
 import {
   getActiveSearch,
   getSelectedLocation,
@@ -25,17 +27,17 @@ import {
 // Redux actions
 import actions from "../../actions";
 
 import Footer from "./Footer";
 import SearchBar from "./SearchBar";
 import HighlightLines from "./HighlightLines";
 import Preview from "./Preview";
 import Breakpoints from "./Breakpoints";
-import CallSites from "./CallSites";
+import ColumnBreakpoints from "./ColumnBreakpoints";
 import DebugLine from "./DebugLine";
 import HighlightLine from "./HighlightLine";
 import EmptyLines from "./EmptyLines";
 import GutterMenu from "./GutterMenu";
 import EditorMenu from "./EditorMenu";
 import ConditionalPanel from "./ConditionalPanel";
 
 import {
@@ -86,17 +88,18 @@ export type Props = {
   openConditionalPanel: (?number) => void,
   closeConditionalPanel: void => void,
   setContextMenu: (string, any) => void,
   continueToHere: (?number) => void,
   toggleBreakpoint: (?number) => void,
   toggleBreakpointsAtLine: (?number) => void,
   addOrToggleDisabledBreakpoint: (?number) => void,
   jumpToMappedLocation: any => void,
-  traverseResults: (boolean, Object) => void
+  traverseResults: (boolean, Object) => void,
+  updateViewport: void => void
 };
 
 type State = {
   editor: SourceEditor
 };
 
 class Editor extends PureComponent<Props, State> {
   $editorWrapper: ?HTMLDivElement;
@@ -152,16 +155,17 @@ class Editor extends PureComponent<Props
 
     codeMirror.on("gutterClick", this.onGutterClick);
 
     // Set code editor wrapper to be focusable
     codeMirrorWrapper.tabIndex = 0;
     codeMirrorWrapper.addEventListener("keydown", e => this.onKeyDown(e));
     codeMirrorWrapper.addEventListener("click", e => this.onClick(e));
     codeMirrorWrapper.addEventListener("mouseover", onMouseOver(codeMirror));
+    codeMirror.on("scroll", this.onEditorScroll);
 
     const toggleFoldMarkerVisibility = e => {
       if (node instanceof HTMLElement) {
         node
           .querySelectorAll(".CodeMirror-guttermarker-subtle")
           .forEach(elem => {
             elem.classList.toggle("visible");
           });
@@ -272,16 +276,18 @@ class Editor extends PureComponent<Props
 
   onToggleConditionalPanel = (key, e: KeyboardEvent) => {
     e.stopPropagation();
     e.preventDefault();
     const line = this.getCurrentLine();
     this.toggleConditionalPanel(line);
   };
 
+  onEditorScroll = debounce(this.props.updateViewport, 200);
+
   onKeyDown(e: KeyboardEvent) {
     const { codeMirror } = this.state.editor;
     const { key, target } = e;
     const codeWrapper = codeMirror.getWrapperElement();
     const textArea = codeWrapper.querySelector("textArea");
 
     if (key === "Escape" && target == textArea) {
       e.stopPropagation();
@@ -543,17 +549,19 @@ class Editor extends PureComponent<Props
         <EmptyLines editor={editor} />
         <Breakpoints editor={editor} />
         <Preview editor={editor} editorRef={this.$editorWrapper} />;
         <Footer horizontal={horizontal} />
         <HighlightLines editor={editor} />
         <EditorMenu editor={editor} />
         <GutterMenu editor={editor} />
         <ConditionalPanel editor={editor} />
-        {features.columnBreakpoints ? <CallSites editor={editor} /> : null}
+        {features.columnBreakpoints ? (
+          <ColumnBreakpoints editor={editor} />
+        ) : null}
       </div>
     );
   }
 
   renderSearchBar() {
     const { editor } = this.state;
 
     if (!editor) {
@@ -602,11 +610,12 @@ export default connect(
     openConditionalPanel: actions.openConditionalPanel,
     closeConditionalPanel: actions.closeConditionalPanel,
     setContextMenu: actions.setContextMenu,
     continueToHere: actions.continueToHere,
     toggleBreakpoint: actions.toggleBreakpoint,
     toggleBreakpointsAtLine: actions.toggleBreakpointsAtLine,
     addOrToggleDisabledBreakpoint: actions.addOrToggleDisabledBreakpoint,
     jumpToMappedLocation: actions.jumpToMappedLocation,
-    traverseResults: actions.traverseResults
+    traverseResults: actions.traverseResults,
+    updateViewport: actions.updateViewport
   }
 )(Editor);
--- a/devtools/client/debugger/new/src/components/Editor/moz.build
+++ b/devtools/client/debugger/new/src/components/Editor/moz.build
@@ -5,18 +5,18 @@
 
 DIRS += [
     'Preview',
 ]
 
 DebuggerModules(
     'Breakpoint.js',
     'Breakpoints.js',
-    'CallSite.js',
-    'CallSites.js',
+    'ColumnBreakpoint.js',
+    'ColumnBreakpoints.js',
     'ConditionalPanel.js',
     'DebugLine.js',
     'EditorMenu.js',
     'EmptyLines.js',
     'Footer.js',
     'GutterMenu.js',
     'HighlightLine.js',
     'HighlightLines.js',
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
@@ -10,17 +10,17 @@ import { connect } from "react-redux";
 
 import ExceptionOption from "./ExceptionOption";
 
 import Breakpoint from "./Breakpoint";
 import BreakpointHeading from "./BreakpointHeading";
 
 import actions from "../../../actions";
 import { getDisplayPath } from "../../../utils/source";
-import { makeLocationId } from "../../../utils/breakpoint";
+import { makeLocationId, sortBreakpoints } from "../../../utils/breakpoint";
 
 import { getSelectedSource, getBreakpointSources } from "../../../selectors";
 
 import type { Source } from "../../../types";
 import type { BreakpointSources } from "../../../selectors/breakpointSources";
 
 import "./Breakpoints.css";
 
@@ -74,24 +74,26 @@ class Breakpoints extends Component<Prop
     const { breakpointSources } = this.props;
     const sources = [
       ...breakpointSources.map(({ source, breakpoints }) => source)
     ];
 
     return [
       ...breakpointSources.map(({ source, breakpoints, i }) => {
         const path = getDisplayPath(source, sources);
+        const sortedBreakpoints = sortBreakpoints(breakpoints);
+
         return [
           <BreakpointHeading
             source={source}
             sources={sources}
             path={path}
             key={source.url}
           />,
-          ...breakpoints.map(breakpoint => (
+          ...sortedBreakpoints.map(breakpoint => (
             <Breakpoint
               breakpoint={breakpoint}
               source={source}
               key={makeLocationId(breakpoint.selectedLocation)}
             />
           ))
         ];
       })
--- a/devtools/client/debugger/new/src/reducers/ui.js
+++ b/devtools/client/debugger/new/src/reducers/ui.js
@@ -7,36 +7,39 @@
 /**
  * UI reducer
  * @module reducers/ui
  */
 
 import makeRecord from "../utils/makeRecord";
 import { prefs } from "../utils/prefs";
 
-import type { Source } from "../types";
+import type { Source, Range } from "../types";
 
 import type { Action, panelPositionType } from "../actions/types";
 import type { Record } from "../utils/makeRecord";
 
 export type ActiveSearchType = "project" | "file";
 
 export type OrientationType = "horizontal" | "vertical";
 
 export type SelectedPrimaryPaneTabType = "sources" | "outline";
 
+type Viewport = Range;
+
 export type UIState = {
   selectedPrimaryPaneTab: SelectedPrimaryPaneTabType,
   activeSearch: ?ActiveSearchType,
   contextMenu: any,
   shownSource: ?Source,
   startPanelCollapsed: boolean,
   endPanelCollapsed: boolean,
   frameworkGroupingOn: boolean,
   orientation: OrientationType,
+  viewport: ?Viewport,
   highlightedLineRange?: {
     start?: number,
     end?: number,
     sourceId?: number
   },
   conditionalPanelLine: null | number
 };
 
@@ -46,17 +49,18 @@ export const createUIState = makeRecord(
     activeSearch: null,
     contextMenu: {},
     shownSource: null,
     startPanelCollapsed: prefs.startPanelCollapsed,
     endPanelCollapsed: prefs.endPanelCollapsed,
     frameworkGroupingOn: prefs.frameworkGroupingOn,
     highlightedLineRange: undefined,
     conditionalPanelLine: null,
-    orientation: "horizontal"
+    orientation: "horizontal",
+    viewport: null
   }: UIState)
 );
 
 function update(
   state: Record<UIState> = createUIState(),
   action: Action
 ): Record<UIState> {
   switch (action.type) {
@@ -116,16 +120,20 @@ function update(
 
     case "CLOSE_PROJECT_SEARCH": {
       if (state.get("activeSearch") === "project") {
         return state.set("activeSearch", null);
       }
       return state;
     }
 
+    case "SET_VIEWPORT": {
+      return state.set("viewport", action.viewport);
+    }
+
     case "NAVIGATE": {
       return state.set("activeSearch", null).set("highlightedLineRange", {});
     }
 
     default: {
       return state;
     }
   }
@@ -175,9 +183,13 @@ export function getHighlightedLineRange(
 export function getConditionalPanelLine(state: OuterState): null | number {
   return state.ui.get("conditionalPanelLine");
 }
 
 export function getOrientation(state: OuterState): boolean {
   return state.ui.get("orientation");
 }
 
+export function getViewport(state: OuterState) {
+  return state.ui.get("viewport");
+}
+
 export default update;
--- a/devtools/client/debugger/new/src/selectors/index.js
+++ b/devtools/client/debugger/new/src/selectors/index.js
@@ -30,16 +30,18 @@ export {
 } from "./breakpointAtLocation";
 export { getVisibleBreakpoints } from "./visibleBreakpoints";
 export { inComponent } from "./inComponent";
 export { isSelectedFrameVisible } from "./isSelectedFrameVisible";
 export { getCallStackFrames } from "./getCallStackFrames";
 export { getVisibleSelectedFrame } from "./visibleSelectedFrame";
 export { getBreakpointSources } from "./breakpointSources";
 export { getXHRBreakpoints, shouldPauseOnAnyXHR } from "./breakpoints";
+export { visibleColumnBreakpoints } from "./visibleColumnBreakpoints";
+export { getVisiblePausePoints } from "./visiblePausePoints";
 
 import { objectInspector } from "devtools-reps";
 
 const { reducer } = objectInspector;
 
 Object.keys(reducer).forEach(function(key) {
   if (key === "default" || key === "__esModule") {
     return;
--- a/devtools/client/debugger/new/src/selectors/moz.build
+++ b/devtools/client/debugger/new/src/selectors/moz.build
@@ -11,10 +11,12 @@ DebuggerModules(
     'breakpointAtLocation.js',
     'breakpoints.js',
     'breakpointSources.js',
     'getCallStackFrames.js',
     'inComponent.js',
     'index.js',
     'isSelectedFrameVisible.js',
     'visibleBreakpoints.js',
+    'visibleColumnBreakpoints.js',
+    'visiblePausePoints.js',
     'visibleSelectedFrame.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/selectors/visibleColumnBreakpoints.js
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+import { groupBy, hasIn } from "lodash";
+import { createSelector } from "reselect";
+
+import { getViewport } from "../selectors";
+import { getVisibleBreakpoints } from "./visibleBreakpoints";
+import { getVisiblePausePoints } from "./visiblePausePoints";
+
+import type { Location } from "../types";
+
+export type ColumnBreakpoint = {|
+  +location: Location,
+  +enabled: boolean
+|};
+
+function contains(location, range) {
+  return (
+    location.line >= range.start.line &&
+    location.line <= range.end.line &&
+    location.column >= range.start.column &&
+    location.column <= range.end.column
+  );
+}
+
+function groupBreakpoints(breakpoints) {
+  const map = groupBy(breakpoints, ({ location }) => location.line);
+  for (const line in map) {
+    map[line] = groupBy(map[line], ({ location }) => location.column);
+  }
+
+  return map;
+}
+
+function isEnabled(location, breakpointMap) {
+  const { line, column } = location;
+  return hasIn(breakpointMap, [line, column]);
+}
+
+export function formatColumnBreakpoints(columnBreakpoints) {
+  console.log(
+    "Column Breakpoints\n\n",
+    columnBreakpoints
+      .map(
+        ({ location, enabled }) =>
+          `(${location.line}, ${location.column}) ${enabled}`
+      )
+      .join("\n")
+  );
+}
+
+export function getColumnBreakpoints(pausePoints, breakpoints, viewport) {
+  if (!pausePoints) {
+    return [];
+  }
+
+  const breakpointMap = groupBreakpoints(breakpoints);
+  const columnBreakpoints = pausePoints
+    .filter(({ types }) => types.break)
+    .filter(({ location }) => breakpointMap[location.line])
+    .filter(({ location }) => viewport && contains(location, viewport));
+
+  return columnBreakpoints.map(({ location }) => ({
+    location,
+    enabled: isEnabled(location, breakpointMap)
+  }));
+}
+
+export const visibleColumnBreakpoints = createSelector(
+  getVisiblePausePoints,
+  getVisibleBreakpoints,
+  getViewport,
+  getColumnBreakpoints
+);
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/selectors/visiblePausePoints.js
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+import { getSelectedSource } from "../reducers/sources";
+import { getPausePoints } from "../reducers/ast";
+import { convertToList } from "../utils/pause/pausePoints";
+
+export function getVisiblePausePoints(state) {
+  const source = getSelectedSource(state);
+  if (!source) {
+    return null;
+  }
+
+  const pausePoints = getPausePoints(state, source.id);
+  return convertToList(pausePoints);
+}
--- a/devtools/client/debugger/new/src/utils/breakpoint/index.js
+++ b/devtools/client/debugger/new/src/utils/breakpoint/index.js
@@ -1,20 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
+import { sortBy } from "lodash";
+
 import { getBreakpoint } from "../../selectors";
 import assert from "../assert";
 import { features } from "../prefs";
 
 export { getASTLocation, findScopeByName } from "./astBreakpointLocation";
 
+import type { FormattedBreakpoint } from "../../selectors/breakpointSources";
 import type {
   Location,
   PendingLocation,
   Breakpoint,
   PendingBreakpoint
 } from "../../types";
 
 import type { State } from "../../reducers/types";
@@ -174,8 +177,25 @@ export function createPendingBreakpoint(
   return {
     condition: bp.condition,
     disabled: bp.disabled,
     location: pendingLocation,
     astLocation: bp.astLocation,
     generatedLocation: pendingGeneratedLocation
   };
 }
+
+export function sortBreakpoints(breakpoints: FormattedBreakpoint[]) {
+  breakpoints = breakpoints.map(bp => ({
+    ...bp,
+    selectedLocation: {
+      ...bp.selectedLocation,
+      // If a breakpoint has an undefined column, we must provide a 0 value
+      // or the breakpoint will display after all explicit column numbers
+      column: bp.selectedLocation.column || 0
+    }
+  }));
+
+  return sortBy(breakpoints, [
+    "selectedLocation.line",
+    "selectedLocation.column"
+  ]);
+}
--- a/devtools/client/debugger/new/src/utils/dbg.js
+++ b/devtools/client/debugger/new/src/utils/dbg.js
@@ -49,16 +49,24 @@ function getCM() {
 }
 
 function _formatPausePoints(dbg: Object, url: string) {
   const source = dbg.helpers.findSource(url);
   const pausePoints = dbg.selectors.getPausePoints(source);
   console.log(formatPausePoints(source.text, pausePoints));
 }
 
+function _formatColumnBreapoints(dbg: Object) {
+  console.log(
+    dbg.selectors.formatColumnBreakpoints(
+      dbg.selectors.visibleColumnBreakpoints()
+    )
+  );
+}
+
 export function setupHelper(obj: Object) {
   const selectors = bindSelectors(obj);
   const dbg: Object = {
     ...obj,
     selectors,
     prefs,
     asyncStore,
     features,
@@ -67,17 +75,18 @@ export function setupHelper(obj: Object)
     helpers: {
       findSource: url => findSource(dbg, url),
       findSources: url => findSources(dbg, url),
       evaluate: (expression, cbk) => evaluate(dbg, expression, cbk),
       sendPacketToThread: (packet, cbk) => sendPacketToThread(dbg, packet, cbk),
       sendPacket: (packet, cbk) => sendPacket(dbg, packet, cbk)
     },
     formatters: {
-      pausePoints: url => _formatPausePoints(dbg, url)
+      pausePoints: url => _formatPausePoints(dbg, url),
+      visibleColumnBreakpoints: () => _formatColumnBreapoints(dbg)
     },
     _telemetry: {
       events: {}
     }
   };
 
   window.dbg = dbg;
 
--- a/devtools/client/debugger/new/src/utils/editor/index.js
+++ b/devtools/client/debugger/new/src/utils/editor/index.js
@@ -153,16 +153,44 @@ function isVisible(codeMirror: any, top:
     top,
     scrollTop,
     scrollTop + scrollArea.clientHeight - fontHeight
   );
 
   return inXView && inYView;
 }
 
+export function getLocationsInViewport(_editor: any) {
+  // Get scroll position
+  const charWidth = _editor.codeMirror.defaultCharWidth();
+  const scrollArea = _editor.codeMirror.getScrollInfo();
+  const { scrollLeft } = _editor.codeMirror.doc;
+  const rect = _editor.codeMirror.getWrapperElement().getBoundingClientRect();
+  const topVisibleLine = _editor.codeMirror.lineAtHeight(rect.top, "window");
+  const bottomVisibleLine = _editor.codeMirror.lineAtHeight(
+    rect.bottom,
+    "window"
+  );
+
+  const leftColumn = Math.floor(scrollLeft > 0 ? scrollLeft / charWidth : 0);
+  const rightPosition = scrollLeft + (scrollArea.clientWidth - 30);
+  const rightCharacter = Math.floor(rightPosition / charWidth);
+
+  return {
+    start: {
+      line: topVisibleLine,
+      column: leftColumn
+    },
+    end: {
+      line: bottomVisibleLine,
+      column: rightCharacter
+    }
+  };
+}
+
 export function markText(_editor: any, className, { start, end }: EditorRange) {
   return _editor.codeMirror.markText(
     { ch: start.column, line: start.line },
     { ch: end.column, line: end.line },
     { className }
   );
 }
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
@@ -28,43 +28,51 @@ add_task(async function() {
   await selectSource(dbg, "simple1");
   await waitForSelectedSource(dbg, "simple1");
 
   await addBreakpoint(dbg, "simple1", 1);
   await addBreakpoint(dbg, "simple1", 4);
   await addBreakpoint(dbg, "simple1", 5);
   await addBreakpoint(dbg, "simple1", 6);
 
-  openFirstBreakpointContextMenu(dbg)
+  openFirstBreakpointContextMenu(dbg);
   // select "Disable Others"
+  let dispatched = waitForDispatch(dbg, "DISABLE_BREAKPOINT", 3);
   selectMenuItem(dbg, 7);
   await waitForState(dbg, state =>
     dbg.selectors.getBreakpointsList(state)
       .every(bp => (bp.location.line !== 1) === bp.disabled)
   );
+  await dispatched;
   ok("breakpoint at 1 is the only enabled breakpoint");
 
-  openFirstBreakpointContextMenu(dbg)
+  openFirstBreakpointContextMenu(dbg);
   // select "Disable All"
+  dispatched = waitForDispatch(dbg, "DISABLE_ALL_BREAKPOINTS");
   selectMenuItem(dbg, 9);
   await waitForState(dbg, state =>
     dbg.selectors.getBreakpointsList(state).every(bp => bp.disabled)
   );
-  ok("all breakpoints are disabled")
+  await dispatched;
+  ok("all breakpoints are disabled");
 
-  openFirstBreakpointContextMenu(dbg)
+  openFirstBreakpointContextMenu(dbg);
   // select "Enable Others"
+  dispatched = waitForDispatch(dbg, "ENABLE_BREAKPOINT", 3);
   selectMenuItem(dbg, 3);
   await waitForState(dbg, state =>
     dbg.selectors.getBreakpointsList(state)
       .every(bp => (bp.location.line === 1) === bp.disabled)
   );
+  await dispatched;
   ok("all breakpoints except line 1 are enabled");
 
-  openFirstBreakpointContextMenu(dbg)
+  openFirstBreakpointContextMenu(dbg);
   // select "Remove Others"
+  dispatched = waitForDispatch(dbg, "REMOVE_BREAKPOINT", 3);
   selectMenuItem(dbg, 6);
   await waitForState(dbg, state =>
     dbg.selectors.getBreakpointsList(state).length === 1 &&
     dbg.selectors.getBreakpointsList(state)[0].location.line === 1
   );
+  await dispatched;
   ok("remaining breakpoint should be on line 1");
 });
--- a/devtools/client/debugger/new/test/mochitest/helpers.js
+++ b/devtools/client/debugger/new/test/mochitest/helpers.js
@@ -336,17 +336,17 @@ function assertDebugLine(dbg, line) {
     "There is only one line"
   );
 
   ok(isVisibleInEditor(dbg, debugLine), "debug line is visible");
 
   const markedSpans = lineInfo.handle.markedSpans;
   if (markedSpans && markedSpans.length > 0) {
     const classMatch = markedSpans.filter(
-      span => span.marker.className.includes("debug-expression")
+      span => span.marker.className && span.marker.className.includes("debug-expression")
     ).length > 0;
 
     ok(
       classMatch,
       "expression is highlighted as paused"
     );
   }
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/reps/images/input.svg
@@ -0,0 +1,7 @@
+<!-- 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" viewBox="0 0 12 12" fill="context-fill #0b0b0b">
+  <path d="M11.04 5.46L7.29 1.71a.75.75 0 0 0-1.06 1.06L9.45 6 6.23 9.21a.75.75 0 1 0 1.06 1.06l3.75-3.75c.3-.3.3-.77 0-1.06z"/>
+  <path d="M6.04 5.46L2.29 1.71a.75.75 0 0 0-1.06 1.06L4.45 6 1.23 9.21a.75.75 0 1 0 1.06 1.06l3.75-3.75c.3-.3.3-.77 0-1.06z"/>
+</svg>
\ No newline at end of file
--- a/devtools/client/shared/components/reps/images/moz.build
+++ b/devtools/client/shared/components/reps/images/moz.build
@@ -1,10 +1,11 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
+    'input.svg',
     'jump-definition.svg',
     'open-inspector.svg',
 )
--- a/devtools/client/shared/components/reps/reps.css
+++ b/devtools/client/shared/components/reps/reps.css
@@ -272,16 +272,32 @@ button.jump-definition {
   vertical-align: middle;
 }
 
 .jump-definition:hover {
   background-color: var(--theme-highlight-blue);
 }
 
 /******************************************************************************/
+/* Invoke getter button */
+
+button.invoke-getter {
+  mask: url("resource://devtools/client/shared/components/reps/images/input.svg") no-repeat;
+  display: inline-block;
+  background-color: var(--comment-node-color);
+  height: 12px;
+  vertical-align:bottom;
+  border:none;
+}
+
+.invoke-getter:hover {
+  background-color: var(--theme-highlight-blue);
+}
+
+/******************************************************************************/
 /* "more…" ellipsis */
 .more-ellipsis {
   color: var(--comment-node-color);
 }
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
@@ -332,44 +348,49 @@ button.jump-definition {
 .tree-node[data-expandable="false"][aria-level="1"] {
   padding-inline-start: 15px
 }
 
 .tree .tree-node[data-expandable="true"] {
   cursor: default;
 }
 
-.tree-node img.arrow {
-  mask: url("resource://devtools/client/debugger/new/images/arrow.svg") no-repeat;
-  mask-size: 100%;
+.tree-node button.arrow {
+  background:url("resource://devtools/client/debugger/new/images/arrow.svg") no-repeat;
+  background-size:contain;
+  background-position:center center;
   width: 9px;
   height: 9px;
+  border:0;
+  padding:0;
   margin-inline-start: 1px;
   margin-inline-end: 4px;
-  background-color: var(--theme-splitter-color, #9B9B9B);
   transform: rotate(-90deg);
+  transform-origin: center center;
   transition: transform 0.125s ease;
   align-self: center;
+  -moz-context-properties: fill;
+  fill: var(--theme-splitter-color, #9B9B9B);
 }
 
-html[dir="rtl"] .tree-node img.arrow {
+html[dir="rtl"] .tree-node button.arrow {
   transform: rotate(90deg);
 }
 
-.tree-node img.arrow.expanded.expanded {
+.tree-node button.arrow.expanded.expanded {
   transform: rotate(0deg);
  }
 
 .tree .tree-node.focused {
   color: white;
   background-color: var(--theme-selection-background, #0a84ff);
 }
 
-.tree-node.focused img.arrow {
-  background-color: currentColor;
+.tree-node.focused button.arrow {
+  fill: currentColor;
 }
 /* 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/. */
 
 .tree.object-inspector .node.object-node {
   display: inline-block;
 }
@@ -399,17 +420,17 @@ html[dir="rtl"] .tree-node img.arrow {
   content: "\2632   ";
   font-size: 1.1em;
 }
 
 .object-inspector .object-delimiter {
   color: var(--theme-comment);
 }
 
-.object-inspector .tree-node img.arrow {
+.object-inspector .tree-node .arrow {
   display: inline-block;
   vertical-align: middle;
 }
 
 /* Focused styles */
 .tree.object-inspector .tree-node.focused * {
   color: inherit;
 }
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -3766,17 +3766,17 @@ class ArrowExpander extends Component {
 
   render() {
     const { expanded } = this.props;
 
     const classNames = ["arrow"];
     if (expanded) {
       classNames.push("expanded");
     }
-    return _reactDomFactories2.default.img({
+    return _reactDomFactories2.default.button({
       className: classNames.join(" ")
     });
   }
 }
 
 const treeIndent = _reactDomFactories2.default.span({ className: "tree-indent" }, "\u200B");
 
 class TreeNode extends Component {
@@ -4988,16 +4988,18 @@ module.exports = {
 /***/ }),
 
 /***/ 3680:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
+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/>. */
 
 // Dependencies
 const dom = __webpack_require__(3643);
 const PropTypes = __webpack_require__(3642);
 const { wrapRender } = __webpack_require__(3644);
@@ -5009,28 +5011,50 @@ const { span } = dom;
  * properties enclosed in curly brackets.
  */
 Accessor.propTypes = {
   object: PropTypes.object.isRequired,
   mode: PropTypes.oneOf(Object.values(MODE))
 };
 
 function Accessor(props) {
-  const { object } = props;
+  const { object, evaluation, onInvokeGetterButtonClick } = props;
+
+  if (evaluation) {
+    const { Rep, Grip } = __webpack_require__(3647);
+    return span({
+      className: "objectBox objectBox-accessor objectTitle"
+    }, Rep(_extends({}, props, {
+      object: evaluation.getterValue,
+      mode: props.mode || MODE.TINY,
+      defaultRep: Grip
+    })));
+  }
+
+  if (hasGetter(object) && onInvokeGetterButtonClick) {
+    return dom.button({
+      className: "invoke-getter",
+      title: "Invoke getter",
+      onClick: event => {
+        onInvokeGetterButtonClick();
+        event.stopPropagation();
+      }
+    });
+  }
 
   const accessors = [];
   if (hasGetter(object)) {
     accessors.push("Getter");
   }
+
   if (hasSetter(object)) {
     accessors.push("Setter");
   }
-  const title = accessors.join(" & ");
-
-  return span({ className: "objectBox objectBox-accessor objectTitle" }, title);
+
+  return span({ className: "objectBox objectBox-accessor objectTitle" }, accessors.join(" & "));
 }
 
 function hasGetter(object) {
   return object && object.get && object.get.type !== "undefined";
 }
 
 function hasSetter(object) {
   return object && object.set && object.set.type !== "undefined";
--- a/devtools/client/shared/vendor/WASMPARSER_UPGRADING
+++ b/devtools/client/shared/vendor/WASMPARSER_UPGRADING
@@ -1,14 +1,14 @@
 # wasmparser version
 
-Current vesion is: 0.6.1
+Current vesion is: 0.6.2
 
 # Upgrade process
 
 1. Pull latest release from npm and extract WasmDis.js and WasmParser.js, e.g.
 
 ```
-curl https://registry.npmjs.org/wasmparser/-/wasmparser-0.6.1.tgz | tar -x --strip-components 2 package/dist/{WasmDis,WasmParser}.js
+curl https://registry.npmjs.org/wasmparser/-/wasmparser-0.6.2.tgz | tar -x --strip-components 2 package/dist/{WasmDis,WasmParser}.js
 ```
 
 2. Remove reference to source maps (last line)
 
--- a/devtools/client/shared/vendor/WasmDis.js
+++ b/devtools/client/shared/vendor/WasmDis.js
@@ -626,17 +626,17 @@ var WasmDisassembler = /** @class */ (fu
                 break;
             case 63 /* current_memory */:
             case 64 /* grow_memory */:
                 break;
             case 65 /* i32_const */:
                 this.appendBuffer(" " + operator.literal.toString());
                 break;
             case 66 /* i64_const */:
-                this.appendBuffer(" " + operator.literal.toDouble());
+                this.appendBuffer(" " + operator.literal.toString());
                 break;
             case 67 /* f32_const */:
                 this.appendBuffer(" " + formatFloat32(operator.literal));
                 break;
             case 68 /* f64_const */:
                 this.appendBuffer(" " + formatFloat64(operator.literal));
                 break;
         }
--- a/devtools/client/shared/vendor/WasmParser.js
+++ b/devtools/client/shared/vendor/WasmParser.js
@@ -409,16 +409,46 @@ var Int64 = /** @class */ (function () {
         }
         else {
             sum = 0;
             for (var i = 0; i < 8; i++, power *= 256)
                 sum += power * this._data[i];
         }
         return sum;
     };
+    Int64.prototype.toString = function () {
+        var low = (this._data[0] | (this._data[1] << 8) | (this._data[2] << 16) | (this._data[3] << 24)) >>> 0;
+        var high = (this._data[4] | (this._data[5] << 8) | (this._data[6] << 16) | (this._data[7] << 24)) >>> 0;
+        if (low === 0 && high === 0) {
+            return '0';
+        }
+        var sign = false;
+        if (high >> 31) {
+            high = 4294967296 - high;
+            if (low > 0) {
+                high--;
+                low = 4294967296 - low;
+            }
+            sign = true;
+        }
+        var buf = [];
+        while (high > 0) {
+            var t = ((high % 10) * 4294967296) + low;
+            high = Math.floor(high / 10);
+            buf.unshift((t % 10).toString());
+            low = Math.floor(t / 10);
+        }
+        while (low > 0) {
+            buf.unshift((low % 10).toString());
+            low = Math.floor(low / 10);
+        }
+        if (sign)
+            buf.unshift('-');
+        return buf.join('');
+    };
     Object.defineProperty(Int64.prototype, "data", {
         get: function () {
             return this._data;
         },
         enumerable: true,
         configurable: true
     });
     return Int64;