summaryrefslogtreecommitdiffstats
path: root/devtools/server/actors/highlighters/css-grid.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/server/actors/highlighters/css-grid.js')
-rw-r--r--devtools/server/actors/highlighters/css-grid.js737
1 files changed, 737 insertions, 0 deletions
diff --git a/devtools/server/actors/highlighters/css-grid.js b/devtools/server/actors/highlighters/css-grid.js
new file mode 100644
index 000000000..0ed1ee961
--- /dev/null
+++ b/devtools/server/actors/highlighters/css-grid.js
@@ -0,0 +1,737 @@
+/* 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/. */
+
+"use strict";
+
+const Services = require("Services");
+const { extend } = require("sdk/core/heritage");
+const { AutoRefreshHighlighter } = require("./auto-refresh");
+const {
+ CanvasFrameAnonymousContentHelper,
+ createNode,
+ createSVGNode,
+ moveInfobar,
+} = require("./utils/markup");
+const {
+ getCurrentZoom,
+ setIgnoreLayoutChanges
+} = require("devtools/shared/layout/utils");
+const { stringifyGridFragments } = require("devtools/server/actors/utils/css-grid-utils");
+
+const CSS_GRID_ENABLED_PREF = "layout.css.grid.enabled";
+const ROWS = "rows";
+const COLUMNS = "cols";
+const GRID_LINES_PROPERTIES = {
+ "edge": {
+ lineDash: [0, 0],
+ strokeStyle: "#4B0082"
+ },
+ "explicit": {
+ lineDash: [5, 3],
+ strokeStyle: "#8A2BE2"
+ },
+ "implicit": {
+ lineDash: [2, 2],
+ strokeStyle: "#9370DB"
+ }
+};
+
+// px
+const GRID_GAP_PATTERN_WIDTH = 14;
+const GRID_GAP_PATTERN_HEIGHT = 14;
+const GRID_GAP_PATTERN_LINE_DASH = [5, 3];
+const GRID_GAP_PATTERN_STROKE_STYLE = "#9370DB";
+
+/**
+ * Cached used by `CssGridHighlighter.getGridGapPattern`.
+ */
+const gCachedGridPattern = new WeakMap();
+// WeakMap key for the Row grid pattern.
+const ROW_KEY = {};
+// WeakMap key for the Column grid pattern.
+const COLUMN_KEY = {};
+
+/**
+ * The CssGridHighlighter is the class that overlays a visual grid on top of
+ * display:grid elements.
+ *
+ * Usage example:
+ * let h = new CssGridHighlighter(env);
+ * h.show(node, options);
+ * h.hide();
+ * h.destroy();
+ *
+ * Available Options:
+ * - showGridArea(areaName)
+ * @param {String} areaName
+ * Shows the grid area highlight for the given area name.
+ * - showAllGridAreas
+ * Shows all the grid area highlights for the current grid.
+ * - showGridLineNumbers(isShown)
+ * @param {Boolean}
+ * Displays the grid line numbers on the grid lines if isShown is true.
+ * - showInfiniteLines(isShown)
+ * @param {Boolean} isShown
+ * Displays an infinite line to represent the grid lines if isShown is true.
+ *
+ * Structure:
+ * <div class="highlighter-container">
+ * <canvas id="css-grid-canvas" class="css-grid-canvas">
+ * <svg class="css-grid-elements" hidden="true">
+ * <g class="css-grid-regions">
+ * <path class="css-grid-areas" points="..." />
+ * </g>
+ * </svg>
+ * <div class="css-grid-infobar-container">
+ * <div class="css-grid-infobar">
+ * <div class="css-grid-infobar-text">
+ * <span class="css-grid-infobar-areaname">Grid Area Name</span>
+ * <span class="css-grid-infobar-dimensions"Grid Area Dimensions></span>
+ * </div>
+ * </div>
+ * </div>
+ * </div>
+ */
+function CssGridHighlighter(highlighterEnv) {
+ AutoRefreshHighlighter.call(this, highlighterEnv);
+
+ this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
+ this._buildMarkup.bind(this));
+
+ this.onNavigate = this.onNavigate.bind(this);
+ this.onWillNavigate = this.onWillNavigate.bind(this);
+
+ this.highlighterEnv.on("navigate", this.onNavigate);
+ this.highlighterEnv.on("will-navigate", this.onWillNavigate);
+}
+
+CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
+ typeName: "CssGridHighlighter",
+
+ ID_CLASS_PREFIX: "css-grid-",
+
+ _buildMarkup() {
+ let container = createNode(this.win, {
+ attributes: {
+ "class": "highlighter-container"
+ }
+ });
+
+ // We use a <canvas> element so that we can draw an arbitrary number of lines
+ // which wouldn't be possible with HTML or SVG without having to insert and remove
+ // the whole markup on every update.
+ createNode(this.win, {
+ parent: container,
+ nodeType: "canvas",
+ attributes: {
+ "id": "canvas",
+ "class": "canvas",
+ "hidden": "true"
+ },
+ prefix: this.ID_CLASS_PREFIX
+ });
+
+ // Build the SVG element
+ let svg = createSVGNode(this.win, {
+ nodeType: "svg",
+ parent: container,
+ attributes: {
+ "id": "elements",
+ "width": "100%",
+ "height": "100%",
+ "hidden": "true"
+ },
+ prefix: this.ID_CLASS_PREFIX
+ });
+
+ let regions = createSVGNode(this.win, {
+ nodeType: "g",
+ parent: svg,
+ attributes: {
+ "class": "regions"
+ },
+ prefix: this.ID_CLASS_PREFIX
+ });
+
+ createSVGNode(this.win, {
+ nodeType: "path",
+ parent: regions,
+ attributes: {
+ "class": "areas",
+ "id": "areas"
+ },
+ 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;
+ },
+
+ destroy() {
+ this.highlighterEnv.off("navigate", this.onNavigate);
+ this.highlighterEnv.off("will-navigate", this.onWillNavigate);
+ this.markup.destroy();
+
+ // Clear the pattern cache to avoid dead object exceptions (Bug 1342051).
+ gCachedGridPattern.delete(ROW_KEY);
+ gCachedGridPattern.delete(COLUMN_KEY);
+
+ AutoRefreshHighlighter.prototype.destroy.call(this);
+ },
+
+ getElement(id) {
+ return this.markup.getElement(this.ID_CLASS_PREFIX + id);
+ },
+
+ get ctx() {
+ return this.canvas.getCanvasContext("2d");
+ },
+
+ get canvas() {
+ return this.getElement("canvas");
+ },
+
+ /**
+ * Gets the grid gap pattern used to render the gap regions.
+ *
+ * @param {Object} dimension
+ * Refers to the WeakMap key for the grid dimension type which is either the
+ * constant COLUMN or ROW.
+ * @return {CanvasPattern} grid gap pattern.
+ */
+ getGridGapPattern(dimension) {
+ if (gCachedGridPattern.has(dimension)) {
+ return gCachedGridPattern.get(dimension);
+ }
+
+ // Create the diagonal lines pattern for the rendering the grid gaps.
+ let canvas = createNode(this.win, { nodeType: "canvas" });
+ canvas.width = GRID_GAP_PATTERN_WIDTH;
+ canvas.height = GRID_GAP_PATTERN_HEIGHT;
+
+ let ctx = canvas.getContext("2d");
+ ctx.setLineDash(GRID_GAP_PATTERN_LINE_DASH);
+ ctx.beginPath();
+ ctx.translate(.5, .5);
+
+ if (dimension === COLUMN_KEY) {
+ ctx.moveTo(0, 0);
+ ctx.lineTo(GRID_GAP_PATTERN_WIDTH, GRID_GAP_PATTERN_HEIGHT);
+ } else {
+ ctx.moveTo(GRID_GAP_PATTERN_WIDTH, 0);
+ ctx.lineTo(0, GRID_GAP_PATTERN_HEIGHT);
+ }
+
+ ctx.strokeStyle = GRID_GAP_PATTERN_STROKE_STYLE;
+ ctx.stroke();
+
+ let pattern = ctx.createPattern(canvas, "repeat");
+ gCachedGridPattern.set(dimension, pattern);
+ return pattern;
+ },
+
+ /**
+ * Called when the page navigates. Used to clear the cached gap patterns and avoid
+ * using DeadWrapper objects as gap patterns the next time.
+ */
+ onNavigate() {
+ gCachedGridPattern.delete(ROW_KEY);
+ gCachedGridPattern.delete(COLUMN_KEY);
+ },
+
+ onWillNavigate({ isTopLevel }) {
+ if (isTopLevel) {
+ this.hide();
+ }
+ },
+
+ _show() {
+ if (Services.prefs.getBoolPref(CSS_GRID_ENABLED_PREF) && !this.isGrid()) {
+ this.hide();
+ return false;
+ }
+
+ return this._update();
+ },
+
+ /**
+ * Shows the grid area highlight for the given area name.
+ *
+ * @param {String} areaName
+ * Grid area name.
+ */
+ showGridArea(areaName) {
+ this.renderGridArea(areaName);
+ this._showGridArea();
+ },
+
+ /**
+ * Shows all the grid area highlights for the current grid.
+ */
+ showAllGridAreas() {
+ this.renderGridArea();
+ this._showGridArea();
+ },
+
+ /**
+ * Clear the grid area highlights.
+ */
+ clearGridAreas() {
+ let box = this.getElement("areas");
+ box.setAttribute("d", "");
+ },
+
+ /**
+ * Checks if the current node has a CSS Grid layout.
+ *
+ * @return {Boolean} true if the current node has a CSS grid layout, false otherwise.
+ */
+ isGrid() {
+ return this.currentNode.getGridFragments().length > 0;
+ },
+
+ /**
+ * The AutoRefreshHighlighter's _hasMoved method returns true only if the
+ * element's quads have changed. Override it so it also returns true if the
+ * element's grid has changed (which can happen when you change the
+ * grid-template-* CSS properties with the highlighter displayed).
+ */
+ _hasMoved() {
+ let hasMoved = AutoRefreshHighlighter.prototype._hasMoved.call(this);
+
+ let oldGridData = stringifyGridFragments(this.gridData);
+ this.gridData = this.currentNode.getGridFragments();
+ let newGridData = stringifyGridFragments(this.gridData);
+
+ return hasMoved || oldGridData !== newGridData;
+ },
+
+ /**
+ * Update the highlighter on the current highlighted node (the one that was
+ * passed as an argument to show(node)).
+ * Should be called whenever node's geometry or grid changes.
+ */
+ _update() {
+ setIgnoreLayoutChanges(true);
+
+ // Clear the canvas the grid area highlights.
+ this.clearCanvas();
+ this.clearGridAreas();
+
+ // Start drawing the grid fragments.
+ for (let i = 0; i < this.gridData.length; i++) {
+ let fragment = this.gridData[i];
+ let quad = this.currentQuads.content[i];
+ this.renderFragment(fragment, quad);
+ }
+
+ // Display the grid area highlights if needed.
+ if (this.options.showAllGridAreas) {
+ this.showAllGridAreas();
+ } else if (this.options.showGridArea) {
+ this.showGridArea(this.options.showGridArea);
+ }
+
+ this._showGrid();
+
+ setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
+ 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;
+ let height = this.win.innerHeight;
+
+ // Resize the canvas taking the dpr into account so as to have crisp lines.
+ this.canvas.setAttribute("width", width * ratio);
+ this.canvas.setAttribute("height", height * ratio);
+ this.canvas.setAttribute("style", `width:${width}px;height:${height}px`);
+ this.ctx.scale(ratio, ratio);
+
+ this.ctx.clearRect(0, 0, width, height);
+ },
+
+ getFirstRowLinePos(fragment) {
+ return fragment.rows.lines[0].start;
+ },
+
+ getLastRowLinePos(fragment) {
+ return fragment.rows.lines[fragment.rows.lines.length - 1].start;
+ },
+
+ getFirstColLinePos(fragment) {
+ return fragment.cols.lines[0].start;
+ },
+
+ getLastColLinePos(fragment) {
+ return fragment.cols.lines[fragment.cols.lines.length - 1].start;
+ },
+
+ /**
+ * Get the GridLine index of the last edge of the explicit grid for a grid dimension.
+ *
+ * @param {GridTracks} tracks
+ * The grid track of a given grid dimension.
+ * @return {Number} index of the last edge of the explicit grid for a grid dimension.
+ */
+ getLastEdgeLineIndex(tracks) {
+ let trackIndex = tracks.length - 1;
+
+ // Traverse the grid track backwards until we find an explicit track.
+ while (trackIndex >= 0 && tracks[trackIndex].type != "explicit") {
+ trackIndex--;
+ }
+
+ // The grid line index is the grid track index + 1.
+ return trackIndex + 1;
+ },
+
+ renderFragment(fragment, quad) {
+ this.renderLines(fragment.cols, quad, COLUMNS, "left", "top", "height",
+ this.getFirstRowLinePos(fragment),
+ this.getLastRowLinePos(fragment));
+ this.renderLines(fragment.rows, quad, ROWS, "top", "left", "width",
+ this.getFirstColLinePos(fragment),
+ this.getLastColLinePos(fragment));
+ },
+
+ /**
+ * Render the grid lines given the grid dimension information of the
+ * column or row lines.
+ *
+ * @param {GridDimension} gridDimension
+ * Column or row grid dimension object.
+ * @param {Object} quad.bounds
+ * The content bounds of the box model region quads.
+ * @param {String} dimensionType
+ * The grid dimension type which is either the constant COLUMNS or ROWS.
+ * @param {String} mainSide
+ * The main side of the given grid dimension - "top" for rows and
+ * "left" for columns.
+ * @param {String} crossSide
+ * The cross side of the given grid dimension - "left" for rows and
+ * "top" for columns.
+ * @param {String} mainSize
+ * The main size of the given grid dimension - "width" for rows and
+ * "height" for columns.
+ * @param {Number} startPos
+ * The start position of the cross side of the grid dimension.
+ * @param {Number} endPos
+ * The end position of the cross side of the grid dimension.
+ */
+ renderLines(gridDimension, {bounds}, dimensionType, mainSide, crossSide,
+ mainSize, startPos, endPos) {
+ let lineStartPos = (bounds[crossSide] / getCurrentZoom(this.win)) + startPos;
+ let lineEndPos = (bounds[crossSide] / getCurrentZoom(this.win)) + endPos;
+
+ if (this.options.showInfiniteLines) {
+ lineStartPos = 0;
+ lineEndPos = parseInt(this.canvas.getAttribute(mainSize), 10);
+ }
+
+ let lastEdgeLineIndex = this.getLastEdgeLineIndex(gridDimension.tracks);
+
+ for (let i = 0; i < gridDimension.lines.length; i++) {
+ let line = gridDimension.lines[i];
+ let linePos = (bounds[mainSide] / getCurrentZoom(this.win)) + line.start;
+
+ if (this.options.showGridLineNumbers) {
+ this.renderGridLineNumber(line.number, linePos, lineStartPos, dimensionType);
+ }
+
+ if (i == 0 || i == lastEdgeLineIndex) {
+ this.renderLine(linePos, lineStartPos, lineEndPos, dimensionType, "edge");
+ } else {
+ this.renderLine(linePos, lineStartPos, lineEndPos, dimensionType,
+ gridDimension.tracks[i - 1].type);
+ }
+
+ // Render a second line to illustrate the gutter for non-zero breadth.
+ if (line.breadth > 0) {
+ this.renderGridGap(linePos, lineStartPos, lineEndPos, line.breadth,
+ dimensionType);
+ this.renderLine(linePos + line.breadth, lineStartPos, lineEndPos, dimensionType,
+ gridDimension.tracks[i].type);
+ }
+ }
+ },
+
+ /**
+ * Render the grid line on the css grid highlighter canvas.
+ *
+ * @param {Number} linePos
+ * The line position along the x-axis for a column grid line and
+ * y-axis for a row grid line.
+ * @param {Number} startPos
+ * The start position of the cross side of the grid line.
+ * @param {Number} endPos
+ * The end position of the cross side of the grid line.
+ * @param {String} dimensionType
+ * The grid dimension type which is either the constant COLUMNS or ROWS.
+ * @param {[type]} lineType
+ * The grid line type - "edge", "explicit", or "implicit".
+ */
+ renderLine(linePos, startPos, endPos, dimensionType, lineType) {
+ this.ctx.save();
+ this.ctx.setLineDash(GRID_LINES_PROPERTIES[lineType].lineDash);
+ this.ctx.beginPath();
+ this.ctx.translate(.5, .5);
+
+ if (dimensionType === COLUMNS) {
+ this.ctx.moveTo(linePos, startPos);
+ this.ctx.lineTo(linePos, endPos);
+ } else {
+ this.ctx.moveTo(startPos, linePos);
+ this.ctx.lineTo(endPos, linePos);
+ }
+
+ this.ctx.strokeStyle = GRID_LINES_PROPERTIES[lineType].strokeStyle;
+ this.ctx.stroke();
+ this.ctx.restore();
+ },
+
+ /**
+ * Render the grid line number on the css grid highlighter canvas.
+ *
+ * @param {Number} lineNumber
+ * The grid line number.
+ * @param {Number} linePos
+ * The line position along the x-axis for a column grid line and
+ * y-axis for a row grid line.
+ * @param {Number} startPos
+ * The start position of the cross side of the grid line.
+ * @param {String} dimensionType
+ * The grid dimension type which is either the constant COLUMNS or ROWS.
+ */
+ renderGridLineNumber(lineNumber, linePos, startPos, dimensionType) {
+ this.ctx.save();
+
+ if (dimensionType === COLUMNS) {
+ this.ctx.fillText(lineNumber, linePos, startPos);
+ } else {
+ let textWidth = this.ctx.measureText(lineNumber).width;
+ this.ctx.fillText(lineNumber, startPos - textWidth, linePos);
+ }
+
+ this.ctx.restore();
+ },
+
+ /**
+ * Render the grid gap area on the css grid highlighter canvas.
+ *
+ * @param {Number} linePos
+ * The line position along the x-axis for a column grid line and
+ * y-axis for a row grid line.
+ * @param {Number} startPos
+ * The start position of the cross side of the grid line.
+ * @param {Number} endPos
+ * The end position of the cross side of the grid line.
+ * @param {Number} breadth
+ * The grid line breadth value.
+ * @param {String} dimensionType
+ * The grid dimension type which is either the constant COLUMNS or ROWS.
+ */
+ renderGridGap(linePos, startPos, endPos, breadth, dimensionType) {
+ this.ctx.save();
+
+ if (dimensionType === COLUMNS) {
+ this.ctx.fillStyle = this.getGridGapPattern(COLUMN_KEY);
+ this.ctx.fillRect(linePos, startPos, breadth, endPos - startPos);
+ } else {
+ this.ctx.fillStyle = this.getGridGapPattern(ROW_KEY);
+ this.ctx.fillRect(startPos, linePos, endPos - startPos, breadth);
+ }
+
+ this.ctx.restore();
+ },
+
+ /**
+ * Render the grid area highlight for the given area name or for all the grid areas.
+ *
+ * @param {String} areaName
+ * Name of the grid area to be highlighted. If no area name is provided, all
+ * the grid areas should be highlighted.
+ */
+ renderGridArea(areaName) {
+ let paths = [];
+ let currentZoom = getCurrentZoom(this.win);
+
+ for (let i = 0; i < this.gridData.length; i++) {
+ let fragment = this.gridData[i];
+ let {bounds} = this.currentQuads.content[i];
+
+ for (let area of fragment.areas) {
+ if (areaName && areaName != area.name) {
+ continue;
+ }
+
+ let rowStart = fragment.rows.lines[area.rowStart - 1];
+ let rowEnd = fragment.rows.lines[area.rowEnd - 1];
+ let columnStart = fragment.cols.lines[area.columnStart - 1];
+ let columnEnd = fragment.cols.lines[area.columnEnd - 1];
+
+ let x1 = columnStart.start + columnStart.breadth +
+ (bounds.left / currentZoom);
+ let x2 = columnEnd.start + (bounds.left / currentZoom);
+ let y1 = rowStart.start + rowStart.breadth +
+ (bounds.top / currentZoom);
+ let y2 = rowEnd.start + (bounds.top / currentZoom);
+
+ let path = "M" + x1 + "," + y1 + " " +
+ "L" + x2 + "," + y1 + " " +
+ "L" + x2 + "," + y2 + " " +
+ "L" + x1 + "," + y2;
+ paths.push(path);
+
+ // Update and show the info bar when only displaying a single grid area.
+ if (areaName) {
+ this._updateInfobar(area, x1, x2, y1, y2);
+ this._showInfoBar();
+ }
+ }
+ }
+
+ let box = this.getElement("areas");
+ box.setAttribute("d", paths.join(" "));
+ },
+
+ /**
+ * Hide the highlighter, the canvas and the infobar.
+ */
+ _hide() {
+ setIgnoreLayoutChanges(true);
+ this._hideGrid();
+ this._hideGridArea();
+ this._hideInfoBar();
+ setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
+ },
+
+ _hideGrid() {
+ this.getElement("canvas").setAttribute("hidden", "true");
+ },
+
+ _showGrid() {
+ this.getElement("canvas").removeAttribute("hidden");
+ },
+
+ _hideGridArea() {
+ this.getElement("elements").setAttribute("hidden", "true");
+ },
+
+ _showGridArea() {
+ this.getElement("elements").removeAttribute("hidden");
+ },
+
+ _hideInfoBar() {
+ this.getElement("infobar-container").setAttribute("hidden", "true");
+ },
+
+ _showInfoBar() {
+ this.getElement("infobar-container").removeAttribute("hidden");
+ },
+
+});
+
+exports.CssGridHighlighter = CssGridHighlighter;