Bug 1076866 - Rect highlighter; r=bgrins

This commit is contained in:
Patrick Brosset 2014-11-19 11:16:25 +01:00
parent e352807814
commit 35bf6cdf38
9 changed files with 310 additions and 5 deletions

View File

@ -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]

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,22 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>rect highlighter parent test page</title>
<style type="text/css">
body {
margin: 50px;
border: 10px solid red;
}
iframe {
border: 10px solid yellow;
padding: 0;
margin: 50px;
}
</style>
</head>
<body>
<iframe src="doc_inspector_highlighter_rect_iframe.html"></iframe>
</body>
</html>

View File

@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>rect highlighter child test page</title>
<style type="text/css">
body {
margin: 0;
}
</style>
</head>
<body>
</body>
</html>

View File

@ -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);

View File

@ -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;
}

View File

@ -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 = '<div id="highlighted-rect" ' +
'class="highlighted-rect" hidden="true">';
return container;
},
destroy: function() {
this.win = null;
this.layoutHelpers = null;
this.markup.destroy();
},
_hasValidOptions: function(options) {
let isValidNb = n => typeof n === "number" && n >= 0 && isFinite(n);
return options && options.rect &&
isValidNb(options.rect.x) &&
isValidNb(options.rect.y) &&
options.rect.width && isValidNb(options.rect.width) &&
options.rect.height && isValidNb(options.rect.height);
},
/**
* @param {DOMNode} node The highlighter rect is relatively positioned to the
* viewport this node is in. Using the provided node, the highligther will get
* the parent documentElement and use it as context to position the
* highlighter correctly.
* @param {Object} options Accepts the following options:
* - rect: mandatory object that should have the x, y, width, height properties
* - fill: optional fill color for the rect
*/
show: function(node, options) {
if (!this._hasValidOptions(options) || !node || !node.ownerDocument) {
this.hide();
return;
}
let contextNode = node.ownerDocument.documentElement;
// Caculate the absolute rect based on the context node's adjusted quads.
let {bounds} = this.layoutHelpers.getAdjustedQuads(contextNode);
let x = "left:" + (bounds.x + options.rect.x) + "px;";
let y = "top:" + (bounds.y + options.rect.y) + "px;";
let width = "width:" + options.rect.width + "px;";
let height = "height:" + options.rect.height + "px;";
let style = x + y + width + height;
if (options.fill) {
style += "background:" + options.fill + ";";
}
// Set the coordinates of the highlighter and show it
this.markup.setAttributeForElement("highlighted-rect", "style", style);
this.markup.removeAttributeForElement("highlighted-rect", "hidden");
},
hide: function() {
this.markup.setAttributeForElement("highlighted-rect", "hidden", "true");
}
};
/**
* The SimpleOutlineHighlighter is a class that has the same API than the
* BoxModelHighlighter, but adds a pseudo-class on the target element itself

View File

@ -128,7 +128,8 @@ RootActor.prototype = {
customHighlighters: [
"BoxModelHighlighter",
"CssTransformHighlighter",
"SelectorHighlighter"
"SelectorHighlighter",
"RectHighlighter"
],
// Whether the inspector actor implements the getImageDataFromURL
// method that returns data-uris for image URLs. This is used for image