From d0fec9eaf37ce76878b3798d8779734fdbb52f0a Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Wed, 7 Sep 2016 15:41:51 -0400 Subject: [PATCH] Bug 1249558 - Part 2: Display the grid info bar for a grid area highlight r=pbro --- .../test/browser_inspector_highlighter-04.js | 12 +- .../browser_inspector_highlighter-options.js | 6 +- .../test/browser_inspector_infobar_01.js | 10 +- .../test/browser_inspector_infobar_02.js | 2 +- .../test/browser_inspector_infobar_03.js | 4 +- .../browser_inspector_pseudoclass-lock.js | 4 +- devtools/server/actors/highlighters.css | 44 +++--- .../server/actors/highlighters/box-model.js | 127 +++++------------ .../server/actors/highlighters/css-grid.js | 129 +++++++++++++++++- .../actors/highlighters/utils/markup.js | 75 ++++++++++ 10 files changed, 282 insertions(+), 131 deletions(-) diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-04.js b/devtools/client/inspector/test/browser_inspector_highlighter-04.js index 1f71b96e35bc..ca1de9eb745d 100644 --- a/devtools/client/inspector/test/browser_inspector_highlighter-04.js +++ b/devtools/client/inspector/test/browser_inspector_highlighter-04.js @@ -19,12 +19,12 @@ const ELEMENTS = ["box-model-root", "box-model-guide-right", "box-model-guide-bottom", "box-model-guide-left", - "box-model-nodeinfobar-container", - "box-model-nodeinfobar-tagname", - "box-model-nodeinfobar-id", - "box-model-nodeinfobar-classes", - "box-model-nodeinfobar-pseudo-classes", - "box-model-nodeinfobar-dimensions"]; + "box-model-infobar-container", + "box-model-infobar-tagname", + "box-model-infobar-id", + "box-model-infobar-classes", + "box-model-infobar-pseudo-classes", + "box-model-infobar-dimensions"]; add_task(function* () { let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL); diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-options.js b/devtools/client/inspector/test/browser_inspector_highlighter-options.js index bd24b6fe8fd8..6738f3cdc32f 100644 --- a/devtools/client/inspector/test/browser_inspector_highlighter-options.js +++ b/devtools/client/inspector/test/browser_inspector_highlighter-options.js @@ -24,7 +24,7 @@ const TEST_DATA = [ options: {}, checkHighlighter: function* (testActor) { let hidden = yield testActor.getHighlighterNodeAttribute( - "box-model-nodeinfobar-container", "hidden"); + "box-model-infobar-container", "hidden"); ok(!hidden, "Node infobar is visible"); hidden = yield testActor.getHighlighterNodeAttribute( @@ -64,8 +64,8 @@ const TEST_DATA = [ options: {hideInfoBar: true}, checkHighlighter: function* (testActor) { let hidden = yield testActor.getHighlighterNodeAttribute( - "box-model-nodeinfobar-container", "hidden"); - is(hidden, "true", "nodeinfobar has been hidden"); + "box-model-infobar-container", "hidden"); + is(hidden, "true", "infobar has been hidden"); } }, { diff --git a/devtools/client/inspector/test/browser_inspector_infobar_01.js b/devtools/client/inspector/test/browser_inspector_infobar_01.js index a4c3c12fe655..441cd9e483d2 100644 --- a/devtools/client/inspector/test/browser_inspector_infobar_01.js +++ b/devtools/client/inspector/test/browser_inspector_infobar_01.js @@ -64,26 +64,26 @@ function* testPosition(test, inspector, testActor) { yield selectAndHighlightNode(test.selector, inspector); let position = yield testActor.getHighlighterNodeAttribute( - "box-model-nodeinfobar-container", "position"); + "box-model-infobar-container", "position"); is(position, test.position, "Node " + test.selector + ": position matches"); let tag = yield testActor.getHighlighterNodeTextContent( - "box-model-nodeinfobar-tagname"); + "box-model-infobar-tagname"); is(tag, test.tag, "node " + test.selector + ": tagName matches."); if (test.id) { let id = yield testActor.getHighlighterNodeTextContent( - "box-model-nodeinfobar-id"); + "box-model-infobar-id"); is(id, "#" + test.id, "node " + test.selector + ": id matches."); } let classes = yield testActor.getHighlighterNodeTextContent( - "box-model-nodeinfobar-classes"); + "box-model-infobar-classes"); is(classes, test.classes, "node " + test.selector + ": classes match."); if (test.dims) { let dims = yield testActor.getHighlighterNodeTextContent( - "box-model-nodeinfobar-dimensions"); + "box-model-infobar-dimensions"); is(dims, test.dims, "node " + test.selector + ": dims match."); } } diff --git a/devtools/client/inspector/test/browser_inspector_infobar_02.js b/devtools/client/inspector/test/browser_inspector_infobar_02.js index cd639d6506c2..1b5bd5edf900 100644 --- a/devtools/client/inspector/test/browser_inspector_infobar_02.js +++ b/devtools/client/inspector/test/browser_inspector_infobar_02.js @@ -45,6 +45,6 @@ function* testNode(test, inspector, testActor) { yield selectAndHighlightNode(test.selector, inspector); let tag = yield testActor.getHighlighterNodeTextContent( - "box-model-nodeinfobar-tagname"); + "box-model-infobar-tagname"); is(tag, test.tag, "node " + test.selector + ": tagName matches."); } diff --git a/devtools/client/inspector/test/browser_inspector_infobar_03.js b/devtools/client/inspector/test/browser_inspector_infobar_03.js index 0c7010afde22..023d5bb385dd 100644 --- a/devtools/client/inspector/test/browser_inspector_infobar_03.js +++ b/devtools/client/inspector/test/browser_inspector_infobar_03.js @@ -26,7 +26,7 @@ function* testPositionAndStyle(test, inspector, testActor) { yield selectAndHighlightNode(test.selector, inspector); let style = yield testActor.getHighlighterNodeAttribute( - "box-model-nodeinfobar-container", "style"); + "box-model-infobar-container", "style"); is(style.split(";")[0], test.style, "Infobar shows on top of the page when page isn't scrolled"); @@ -34,7 +34,7 @@ function* testPositionAndStyle(test, inspector, testActor) { yield testActor.scrollWindow(0, 500); style = yield testActor.getHighlighterNodeAttribute( - "box-model-nodeinfobar-container", "style"); + "box-model-infobar-container", "style"); is(style.split(";")[0], test.style, "Infobar shows on top of the page even if the page is scrolled"); diff --git a/devtools/client/inspector/test/browser_inspector_pseudoclass-lock.js b/devtools/client/inspector/test/browser_inspector_pseudoclass-lock.js index 23a68344d398..ee31dfe32bf2 100644 --- a/devtools/client/inspector/test/browser_inspector_pseudoclass-lock.js +++ b/devtools/client/inspector/test/browser_inspector_pseudoclass-lock.js @@ -117,7 +117,7 @@ function* assertPseudoAddedToNode(inspector, testActor, ruleview) { info("Check that the infobar selector contains the pseudo-class"); let value = yield testActor.getHighlighterNodeTextContent( - "box-model-nodeinfobar-pseudo-classes"); + "box-model-infobar-pseudo-classes"); is(value, PSEUDO, "pseudo-class in infobar selector"); yield inspector.toolbox.highlighter.hideBoxModel(); } @@ -143,7 +143,7 @@ function* assertPseudoRemovedFromView(inspector, testActor, ruleview) { yield showPickerOn("#div-1", inspector); let value = yield testActor.getHighlighterNodeTextContent( - "box-model-nodeinfobar-pseudo-classes"); + "box-model-infobar-pseudo-classes"); is(value, "", "pseudo-class removed from infobar selector"); yield inspector.toolbox.highlighter.hideBoxModel(); } diff --git a/devtools/server/actors/highlighters.css b/devtools/server/actors/highlighters.css index ccfd9ac4e03b..6f0a681cecc6 100644 --- a/devtools/server/actors/highlighters.css +++ b/devtools/server/actors/highlighters.css @@ -99,9 +99,9 @@ shape-rendering: crispEdges; } -/* Highlighter - Node Infobar */ +/* Highlighter - Infobar */ -:-moz-native-anonymous .box-model-nodeinfobar-container { +:-moz-native-anonymous [class$=infobar-container] { position: absolute; max-width: 95%; @@ -109,10 +109,10 @@ font-size: 11px; } -:-moz-native-anonymous .box-model-nodeinfobar { +:-moz-native-anonymous [class$=infobar] { position: relative; - /* Centering the nodeinfobar in the container */ + /* Centering the infobar in the container */ left: -50%; padding: 5px; @@ -127,24 +127,24 @@ border: 1px solid var(--highlighter-bubble-border-color); } -:-moz-native-anonymous .box-model-nodeinfobar-container[hide-arrow] > .box-model-nodeinfobar { +:-moz-native-anonymous [class$=infobar-container][hide-arrow] > [class$=infobar] { margin: 7px 0; } /* Arrows */ -:-moz-native-anonymous .box-model-nodeinfobar-container > .box-model-nodeinfobar:before { +:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:before { left: calc(50% - 8px); border: 8px solid var(--highlighter-bubble-border-color); } -:-moz-native-anonymous .box-model-nodeinfobar-container > .box-model-nodeinfobar:after { +:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:after { left: calc(50% - 7px); border: 7px solid var(--highlighter-bubble-background-color); } -:-moz-native-anonymous .box-model-nodeinfobar-container > .box-model-nodeinfobar:before, -:-moz-native-anonymous .box-model-nodeinfobar-container > .box-model-nodeinfobar:after { +:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:before, +:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:after { content: ""; display: none; position: absolute; @@ -154,23 +154,23 @@ border-right-color: transparent; } -:-moz-native-anonymous .box-model-nodeinfobar-container[position="top"]:not([hide-arrow]) > .box-model-nodeinfobar:before, -:-moz-native-anonymous .box-model-nodeinfobar-container[position="top"]:not([hide-arrow]) > .box-model-nodeinfobar:after { +:-moz-native-anonymous [class$=infobar-container][position="top"]:not([hide-arrow]) > [class$=infobar]:before, +:-moz-native-anonymous [class$=infobar-container][position="top"]:not([hide-arrow]) > [class$=infobar]:after { border-bottom: 0; top: 100%; display: block; } -:-moz-native-anonymous .box-model-nodeinfobar-container[position="bottom"]:not([hide-arrow]) > .box-model-nodeinfobar:before, -:-moz-native-anonymous .box-model-nodeinfobar-container[position="bottom"]:not([hide-arrow]) > .box-model-nodeinfobar:after { +:-moz-native-anonymous [class$=infobar-container][position="bottom"]:not([hide-arrow]) > [class$=infobar]:before, +:-moz-native-anonymous [class$=infobar-container][position="bottom"]:not([hide-arrow]) > [class$=infobar]:after { border-top: 0; bottom: 100%; display: block; } -/* Text container */ +/* Text Container */ -:-moz-native-anonymous .box-model-nodeinfobar-text { +:-moz-native-anonymous [class$=infobar-text] { overflow: hidden; white-space: nowrap; direction: ltr; @@ -178,24 +178,24 @@ display: flex; } -:-moz-native-anonymous .box-model-nodeinfobar-tagname { +:-moz-native-anonymous .box-model-infobar-tagname { color: hsl(285,100%, 75%); } -:-moz-native-anonymous .box-model-nodeinfobar-id { +:-moz-native-anonymous .box-model-infobar-id { color: hsl(103, 46%, 54%); overflow: hidden; text-overflow: ellipsis; } -:-moz-native-anonymous .box-model-nodeinfobar-classes, -:-moz-native-anonymous .box-model-nodeinfobar-pseudo-classes { +:-moz-native-anonymous .box-model-infobar-classes, +:-moz-native-anonymous .box-model-infobar-pseudo-classes { color: hsl(200, 74%, 57%); overflow: hidden; text-overflow: ellipsis; } -:-moz-native-anonymous .box-model-nodeinfobar-dimensions { +:-moz-native-anonymous [class$=infobar-dimensions] { color: hsl(210, 30%, 85%); border-inline-start: 1px solid #5a6169; margin-inline-start: 6px; @@ -221,6 +221,10 @@ stroke: none; } +:-moz-native-anonymous .css-grid-infobar-areaname { + color: hsl(285,100%, 75%); +} + /* CSS Transform Highlighter */ :-moz-native-anonymous .css-transform-transformed { diff --git a/devtools/server/actors/highlighters/box-model.js b/devtools/server/actors/highlighters/box-model.js index b34f7e80cfab..ba8199aaf871 100644 --- a/devtools/server/actors/highlighters/box-model.js +++ b/devtools/server/actors/highlighters/box-model.js @@ -7,11 +7,10 @@ const { extend } = require("sdk/core/heritage"); const { AutoRefreshHighlighter } = require("./auto-refresh"); const { - CanvasFrameAnonymousContentHelper, + CanvasFrameAnonymousContentHelper, moveInfobar, getBindingElementAndPseudo, hasPseudoClassLock, getComputedStyle, createSVGNode, createNode, isNodeValid } = require("./utils/markup"); -const { getCurrentZoom, - setIgnoreLayoutChanges } = require("devtools/shared/layout/utils"); +const { setIgnoreLayoutChanges } = require("devtools/shared/layout/utils"); const inspector = require("devtools/server/actors/inspector"); // Note that the order of items in this array is important because it is used @@ -20,10 +19,6 @@ const BOX_MODEL_REGIONS = ["margin", "border", "padding", "content"]; const BOX_MODEL_SIDES = ["top", "right", "bottom", "left"]; // Width of boxmodelhighlighter guides const GUIDE_STROKE_WIDTH = 1; -// How high is the nodeinfobar (px). -const NODE_INFOBAR_HEIGHT = 34; -// What's the size of the nodeinfobar arrow (px). -const NODE_INFOBAR_ARROW_SIZE = 9; // FIXME: add ":visited" and ":link" after bug 713106 is fixed const PSEUDO_CLASSES = [":hover", ":active", ":focus"]; @@ -73,17 +68,17 @@ const PSEUDO_CLASSES = [":hover", ":active", ":focus"]; * * * - *
- *
- *
- *
- * Node name - * Node id - * .someClass - * :hover + *
+ *
+ *
+ *
+ * Node name + * Node id + * .someClass + * :hover *
*
- *
+ *
*
*
*
@@ -186,26 +181,26 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { let infobarContainer = createNode(this.win, { parent: rootWrapper, attributes: { - "class": "nodeinfobar-container", - "id": "nodeinfobar-container", + "class": "infobar-container", + "id": "infobar-container", "position": "top", "hidden": "true" }, prefix: this.ID_CLASS_PREFIX }); - let nodeInfobar = createNode(this.win, { + let infobar = createNode(this.win, { parent: infobarContainer, attributes: { - "class": "nodeinfobar" + "class": "infobar" }, prefix: this.ID_CLASS_PREFIX }); let texthbox = createNode(this.win, { - parent: nodeInfobar, + parent: infobar, attributes: { - "class": "nodeinfobar-text" + "class": "infobar-text" }, prefix: this.ID_CLASS_PREFIX }); @@ -213,8 +208,8 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { nodeType: "span", parent: texthbox, attributes: { - "class": "nodeinfobar-tagname", - "id": "nodeinfobar-tagname" + "class": "infobar-tagname", + "id": "infobar-tagname" }, prefix: this.ID_CLASS_PREFIX }); @@ -222,8 +217,8 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { nodeType: "span", parent: texthbox, attributes: { - "class": "nodeinfobar-id", - "id": "nodeinfobar-id" + "class": "infobar-id", + "id": "infobar-id" }, prefix: this.ID_CLASS_PREFIX }); @@ -231,8 +226,8 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { nodeType: "span", parent: texthbox, attributes: { - "class": "nodeinfobar-classes", - "id": "nodeinfobar-classes" + "class": "infobar-classes", + "id": "infobar-classes" }, prefix: this.ID_CLASS_PREFIX }); @@ -240,8 +235,8 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { nodeType: "span", parent: texthbox, attributes: { - "class": "nodeinfobar-pseudo-classes", - "id": "nodeinfobar-pseudo-classes" + "class": "infobar-pseudo-classes", + "id": "infobar-pseudo-classes" }, prefix: this.ID_CLASS_PREFIX }); @@ -249,8 +244,8 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { nodeType: "span", parent: texthbox, attributes: { - "class": "nodeinfobar-dimensions", - "id": "nodeinfobar-dimensions" + "class": "infobar-dimensions", + "id": "infobar-dimensions" }, prefix: this.ID_CLASS_PREFIX }); @@ -350,14 +345,14 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { * Hide the infobar */ _hideInfobar: function () { - this.getElement("nodeinfobar-container").setAttribute("hidden", "true"); + this.getElement("infobar-container").setAttribute("hidden", "true"); }, /** * Show the infobar */ _showInfobar: function () { - this.getElement("nodeinfobar-container").removeAttribute("hidden"); + this.getElement("infobar-container").removeAttribute("hidden"); this._updateInfobar(); }, @@ -379,7 +374,7 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { * Calculate an outer quad based on the quads returned by getAdjustedQuads. * The BoxModelHighlighter may highlight more than one boxes, so in this case * create a new quad that "contains" all of these quads. - * This is useful to position the guides and nodeinfobar. + * This is useful to position the guides and infobar. * This may happen if the BoxModelHighlighter is used to highlight an inline * element that spans line breaks. * @param {String} region The box-model region to get the outer quad for. @@ -676,11 +671,11 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { " \u00D7 " + parseFloat(rect.height.toPrecision(6)); - this.getElement("nodeinfobar-tagname").setTextContent(displayName); - this.getElement("nodeinfobar-id").setTextContent(id); - this.getElement("nodeinfobar-classes").setTextContent(classList); - this.getElement("nodeinfobar-pseudo-classes").setTextContent(pseudos); - this.getElement("nodeinfobar-dimensions").setTextContent(dim); + this.getElement("infobar-tagname").setTextContent(displayName); + this.getElement("infobar-id").setTextContent(id); + this.getElement("infobar-classes").setTextContent(classList); + this.getElement("infobar-pseudo-classes").setTextContent(pseudos); + this.getElement("infobar-dimensions").setTextContent(dim); this._moveInfobar(); }, @@ -690,57 +685,9 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { */ _moveInfobar: function () { let bounds = this._getOuterBounds(); - let winHeight = this.win.innerHeight * getCurrentZoom(this.win); - let winWidth = this.win.innerWidth * getCurrentZoom(this.win); - let winScrollY = this.win.scrollY; + let container = this.getElement("infobar-container"); - // Ensure that containerBottom and containerTop are at least zero to avoid - // showing tooltips outside the viewport. - let containerBottom = Math.max(0, bounds.bottom) + NODE_INFOBAR_ARROW_SIZE; - let containerTop = Math.min(winHeight, bounds.top); - let container = this.getElement("nodeinfobar-container"); - - // Can the bar be above the node? - let top; - if (containerTop < NODE_INFOBAR_HEIGHT) { - // No. Can we move the bar under the node? - if (containerBottom + NODE_INFOBAR_HEIGHT > winHeight) { - // No. Let's move it inside. Can we show it at the top of the element? - if (containerTop < winScrollY) { - // No. Window is scrolled past the top of the element. - top = 0; - } else { - // Yes. Show it at the top of the element - top = containerTop; - } - container.setAttribute("position", "overlap"); - } else { - // Yes. Let's move it under the node. - top = containerBottom; - container.setAttribute("position", "bottom"); - } - } else { - // Yes. Let's move it on top of the node. - top = containerTop - NODE_INFOBAR_HEIGHT; - container.setAttribute("position", "top"); - } - - // Align the bar with the box's center if possible. - let left = bounds.right - bounds.width / 2; - // Make sure the while infobar is visible. - let buffer = 100; - if (left < buffer) { - left = buffer; - container.setAttribute("hide-arrow", "true"); - } else if (left > winWidth - buffer) { - left = winWidth - buffer; - container.setAttribute("hide-arrow", "true"); - } else { - container.removeAttribute("hide-arrow"); - } - - let style = "top:" + top + "px;left:" + left + "px;"; - container.setAttribute("style", style); + moveInfobar(container, bounds, this.win); } }); exports.BoxModelHighlighter = BoxModelHighlighter; diff --git a/devtools/server/actors/highlighters/css-grid.js b/devtools/server/actors/highlighters/css-grid.js index 3e07bdfd2506..3c020303f682 100644 --- a/devtools/server/actors/highlighters/css-grid.js +++ b/devtools/server/actors/highlighters/css-grid.js @@ -9,7 +9,8 @@ const { AutoRefreshHighlighter } = require("./auto-refresh"); const { CanvasFrameAnonymousContentHelper, createNode, - createSVGNode + createSVGNode, + moveInfobar, } = require("./utils/markup"); const { getCurrentZoom, @@ -66,6 +67,14 @@ const GRID_LINES_PROPERTIES = { * * * + *
+ *
+ *
+ * Grid Area Name + * + *
+ *
+ *
*
*/ function CssGridHighlighter(highlighterEnv) { @@ -133,6 +142,52 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { prefix: this.ID_CLASS_PREFIX }); + // Building the grid infobar markup + let infobarContainer = createNode(this.win, { + parent: container, + attributes: { + "class": "infobar-container", + "id": "infobar-container", + "position": "top", + "hidden": "true" + }, + prefix: this.ID_CLASS_PREFIX + }); + + let infobar = createNode(this.win, { + parent: infobarContainer, + attributes: { + "class": "infobar" + }, + prefix: this.ID_CLASS_PREFIX + }); + + let textbox = createNode(this.win, { + parent: infobar, + attributes: { + "class": "infobar-text" + }, + prefix: this.ID_CLASS_PREFIX + }); + createNode(this.win, { + nodeType: "span", + parent: textbox, + attributes: { + "class": "infobar-areaname", + "id": "infobar-areaname" + }, + prefix: this.ID_CLASS_PREFIX + }); + createNode(this.win, { + nodeType: "span", + parent: textbox, + attributes: { + "class": "infobar-dimensions", + "id": "infobar-dimensions" + }, + prefix: this.ID_CLASS_PREFIX + }); + return container; }, @@ -246,6 +301,61 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { return true; }, + /** + * Update the grid information displayed in the grid info bar. + * + * @param {GridArea} area + * The grid area object. + * @param {Number} x1 + * The first x-coordinate of the grid area rectangle. + * @param {Number} x2 + * The second x-coordinate of the grid area rectangle. + * @param {Number} y1 + * The first y-coordinate of the grid area rectangle. + * @param {Number} y2 + * The second y-coordinate of the grid area rectangle. + */ + _updateInfobar(area, x1, x2, y1, y2) { + let width = x2 - x1; + let height = y2 - y1; + let dim = parseFloat(width.toPrecision(6)) + + " \u00D7 " + + parseFloat(height.toPrecision(6)); + + this.getElement("infobar-areaname").setTextContent(area.name); + this.getElement("infobar-dimensions").setTextContent(dim); + + this._moveInfobar(x1, x2, y1, y2); + }, + + /** + * Move the grid infobar to the right place in the highlighter. + * + * @param {Number} x1 + * The first x-coordinate of the grid area rectangle. + * @param {Number} x2 + * The second x-coordinate of the grid area rectangle. + * @param {Number} y1 + * The first y-coordinate of the grid area rectangle. + * @param {Number} y2 + * The second y-coordinate of the grid area rectangle. + */ + _moveInfobar(x1, x2, y1, y2) { + let bounds = { + bottom: y2, + height: y2 - y1, + left: x1, + right: x2, + top: y1, + width: x2 - x1, + x: x1, + y: y1, + }; + let container = this.getElement("infobar-container"); + + moveInfobar(container, bounds, this.win); + }, + clearCanvas() { let ratio = parseFloat((this.win.devicePixelRatio || 1).toFixed(2)); let width = this.win.innerWidth; @@ -457,6 +567,12 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { "L" + x2 + "," + y2 + " " + "L" + x1 + "," + y2; paths.push(path); + + // Update and show the info bar when only displaying a single grid area. + if (!renderAllGridAreas) { + this._updateInfobar(area, x1, x2, y1, y2); + this._showInfoBar(); + } } } @@ -465,12 +581,13 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { }, /** - * Hide the highlighter and the canvas. + * Hide the highlighter, the canvas and the infobar. */ _hide() { setIgnoreLayoutChanges(true); this._hideGrid(); this._hideGridArea(); + this._hideInfoBar(); setIgnoreLayoutChanges(false, this.currentNode.ownerDocument.documentElement); }, @@ -490,6 +607,14 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, { this.getElement("elements").removeAttribute("hidden"); }, + _hideInfoBar() { + this.getElement("infobar-container").setAttribute("hidden", "true"); + }, + + _showInfoBar() { + this.getElement("infobar-container").removeAttribute("hidden"); + }, + }); exports.CssGridHighlighter = CssGridHighlighter; diff --git a/devtools/server/actors/highlighters/utils/markup.js b/devtools/server/actors/highlighters/utils/markup.js index d496e0bc36d2..e9ee3796a11d 100644 --- a/devtools/server/actors/highlighters/utils/markup.js +++ b/devtools/server/actors/highlighters/utils/markup.js @@ -37,6 +37,10 @@ const SVG_NS = "http://www.w3.org/2000/svg"; const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; const STYLESHEET_URI = "resource://devtools/server/actors/" + "highlighters.css"; +// How high is the infobar (px). +const INFOBAR_HEIGHT = 34; +// What's the size of the infobar arrow (px). +const INFOBAR_ARROW_SIZE = 9; const _tokens = Symbol("classList/tokens"); @@ -517,3 +521,74 @@ CanvasFrameAnonymousContentHelper.prototype = { } }; exports.CanvasFrameAnonymousContentHelper = CanvasFrameAnonymousContentHelper; + +/** + * Move the infobar to the right place in the highlighter. This helper method is utilized + * in both css-grid.js and box-model.js to help position the infobar in an appropriate + * space over the highlighted node element or grid area. The infobar is used to display + * relevant information about the highlighted item (ex, node or grid name and dimensions). + * + * This method will first try to position the infobar to top or bottom of the container + * such that it has enough space for the height of the infobar. Afterwards, it will try + * to horizontally center align with the container element if possible. + * + * @param {DOMNode} container + * The container element which will be used to position the infobar. + * @param {Object} bounds + * The content bounds of the container element. + * @param {Window} win + * The window object. + */ +function moveInfobar(container, bounds, win) { + let winHeight = win.innerHeight * getCurrentZoom(win); + let winWidth = win.innerWidth * getCurrentZoom(win); + let winScrollY = win.scrollY; + + // Ensure that containerBottom and containerTop are at least zero to avoid + // showing tooltips outside the viewport. + let containerBottom = Math.max(0, bounds.bottom) + INFOBAR_ARROW_SIZE; + let containerTop = Math.min(winHeight, bounds.top); + + // Can the bar be above the node? + let top; + if (containerTop < INFOBAR_HEIGHT) { + // No. Can we move the bar under the node? + if (containerBottom + INFOBAR_HEIGHT > winHeight) { + // No. Let's move it inside. Can we show it at the top of the element? + if (containerTop < winScrollY) { + // No. Window is scrolled past the top of the element. + top = 0; + } else { + // Yes. Show it at the top of the element + top = containerTop; + } + container.setAttribute("position", "overlap"); + } else { + // Yes. Let's move it under the node. + top = containerBottom; + container.setAttribute("position", "bottom"); + } + } else { + // Yes. Let's move it on top of the node. + top = containerTop - INFOBAR_HEIGHT; + container.setAttribute("position", "top"); + } + + // Align the bar with the box's center if possible. + let left = bounds.right - bounds.width / 2; + // Make sure the while infobar is visible. + let buffer = 100; + if (left < buffer) { + left = buffer; + container.setAttribute("hide-arrow", "true"); + } else if (left > winWidth - buffer) { + left = winWidth - buffer; + container.setAttribute("hide-arrow", "true"); + } else { + container.removeAttribute("hide-arrow"); + } + + let style = "top:" + top + "px;left:" + left + "px;"; + container.setAttribute("style", style); +} +exports.moveInfobar = moveInfobar;