/* 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"; loader.lazyRequireGetter( this, ["setIgnoreLayoutChanges", "getCurrentZoom"], "resource://devtools/shared/layout/utils.js", true ); loader.lazyRequireGetter( this, "AutoRefreshHighlighter", "resource://devtools/server/actors/highlighters/auto-refresh.js", true ); loader.lazyRequireGetter( this, ["CanvasFrameAnonymousContentHelper"], "resource://devtools/server/actors/highlighters/utils/markup.js", true ); /** * The NodeTabbingOrderHighlighter draws an outline around a node (based on its * border bounds). * * Usage example: * * const h = new NodeTabbingOrderHighlighter(env); * await h.isReady(); * h.show(node, options); * h.hide(); * h.destroy(); * * @param {Number} options.index * Tabbing index value to be displayed in the highlighter info bar. */ class NodeTabbingOrderHighlighter extends AutoRefreshHighlighter { constructor(highlighterEnv) { super(highlighterEnv); this._doNotStartRefreshLoop = true; this.ID_CLASS_PREFIX = "tabbing-order-"; this.markup = new CanvasFrameAnonymousContentHelper( this.highlighterEnv, this._buildMarkup.bind(this) ); this.isReady = this.markup.initialize(); } _buildMarkup() { const root = this.markup.createNode({ attributes: { id: "root", class: "root highlighter-container tabbing-order", "aria-hidden": "true", }, prefix: this.ID_CLASS_PREFIX, }); const container = this.markup.createNode({ parent: root, attributes: { id: "container", width: "100%", height: "100%", hidden: "true", }, prefix: this.ID_CLASS_PREFIX, }); // Building the SVG element this.markup.createNode({ parent: container, attributes: { class: "bounds", id: "bounds", }, prefix: this.ID_CLASS_PREFIX, }); // Building the nodeinfo bar markup const infobarContainer = this.markup.createNode({ parent: root, attributes: { class: "infobar-container", id: "infobar-container", position: "top", hidden: "true", }, prefix: this.ID_CLASS_PREFIX, }); const infobar = this.markup.createNode({ parent: infobarContainer, attributes: { class: "infobar", }, prefix: this.ID_CLASS_PREFIX, }); this.markup.createNode({ parent: infobar, attributes: { class: "infobar-text", id: "infobar-text", }, prefix: this.ID_CLASS_PREFIX, }); return root; } /** * Destroy the nodes. Remove listeners. */ destroy() { this.markup.destroy(); AutoRefreshHighlighter.prototype.destroy.call(this); } getElement(id) { return this.markup.getElement(this.ID_CLASS_PREFIX + id); } /** * Update focused styling for a node tabbing index highlight. * * @param {Boolean} focused * Indicates if the highlighted node needs to be focused. */ updateFocus(focused) { const root = this.getElement("root"); root.classList.toggle("focused", focused); } /** * Show the highlighter on a given node */ _show() { return this._update(); } /** * Update the highlighter on the current highlighted node (the one that was * passed as an argument to show(node)). * Should be called whenever node size or attributes change */ _update() { let shown = false; setIgnoreLayoutChanges(true); if (this._updateTabbingOrder()) { this._showInfobar(); this._showTabbingOrder(); shown = true; setIgnoreLayoutChanges( false, this.highlighterEnv.window.document.documentElement ); } else { // Nothing to highlight (0px rectangle like a