From 35bf6cdf38c1804f8f8a9a9ad51bcd8cd11a94c4 Mon Sep 17 00:00:00 2001 From: Patrick Brosset Date: Wed, 19 Nov 2014 11:16:25 +0100 Subject: [PATCH] Bug 1076866 - Rect highlighter; r=bgrins --- browser/devtools/inspector/test/browser.ini | 4 + .../browser_inspector_highlighter-rect_01.js | 118 ++++++++++++++++++ .../browser_inspector_highlighter-rect_02.js | 46 +++++++ .../test/doc_inspector_highlighter_rect.html | 22 ++++ ...doc_inspector_highlighter_rect_iframe.html | 15 +++ browser/devtools/inspector/test/head.js | 4 +- .../devtools/server/actors/highlighter.css | 8 ++ toolkit/devtools/server/actors/highlighter.js | 95 +++++++++++++- toolkit/devtools/server/actors/root.js | 3 +- 9 files changed, 310 insertions(+), 5 deletions(-) create mode 100644 browser/devtools/inspector/test/browser_inspector_highlighter-rect_01.js create mode 100644 browser/devtools/inspector/test/browser_inspector_highlighter-rect_02.js create mode 100644 browser/devtools/inspector/test/doc_inspector_highlighter_rect.html create mode 100644 browser/devtools/inspector/test/doc_inspector_highlighter_rect_iframe.html diff --git a/browser/devtools/inspector/test/browser.ini b/browser/devtools/inspector/test/browser.ini index baade01dec3..21e6ff48fb9 100644 --- a/browser/devtools/inspector/test/browser.ini +++ b/browser/devtools/inspector/test/browser.ini @@ -11,6 +11,8 @@ support-files = doc_inspector_highlighter-comments.html doc_inspector_highlighter_csstransform.html doc_inspector_highlighter.html + doc_inspector_highlighter_rect.html + doc_inspector_highlighter_rect_iframe.html doc_inspector_infobar_01.html doc_inspector_infobar_02.html doc_inspector_menu.html @@ -41,6 +43,8 @@ support-files = [browser_inspector_highlighter-hover_03.js] [browser_inspector_highlighter-iframes.js] [browser_inspector_highlighter-options.js] +[browser_inspector_highlighter-rect_01.js] +[browser_inspector_highlighter-rect_02.js] [browser_inspector_highlighter-selector_01.js] [browser_inspector_highlighter-selector_02.js] [browser_inspector_iframe-navigation.js] diff --git a/browser/devtools/inspector/test/browser_inspector_highlighter-rect_01.js b/browser/devtools/inspector/test/browser_inspector_highlighter-rect_01.js new file mode 100644 index 00000000000..ae5eff33623 --- /dev/null +++ b/browser/devtools/inspector/test/browser_inspector_highlighter-rect_01.js @@ -0,0 +1,118 @@ +/* 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"; + +// Test that the custom rect highlighter provides the right API, ensures that +// the input is valid and that it does create a box with the right dimensions, +// at the right position. + +const TEST_URL = "data:text/html;charset=utf-8,Rect Highlighter Test"; + +add_task(function*() { + let {inspector, toolbox} = yield openInspectorForURL(TEST_URL); + let front = inspector.inspector; + let highlighter = yield front.getHighlighterByType("RectHighlighter"); + let body = yield getNodeFront("body", inspector); + + info("Make sure the highlighter returned is correct"); + + ok(highlighter, "The RectHighlighter custom type was created"); + is(highlighter.typeName, "customhighlighter", + "The RectHighlighter has the right type"); + ok(highlighter.show && highlighter.hide, + "The RectHighlighter has the expected show/hide methods"); + + info("Check that the highlighter is hidden by default"); + + let hidden = yield getAttribute(highlighter, "hidden"); + is(hidden, "true", "The highlighter is hidden by default"); + + info("Check that nothing is shown if no rect is passed"); + + yield highlighter.show(body); + hidden = yield getAttribute(highlighter, "hidden"); + is(hidden, "true", "The highlighter is hidden when no rect is passed"); + + info("Check that nothing is shown if rect is incomplete or invalid"); + + yield highlighter.show(body, { + rect: {x: 0, y: 0} + }); + hidden = yield getAttribute(highlighter, "hidden"); + is(hidden, "true", "The highlighter is hidden when the rect is incomplete"); + + yield highlighter.show(body, { + rect: {x: 0, y: 0, width: -Infinity, height: 0} + }); + hidden = yield getAttribute(highlighter, "hidden"); + is(hidden, "true", "The highlighter is hidden when the rect is invalid (1)"); + + yield highlighter.show(body, { + rect: {x: 0, y: 0, width: 5, height: -45} + }); + hidden = yield getAttribute(highlighter, "hidden"); + is(hidden, "true", "The highlighter is hidden when the rect is invalid (2)"); + + yield highlighter.show(body, { + rect: {x: "test", y: 0, width: 5, height: 5} + }); + hidden = yield getAttribute(highlighter, "hidden"); + is(hidden, "true", "The highlighter is hidden when the rect is invalid (3)"); + + info("Check that the highlighter is displayed when valid options are passed"); + + yield highlighter.show(body, { + rect: {x: 5, y: 5, width: 50, height: 50} + }); + hidden = yield getAttribute(highlighter, "hidden"); + ok(!hidden, "The highlighter is displayed"); + let style = yield getAttribute(highlighter, "style"); + is(style, "left:5px;top:5px;width:50px;height:50px;", + "The highlighter is positioned correctly"); + + info("Check that the highlighter can be displayed at x=0 y=0"); + + yield highlighter.show(body, { + rect: {x: 0, y: 0, width: 50, height: 50} + }); + hidden = yield getAttribute(highlighter, "hidden"); + ok(!hidden, "The highlighter is displayed when x=0 and y=0"); + style = yield getAttribute(highlighter, "style"); + is(style, "left:0px;top:0px;width:50px;height:50px;", + "The highlighter is positioned correctly"); + + info("Check that the highlighter is hidden when dimensions are 0"); + + yield highlighter.show(body, { + rect: {x: 0, y: 0, width: 0, height: 0} + }); + hidden = yield getAttribute(highlighter, "hidden"); + is(hidden, "true", "The highlighter is hidden width and height are 0"); + + info("Check that a fill color can be passed"); + + yield highlighter.show(body, { + rect: {x: 100, y: 200, width: 500, height: 200}, + fill: "red" + }); + hidden = yield getAttribute(highlighter, "hidden"); + ok(!hidden, "The highlighter is displayed"); + style = yield getAttribute(highlighter, "style"); + is(style, "left:100px;top:200px;width:500px;height:200px;background:red;", + "The highlighter has the right background color"); + + yield highlighter.hide(); + yield highlighter.finalize(); + gBrowser.removeCurrentTab(); +}); + +function* getAttribute(highlighter, name) { + let {data: value} = yield executeInContent("Test:GetHighlighterAttribute", { + nodeID: "highlighted-rect", + name: name, + actorID: highlighter.actorID + }); + return value; +} diff --git a/browser/devtools/inspector/test/browser_inspector_highlighter-rect_02.js b/browser/devtools/inspector/test/browser_inspector_highlighter-rect_02.js new file mode 100644 index 00000000000..01b79019e81 --- /dev/null +++ b/browser/devtools/inspector/test/browser_inspector_highlighter-rect_02.js @@ -0,0 +1,46 @@ +/* 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"; + +// Test that the custom rect highlighter positions the rectangle relative to the +// viewport of the context node we pass to it. + +const TEST_URL = TEST_URL_ROOT + "doc_inspector_highlighter_rect.html"; + +add_task(function*() { + let {inspector, toolbox} = yield openInspectorForURL(TEST_URL); + let front = inspector.inspector; + let highlighter = yield front.getHighlighterByType("RectHighlighter"); + + info("Showing the rect highlighter in the context of the iframe"); + + // Get the reference to a context node inside the iframe + let childBody = yield getNodeFrontInFrame("body", "iframe", inspector); + yield highlighter.show(childBody, { + rect: {x: 50, y: 50, width: 100, height: 100} + }); + + let style = yield getAttribute(highlighter, "style"); + + // The parent body has margin=50px and border=10px + // The parent iframe also has margin=50px and border=10px + // = 50 + 10 + 50 + 10 = 120px + // The rect is aat x=50 and y=50, so left and top should be 170px + is(style, "left:170px;top:170px;width:100px;height:100px;", + "The highlighter is correctly positioned"); + + yield highlighter.hide(); + yield highlighter.finalize(); + gBrowser.removeCurrentTab(); +}); + +function* getAttribute(highlighter, name) { + let {data: value} = yield executeInContent("Test:GetHighlighterAttribute", { + nodeID: "highlighted-rect", + name: name, + actorID: highlighter.actorID + }); + return value; +} diff --git a/browser/devtools/inspector/test/doc_inspector_highlighter_rect.html b/browser/devtools/inspector/test/doc_inspector_highlighter_rect.html new file mode 100644 index 00000000000..4d23d52fd28 --- /dev/null +++ b/browser/devtools/inspector/test/doc_inspector_highlighter_rect.html @@ -0,0 +1,22 @@ + + + + + rect highlighter parent test page + + + + + + diff --git a/browser/devtools/inspector/test/doc_inspector_highlighter_rect_iframe.html b/browser/devtools/inspector/test/doc_inspector_highlighter_rect_iframe.html new file mode 100644 index 00000000000..d59050f69bb --- /dev/null +++ b/browser/devtools/inspector/test/doc_inspector_highlighter_rect_iframe.html @@ -0,0 +1,15 @@ + + + + + rect highlighter child test page + + + + + + diff --git a/browser/devtools/inspector/test/head.js b/browser/devtools/inspector/test/head.js index 64f37868803..a6a4c23c8c4 100644 --- a/browser/devtools/inspector/test/head.js +++ b/browser/devtools/inspector/test/head.js @@ -257,8 +257,8 @@ function getNodeFront(selector, {walker}) { * to highlight the node upon selection * @return {Promise} Resolves when the inspector is updated with the new node */ -let getNodeFrontInFrame = Task.async(function*(selector, frameSelector, inspector, - reason="test") { +let getNodeFrontInFrame = Task.async(function*(selector, frameSelector, + inspector, reason="test") { let iframe = yield getNodeFront(frameSelector, inspector); let {nodes} = yield inspector.walker.children(iframe); return inspector.walker.querySelector(nodes[0], selector); diff --git a/toolkit/devtools/server/actors/highlighter.css b/toolkit/devtools/server/actors/highlighter.css index 6a29d797822..aa4d88a2e0b 100644 --- a/toolkit/devtools/server/actors/highlighter.css +++ b/toolkit/devtools/server/actors/highlighter.css @@ -170,3 +170,11 @@ stroke-dasharray: 5 3; stroke-width: 2; } + +/* Rect highlighter */ + +:-moz-native-anonymous .highlighted-rect { + position: absolute; + background: #80d4ff; + opacity: 0.8; +} diff --git a/toolkit/devtools/server/actors/highlighter.js b/toolkit/devtools/server/actors/highlighter.js index 30457a38131..fb59aa48233 100644 --- a/toolkit/devtools/server/actors/highlighter.js +++ b/toolkit/devtools/server/actors/highlighter.js @@ -48,7 +48,8 @@ const SIMPLE_OUTLINE_SHEET = ".__fx-devtools-hide-shortcut__ {" + let HIGHLIGHTER_CLASSES = exports.HIGHLIGHTER_CLASSES = { "BoxModelHighlighter": BoxModelHighlighter, "CssTransformHighlighter": CssTransformHighlighter, - "SelectorHighlighter": SelectorHighlighter + "SelectorHighlighter": SelectorHighlighter, + "RectHighlighter": RectHighlighter }; /** @@ -343,7 +344,17 @@ let CustomHighlighterActor = exports.CustomHighlighterActor = protocol.ActorClas }, /** - * Display the highlighter on a given NodeActor. + * Show the highlighter. + * This calls through to the highlighter instance's |show(node, options)| + * method. + * + * Most custom highlighters are made to highlight DOM nodes, hence the first + * NodeActor argument (NodeActor as in toolkit/devtools/server/actor/inspector). + * Note however that some highlighters use this argument merely as a context + * node: the RectHighlighter for instance uses it to calculate the absolute + * position of the provided rect. The SelectHighlighter uses it as a base node + * to run the provided CSS selector on. + * * @param NodeActor The node to be highlighted * @param Object Options for the custom highlighter */ @@ -1472,6 +1483,86 @@ SelectorHighlighter.prototype = { } }; +/** + * The RectHighlighter is a class that draws a rectangle highlighter at specific + * coordinates. + * It does *not* highlight DOM nodes, but rects. + * It also does *not* update dynamically, it only highlights a rect and remains + * there as long as it is shown. + */ +function RectHighlighter(tabActor) { + this.win = tabActor.window; + this.layoutHelpers = new LayoutHelpers(this.win); + this.markup = new CanvasFrameAnonymousContentHelper(tabActor, + this._buildMarkup.bind(this)); +} + +RectHighlighter.prototype = { + _buildMarkup: function() { + let doc = this.win.document; + + let container = doc.createElement("div"); + container.className = "highlighter-container"; + container.innerHTML = '