selector.js (3428B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { 8 isNodeValid, 9 } = require("resource://devtools/server/actors/highlighters/utils/markup.js"); 10 const { 11 BoxModelHighlighter, 12 } = require("resource://devtools/server/actors/highlighters/box-model.js"); 13 14 // How many maximum nodes can be highlighted at the same time by the SelectorHighlighter 15 const MAX_HIGHLIGHTED_ELEMENTS = 100; 16 17 /** 18 * The SelectorHighlighter runs a given selector through querySelectorAll on the 19 * document of the provided context node and then uses the BoxModelHighlighter 20 * to highlight the matching nodes 21 */ 22 class SelectorHighlighter { 23 constructor(highlighterEnv, inspector) { 24 this.highlighterEnv = highlighterEnv; 25 this.inspector = inspector; 26 this._highlighters = []; 27 } 28 29 /** 30 * Show a BoxModelHighlighter on each node that matches a given selector. 31 * 32 * @param {DOMNode} node 33 * A context node used to get the document element on which to run 34 * querySelectorAll(). This node will not be highlighted. 35 * @param {object} options 36 * Configuration options for SelectorHighlighter. 37 * All of the options for BoxModelHighlighter.show() are also valid here. 38 * @param {string} options.selector 39 * Required. CSS selector used with querySelectorAll() to find matching elements. 40 */ 41 async show(node, options = {}) { 42 this.hide(); 43 44 if (!isNodeValid(node) || !options.selector) { 45 return false; 46 } 47 48 let nodes = []; 49 50 if (options.ruleActorID && this.inspector) { 51 const pageStyle = await this.inspector.getPageStyle(); 52 const rule = pageStyle.getActorByID(options.ruleActorID); 53 if (rule) { 54 nodes = rule.rawRule.querySelectorAll(node.getRootNode()); 55 } 56 } else { 57 try { 58 nodes = node.ownerDocument.querySelectorAll(options.selector); 59 } catch (e) { 60 // It's fine if the provided selector is invalid, `nodes` will be an empty array. 61 } 62 } 63 64 // Prevent passing the `selector` option to BoxModelHighlighter 65 delete options.selector; 66 67 const promises = []; 68 for (let i = 0; i < Math.min(nodes.length, MAX_HIGHLIGHTED_ELEMENTS); i++) { 69 promises.push(this._showHighlighter(nodes[i], options)); 70 } 71 72 await Promise.all(promises); 73 return true; 74 } 75 76 /** 77 * Create an instance of BoxModelHighlighter, wait for it to be ready 78 * (see CanvasFrameAnonymousContentHelper.initialize()), 79 * then show the highlighter on the given node with the given configuration options. 80 * 81 * @param {DOMNode} node 82 * Node to be highlighted 83 * @param {object} options 84 * Configuration options for the BoxModelHighlighter 85 * @return {Promise} Promise that resolves when the BoxModelHighlighter is ready 86 */ 87 async _showHighlighter(node, options) { 88 const highlighter = new BoxModelHighlighter(this.highlighterEnv); 89 await highlighter.isReady; 90 91 highlighter.show(node, options); 92 this._highlighters.push(highlighter); 93 } 94 95 hide() { 96 for (const highlighter of this._highlighters) { 97 highlighter.destroy(); 98 } 99 this._highlighters = []; 100 } 101 102 destroy() { 103 this.hide(); 104 this.highlighterEnv = null; 105 } 106 } 107 exports.SelectorHighlighter = SelectorHighlighter;