From 6893913e528731e13c67472547610a1e3400246f Mon Sep 17 00:00:00 2001 From: Patrick Brosset Date: Wed, 25 Jun 2014 16:40:43 +0200 Subject: [PATCH] Bug 971662 - part 1 - GCLI command that highlights all nodes that match a selector; r=jwalker --- .../devtools/commandline/commands-index.js | 1 + browser/devtools/commandline/test/browser.ini | 2 + .../test/browser_cmd_highlight_01.js | 257 ++++++++++++++++++ .../test/browser_cmd_highlight_02.js | 43 +++ .../browser/devtools/gclicommands.properties | 107 ++++++++ toolkit/devtools/gcli/commands/highlight.js | 142 ++++++++++ toolkit/devtools/server/actors/highlighter.js | 13 + 7 files changed, 565 insertions(+) create mode 100644 browser/devtools/commandline/test/browser_cmd_highlight_01.js create mode 100644 browser/devtools/commandline/test/browser_cmd_highlight_02.js create mode 100644 toolkit/devtools/gcli/commands/highlight.js diff --git a/browser/devtools/commandline/commands-index.js b/browser/devtools/commandline/commands-index.js index 82a045ffc1b6..6cf7f57e6135 100644 --- a/browser/devtools/commandline/commands-index.js +++ b/browser/devtools/commandline/commands-index.js @@ -15,6 +15,7 @@ const commandModules = [ "gcli/commands/cookie", "gcli/commands/csscoverage", "gcli/commands/folder", + "gcli/commands/highlight", "gcli/commands/inject", "gcli/commands/jsb", "gcli/commands/listen", diff --git a/browser/devtools/commandline/test/browser.ini b/browser/devtools/commandline/test/browser.ini index 7831692cae74..c306724bb934 100644 --- a/browser/devtools/commandline/test/browser.ini +++ b/browser/devtools/commandline/test/browser.ini @@ -51,6 +51,8 @@ support-files = browser_cmd_csscoverage_sheetC.css browser_cmd_csscoverage_sheetD.css [browser_cmd_folder.js] +[browser_cmd_highlight_01.js] +[browser_cmd_highlight_02.js] [browser_cmd_inject.js] support-files = browser_cmd_inject.html diff --git a/browser/devtools/commandline/test/browser_cmd_highlight_01.js b/browser/devtools/commandline/test/browser_cmd_highlight_01.js new file mode 100644 index 000000000000..6c8e85afe74f --- /dev/null +++ b/browser/devtools/commandline/test/browser_cmd_highlight_01.js @@ -0,0 +1,257 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests the various highlight command parameters and options + +// Creating a test page with many elements to test the --showall option +let TEST_PAGE = "data:text/html;charset=utf-8,"; + +function test() { + return Task.spawn(spawnTest).then(finish, helpers.handleError); +} + +function* spawnTest() { + let options = yield helpers.openTab(TEST_PAGE); + yield helpers.openToolbar(options); + + yield helpers.audit(options, [ + { + setup: 'highlight', + check: { + input: 'highlight', + hints: ' [selector] [options]', + markup: 'VVVVVVVVV', + status: 'VALID' + }, + exec: { + output: '0 nodes highlighted' + } + }, + { + setup: 'highlight bo', + check: { + input: 'highlight bo', + hints: ' [options]', + markup: 'VVVVVVVVVVII', + status: 'ERROR' + }, + exec: { + output: 'Error: No matches' + } + }, + { + setup: 'highlight body', + check: { + input: 'highlight body', + hints: ' [options]', + markup: 'VVVVVVVVVVVVVV', + status: 'VALID' + }, + exec: { + output: '1 nodes highlighted' + } + }, + { + setup: 'highlight body --hide', + check: { + input: 'highlight body --hide', + hints: 'guides [options]', + markup: 'VVVVVVVVVVVVVVVIIIIII', + status: 'ERROR' + }, + exec: { + output: 'Error: Too many arguments' + } + }, + { + setup: 'highlight body --hideguides', + check: { + input: 'highlight body --hideguides', + hints: ' [options]', + markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVV', + status: 'VALID' + }, + exec: { + output: '1 nodes highlighted' + } + }, + { + setup: 'highlight body --show', + check: { + input: 'highlight body --show', + hints: 'infobar [options]', + markup: 'VVVVVVVVVVVVVVVIIIIII', + status: 'ERROR' + }, + exec: { + output: 'Error: Too many arguments' + } + }, + { + setup: 'highlight body --showinfobar', + check: { + input: 'highlight body --showinfobar', + hints: ' [options]', + markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVV', + status: 'VALID' + }, + exec: { + output: '1 nodes highlighted' + } + }, + { + setup: 'highlight body --showa', + check: { + input: 'highlight body --showa', + hints: 'll [options]', + markup: 'VVVVVVVVVVVVVVVIIIIIII', + status: 'ERROR' + }, + exec: { + output: 'Error: Too many arguments' + } + }, + { + setup: 'highlight body --showall', + check: { + input: 'highlight body --showall', + hints: ' [options]', + markup: 'VVVVVVVVVVVVVVVVVVVVVVVV', + status: 'VALID' + }, + exec: { + output: '1 nodes highlighted' + } + }, + { + setup: 'highlight body --r', + check: { + input: 'highlight body --r', + hints: 'egion [options]', + markup: 'VVVVVVVVVVVVVVVIII', + status: 'ERROR' + }, + exec: { + output: 'Error: Too many arguments' + } + }, + { + setup: 'highlight body --region', + check: { + input: 'highlight body --region', + hints: ' [options]', + markup: 'VVVVVVVVVVVVVVVIIIIIIII', + status: 'ERROR' + }, + exec: { + output: 'Error: Value required for \'region\'.' + } + }, + { + setup: 'highlight body --fi', + check: { + input: 'highlight body --fi', + hints: 'll [options]', + markup: 'VVVVVVVVVVVVVVVIIII', + status: 'ERROR' + }, + exec: { + output: 'Error: Too many arguments' + } + }, + { + setup: 'highlight body --fill', + check: { + input: 'highlight body --fill', + hints: ' [options]', + markup: 'VVVVVVVVVVVVVVVIIIIII', + status: 'ERROR' + }, + exec: { + output: 'Error: Value required for \'fill\'.' + } + }, + { + setup: 'highlight body --ke', + check: { + input: 'highlight body --ke', + hints: 'ep [options]', + markup: 'VVVVVVVVVVVVVVVIIII', + status: 'ERROR' + }, + exec: { + output: 'Error: Too many arguments' + } + }, + { + setup: 'highlight body --keep', + check: { + input: 'highlight body --keep', + hints: ' [options]', + markup: 'VVVVVVVVVVVVVVVVVVVVV', + status: 'VALID' + }, + exec: { + output: '1 nodes highlighted' + } + }, + { + setup: 'highlight body --hideguides --showinfobar --showall --region ' + + 'content --fill red --keep', + check: { + input: 'highlight body --hideguides --showinfobar --showall --region ' + + 'content --fill red --keep', + hints: '', + markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV' + + 'VVVVVVVVVVVVVVVVVVVVVVVVV', + status: 'VALID' + }, + exec: { + output: '1 nodes highlighted' + } + }, + { + setup: 'highlight .item', + check: { + input: 'highlight .item', + hints: ' [options]', + markup: 'VVVVVVVVVVVVVVV', + status: 'VALID' + }, + exec: { + output: '200 nodes matched, but only 100 nodes highlighted. Use ' + + '\'--showall\' to show all' + } + }, + { + setup: 'highlight .item --showall', + check: { + input: 'highlight .item --showall', + hints: ' [options]', + markup: 'VVVVVVVVVVVVVVVVVVVVVVVVV', + status: 'VALID' + }, + exec: { + output: '200 nodes highlighted' + } + }, + { + setup: 'unhighlight', + check: { + input: 'unhighlight', + hints: '', + markup: 'VVVVVVVVVVV', + status: 'VALID' + } + } + ]); + + yield helpers.closeToolbar(options); + yield helpers.closeTab(options); +} diff --git a/browser/devtools/commandline/test/browser_cmd_highlight_02.js b/browser/devtools/commandline/test/browser_cmd_highlight_02.js new file mode 100644 index 000000000000..1bed63ae8402 --- /dev/null +++ b/browser/devtools/commandline/test/browser_cmd_highlight_02.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the highlight command actually creates a highlighter + +const TEST_PAGE = "data:text/html;charset=utf-8,
"; + +function test() { + return Task.spawn(function*() { + let options = yield helpers.openTab(TEST_PAGE); + yield helpers.openToolbar(options); + + info("highlighting the body node"); + yield runCommand("highlight body", options); + is(getHighlighters().length, 1, "The highlighter element exists for body"); + + info("highlighting the div node"); + yield runCommand("highlight div", options); + is(getHighlighters().length, 1, "The highlighter element exists for div"); + + info("highlighting the body node again, asking to keep the div"); + yield runCommand("highlight body --keep", options); + is(getHighlighters().length, 2, "2 highlighter elements have been created"); + + info("unhighlighting all nodes"); + yield runCommand("unhighlight", options); + is(getHighlighters().length, 0, "All highlighters have been removed"); + + yield helpers.closeToolbar(options); + yield helpers.closeTab(options); + }).then(finish, helpers.handleError); +} + +function getHighlighters() { + return gBrowser.selectedBrowser.parentNode + .querySelectorAll(".highlighter-container"); +} + +function* runCommand(cmd, options) { + yield helpers.audit(options, [{ setup: cmd, exec: {} }]); +} diff --git a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties index 4a790df3f5bf..3fa843630622 100644 --- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties +++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties @@ -128,6 +128,113 @@ screenshotCopied=Copied to clipboard. # LOCALIZATION NOTE (screenshotTooltip) Text displayed as tooltip for screenshot button in devtools ToolBox. screenshotTooltip=Take a fullpage screenshot +# LOCALIZATION NOTE (highlightDesc) A very short description of the +# 'highlight' command. See highlightManual for a fuller description of what +# it does. This string is designed to be shown in a menu alongside the +# command name, which is why it should be as short as possible. +highlightDesc=Highlight nodes + +# LOCALIZATION NOTE (highlightManual) A fuller description of the 'highlight' +# command, displayed when the user asks for help on what it does. +highlightManual=Highlight nodes that match a selector on the page + +# LOCALIZATION NOTE (highlightSelectorDesc) A very short string to describe +# the 'selector' parameter to the 'highlight' command, which is displayed in +# a dialog when the user is using this command. +highlightSelectorDesc=CSS selector + +# LOCALIZATION NOTE (highlightSelectorManual) A fuller description of the +# 'selector' parameter to the 'highlight' command, displayed when the user +# asks for help on what it does. +highlightSelectorManual=The CSS selector used to match nodes in the page + +# LOCALIZATION NOTE (highlightOptionsDesc) The title of a set of options to +# the 'highlight' command, displayed as a heading to the list of option. +highlightOptionsDesc=Options + +# LOCALIZATION NOTE (highlightHideGuidesDesc) A very short string to describe +# the 'hideguides' option parameter to the 'highlight' command, which is +# displayed in a dialog when the user is using this command. +highlightHideGuidesDesc=Hide guides + +# LOCALIZATION NOTE (highlightHideGuidesManual) A fuller description of the +# 'hideguides' option parameter to the 'highlight' command, displayed when the +# user asks for help on what it does. +highlightHideGuidesManual=Hide the guides around the highlighted node + +# LOCALIZATION NOTE (highlightShowInfoBarDesc) A very short string to describe +# the 'showinfobar' option parameter to the 'highlight' command, which is +# displayed in a dialog when the user is using this command. +highlightShowInfoBarDesc=Show the node infobar + +# LOCALIZATION NOTE (highlightShowInfoBarManual) A fuller description of the +# 'showinfobar' option parameter to the 'highlight' command, displayed when the +# user asks for help on what it does. +highlightShowInfoBarManual=Show the infobar above the highlighted node (the infobar displays the tagname, attributes and dimension) + +# LOCALIZATION NOTE (highlightShowAllDesc) A very short string to describe +# the 'showall' option parameter to the 'highlight' command, which is +# displayed in a dialog when the user is using this command. +highlightShowAllDesc=Show all matches + +# LOCALIZATION NOTE (highlightShowAllManual) A fuller description of the +# 'showall' option parameter to the 'highlight' command, displayed when the +# user asks for help on what it does. +highlightShowAllManual=If too many nodes match the selector, only the first 100 will be shown to avoid slowing down the page too much. Use this option to show all matches instead + +# LOCALIZATION NOTE (highlightRegionDesc) A very short string to describe the +# 'region' option parameter to the 'highlight' command, which is displayed in a +# dialog when the user is using this command. +highlightRegionDesc=Box model region + +# LOCALIZATION NOTE (highlightRegionManual) A fuller description of the 'region' +# option parameter to the 'highlight' command, displayed when the user asks for +# help on what it does. +highlightRegionManual=Which box model region should be highlighted: 'content', 'padding', 'border' or 'margin' + +# LOCALIZATION NOTE (highlightFillDesc) A very short string to describe the +# 'fill' option parameter to the 'highlight' command, which is displayed in a +# dialog when the user is using this command. +highlightFillDesc=Fill style + +# LOCALIZATION NOTE (highlightFillManual) A fuller description of the 'fill' +# option parameter to the 'highlight' command, displayed when the user asks for +# help on what it does. +highlightFillManual=Override the default region fill style with a custom color + +# LOCALIZATION NOTE (highlightKeepDesc) A very short string to describe the +# 'keep' option parameter to the 'highlight' command, which is displayed in a +# dialog when the user is using this command. +highlightKeepDesc=Keep existing highlighters + +# LOCALIZATION NOTE (highlightKeepManual) A fuller description of the 'keep' +# option parameter to the 'highlight' command, displayed when the user asks for +# help on what it does. +highlightKeepManual=By default, existing highlighters are hidden when running the command, unless this option is set + +# LOCALIZATION NOTE (highlightOutputConfirm) A confirmation message for the +# 'highlight' command, displayed to the user once the command has been entered, +# informing the user how many nodes have been highlighted successfully and how +# to turn highlighting off +highlightOutputConfirm=%1$S nodes highlighted + +# LOCALIZATION NOTE (highlightOutputMaxReached) A confirmation message for the +# 'highlight' command, displayed to the user once the command has been entered, +# informing the user how many nodes have been highlighted successfully and that +# some nodes could not be highlighted due to the maximum number of nodes being +# reached, and how to turn highlighting off +highlightOutputMaxReached=%1$S nodes matched, but only %2$S nodes highlighted. Use '--showall' to show all + +# LOCALIZATION NOTE (unhighlightDesc) A very short description of the +# 'unhighlight' command. See unhighlightManual for a fuller description of what +# it does. This string is designed to be shown in a menu alongside the +# command name, which is why it should be as short as possible. +unhighlightDesc=Unhighlight all nodes + +# LOCALIZATION NOTE (unhighlightManual) A fuller description of the 'unhighlight' +# command, displayed when the user asks for help on what it does. +unhighlightManual=Unhighlight all nodes previously highlighted with the 'highlight' command + # LOCALIZATION NOTE (restartBrowserDesc) A very short description of the # 'restart' command. This string is designed to be shown in a menu alongside the # command name, which is why it should be as short as possible. diff --git a/toolkit/devtools/gcli/commands/highlight.js b/toolkit/devtools/gcli/commands/highlight.js new file mode 100644 index 000000000000..14ea5aa9a529 --- /dev/null +++ b/toolkit/devtools/gcli/commands/highlight.js @@ -0,0 +1,142 @@ +/* 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 {Cc, Ci, Cu} = require("chrome"); +const gcli = require("gcli/index"); +require("devtools/server/actors/inspector"); +const {HIGHLIGHTER_CLASSES} = require("devtools/server/actors/highlighter"); +const {BoxModelHighlighter} = HIGHLIGHTER_CLASSES; + +// How many maximum nodes can be highlighted in parallel +const MAX_HIGHLIGHTED_ELEMENTS = 100; + +// Stores the highlighters instances so they can be destroyed +let highlighters = []; + +/** + * Destroy all existing highlighters + */ +function unhighlightAll() { + for (let highlighter of highlighters) { + highlighter.destroy(); + } + highlighters = []; +} + +exports.items = [ + { + name: "highlight", + description: gcli.lookup("highlightDesc"), + manual: gcli.lookup("highlightManual"), + params: [ + { + name: "selector", + type: "nodelist", + description: gcli.lookup("highlightSelectorDesc"), + manual: gcli.lookup("highlightSelectorManual") + }, + { + group: gcli.lookup("highlightOptionsDesc"), + params: [ + { + name: "hideguides", + type: "boolean", + description: gcli.lookup("highlightHideGuidesDesc"), + manual: gcli.lookup("highlightHideGuidesManual") + }, + { + name: "showinfobar", + type: "boolean", + description: gcli.lookup("highlightShowInfoBarDesc"), + manual: gcli.lookup("highlightShowInfoBarManual") + }, + { + name: "showall", + type: "boolean", + description: gcli.lookup("highlightShowAllDesc"), + manual: gcli.lookup("highlightShowAllManual") + }, + { + name: "region", + type: { + name: "selection", + data: ["content", "padding", "border", "margin"] + }, + description: gcli.lookup("highlightRegionDesc"), + manual: gcli.lookup("highlightRegionManual"), + defaultValue: "border" + }, + { + name: "fill", + type: "string", + description: gcli.lookup("highlightFillDesc"), + manual: gcli.lookup("highlightFillManual"), + defaultValue: null + }, + { + name: "keep", + type: "boolean", + description: gcli.lookup("highlightKeepDesc"), + manual: gcli.lookup("highlightKeepManual"), + } + ] + } + ], + exec: function(args, context) { + // Remove all existing highlighters unless told otherwise + if (!args.keep) { + unhighlightAll(); + } + + let env = context.environment; + + // Unhighlight on navigate + env.target.once("navigate", unhighlightAll); + + // Build a tab context for the highlighter (which normally takes a + // TabActor as parameter to its constructor) + let tabContext = { + browser: env.chromeWindow.gBrowser.getBrowserForDocument(env.document), + window: env.window + }; + + let i = 0; + for (let node of args.selector) { + if (!args.showall && i >= MAX_HIGHLIGHTED_ELEMENTS) { + break; + } + + let highlighter = new BoxModelHighlighter(tabContext); + if (args.fill) { + highlighter.regionFill[args.region] = args.fill; + } + highlighter.show(node, { + region: args.region, + hideInfoBar: !args.showinfobar, + hideGuides: args.hideguides, + showOnly: args.region + }); + highlighters.push(highlighter); + i ++; + } + + let output = gcli.lookupFormat("highlightOutputConfirm", + ["" + args.selector.length]); + if (args.selector.length > i) { + output = gcli.lookupFormat("highlightOutputMaxReached", + ["" + args.selector.length, "" + i]); + } + + return output; + } + }, + { + name: "unhighlight", + description: gcli.lookup("unhighlightDesc"), + manual: gcli.lookup("unhighlightManual"), + exec: unhighlightAll + } +]; diff --git a/toolkit/devtools/server/actors/highlighter.js b/toolkit/devtools/server/actors/highlighter.js index d9be26805e9c..1f0276e483f1 100644 --- a/toolkit/devtools/server/actors/highlighter.js +++ b/toolkit/devtools/server/actors/highlighter.js @@ -502,6 +502,12 @@ function BoxModelHighlighter(tabActor) { this._initMarkup(); EventEmitter.decorate(this); + /** + * Optionally customize each region's fill color by adding an entry to the + * regionFill property: `highlighter.regionFill.margin = "red"; + */ + this.regionFill = {}; + this._currentNode = null; } @@ -774,6 +780,13 @@ BoxModelHighlighter.prototype = Heritage.extend(XULBasedHighlighter.prototype, { this.layoutHelpers.getAdjustedQuads(this.currentNode, boxType); let boxNode = this._boxModelNodes[boxType]; + + if (this.regionFill[boxType]) { + boxNode.setAttribute("style", "fill:" + this.regionFill[boxType]); + } else { + boxNode.removeAttribute("style"); + } + if (!this.options.showOnly || this.options.showOnly === boxType) { boxNode.setAttribute("points", p1.x + "," + p1.y + " " +