Bug 1003569 - Hide node infobar when it is outside the viewport r=jwalker

This commit is contained in:
Michael Ratcliffe 2014-06-20 12:25:09 +01:00
parent 1220898967
commit 72b35ceb5e
5 changed files with 223 additions and 158 deletions

View File

@ -9,6 +9,7 @@ support-files =
browser_inspector_dead_node_exception.html
browser_inspector_destroyselection.html
browser_inspector_highlighter.html
browser_inspector_infobar.html
browser_inspector_menu.html
browser_inspector_select_last_selected.html
browser_inspector_select_last_selected2.html

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
body {
width: 100%;
height: 100%;
}
div {
position: absolute;
height: 100px;
width: 500px;
}
#bottom {
bottom: 0px;
}
#vertical {
height: 100%;
}
#farbottom {
top: 2000px;
background: red;
}
#abovetop {
top: -123px;
}";
</style>
</head>
<body>
<div id="abovetop"></div>
<div id="vertical"></div>
<div id="top" class="class1 class2"></div>
<div id="bottom"></div>
<div id="farbottom"></div>
</body>
</html>

View File

@ -1,140 +1,150 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* 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/. */
function test() {
ignoreAllUncaughtExceptions();
"use strict";
let doc;
let nodes;
let cursor;
let inspector;
const TEST_URI = "http://example.com/browser/browser/devtools/inspector/" +
"test/browser_inspector_infobar.html";
const DOORHANGER_ARROW_HEIGHT = 5;
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
doc = content.document;
waitForFocus(setupInfobarTest, content);
}, true);
// Test that hovering over nodes in the markup-view shows the highlighter over
// those nodes
let test = asyncTest(function*() {
info("Loading the test document and opening the inspector");
let style = "body{width:100%;height: 100%} div {position: absolute;" +
"height: 100px;width: 500px}#bottom {bottom: 0px}#vertical {"+
"height: 100%}#farbottom{bottom: -200px}";
let html = "<style>" + style + "</style><div id=vertical></div>" +
"<div id=top class='class1 class2'></div><div id=bottom></div>" +
"<div id=farbottom></div>"
yield addTab(TEST_URI);
content.location = "data:text/html;charset=utf-8," + encodeURIComponent(html);
let {inspector} = yield openInspector();
function setupInfobarTest() {
nodes = [
{
node: doc.querySelector("#top"),
position: "bottom",
tag: "DIV",
id: "#top",
classes: ".class1.class2",
dims: "500 x 100"
},
{
node: doc.querySelector("#vertical"),
position: "overlap",
tag: "DIV",
id: "#vertical",
classes: ""
// No dims as they will vary between computers
},
{
node: doc.querySelector("#bottom"),
position: "top",
tag: "DIV",
id: "#bottom",
classes: "",
dims: "500 x 100"
},
{
node: doc.querySelector("body"),
position: "overlap",
tag: "BODY",
id: "",
classes: ""
// No dims as they will vary between computers
},
{
node: doc.querySelector("#farbottom"),
position: "top",
tag: "DIV",
id: "#farbottom",
classes: "",
dims: "500 x 100"
},
];
let doc = content.document;
let testData = [
{
node: doc.querySelector("#top"),
position: "bottom",
tag: "DIV",
id: "#top",
classes: ".class1.class2",
dims: "500 x 100"
},
{
node: doc.querySelector("#vertical"),
position: "overlap",
tag: "DIV",
id: "#vertical",
classes: ""
// No dims as they will vary between computers
},
{
node: doc.querySelector("#bottom"),
position: "top",
tag: "DIV",
id: "#bottom",
classes: "",
dims: "500 x 100"
},
{
node: doc.querySelector("body"),
position: "bottom",
tag: "BODY",
id: "",
classes: ""
// No dims as they will vary between computers
},
{
node: doc.querySelector("#farbottom"),
position: "top",
tag: "DIV",
id: "#farbottom",
classes: "",
dims: "500 x 100"
},
];
for (let i = 0; i < nodes.length; i++) {
ok(nodes[i].node, "node " + i + " found");
}
openInspector(runTests);
for (let currTest of testData) {
yield testPosition(currTest, inspector);
}
function mouseOverContainerToShowHighlighter(node, cb) {
let container = getContainerForRawNode(inspector.markup, node);
EventUtils.synthesizeMouse(container.tagLine, 2, 2, {type: "mousemove"},
inspector.markup.doc.defaultView);
executeSoon(cb);
}
yield checkInfoBarAboveTop(inspector);
yield checkInfoBarBelowFindbar(inspector);
function runTests(aInspector) {
inspector = aInspector;
inspector.selection.setNode(content.document.querySelector("body"));
inspector.once("inspector-updated", () => {
cursor = 0;
executeSoon(function() {
mouseOverContainerToShowHighlighter(nodes[0].node, nodeSelected);
});
});
}
gBrowser.removeCurrentTab();
});
function nodeSelected() {
executeSoon(function() {
performTest();
cursor++;
if (cursor >= nodes.length) {
finishUp();
} else {
let node = nodes[cursor].node;
mouseOverContainerToShowHighlighter(node, nodeSelected);
}
});
}
function* testPosition(currTest, inspector) {
let browser = gBrowser.selectedBrowser;
let stack = browser.parentNode;
function performTest() {
let browser = gBrowser.selectedBrowser;
let stack = browser.parentNode;
info("Testing " + currTest.id);
let container = stack.querySelector(".highlighter-nodeinfobar-positioner");
is(container.getAttribute("position"),
nodes[cursor].position, "node " + cursor + ": position matches.");
yield selectNode(currTest.node, inspector, "highlight");
let tagNameLabel = stack.querySelector(".highlighter-nodeinfobar-tagname");
is(tagNameLabel.textContent, nodes[cursor].tag,
"node " + cursor + ": tagName matches.");
let container = stack.querySelector(".highlighter-nodeinfobar-positioner");
is(container.getAttribute("position"),
currTest.position, "node " + currTest.id + ": position matches.");
let tagNameLabel = stack.querySelector(".highlighter-nodeinfobar-tagname");
is(tagNameLabel.textContent, currTest.tag,
"node " + currTest.id + ": tagName matches.");
if (currTest.id) {
let idLabel = stack.querySelector(".highlighter-nodeinfobar-id");
is(idLabel.textContent, nodes[cursor].id, "node " + cursor + ": id matches.");
let classesBox = stack.querySelector(".highlighter-nodeinfobar-classes");
is(classesBox.textContent, nodes[cursor].classes,
"node " + cursor + ": classes match.");
if (nodes[cursor].dims) {
let dimBox = stack.querySelector(".highlighter-nodeinfobar-dimensions");
is(dimBox.textContent, nodes[cursor].dims, "node " + cursor + ": dims match.");
}
is(idLabel.textContent, currTest.id, "node " + currTest.id + ": id matches.");
}
function finishUp() {
doc = nodes = null;
gBrowser.removeCurrentTab();
finish();
let classesBox = stack.querySelector(".highlighter-nodeinfobar-classes");
is(classesBox.textContent, currTest.classes,
"node " + currTest.id + ": classes match.");
if (currTest.dims) {
let dimBox = stack.querySelector(".highlighter-nodeinfobar-dimensions");
is(dimBox.textContent, currTest.dims, "node " + currTest.id + ": dims match.");
}
}
function* checkInfoBarAboveTop(inspector) {
yield selectNode("#abovetop", inspector);
let positioner = getPositioner();
let insideContent = parseInt(positioner.style.top, 10) >= -DOORHANGER_ARROW_HEIGHT;
ok(insideContent, "Infobar is inside the content window (top = " +
parseInt(positioner.style.top, 10) + ", content = '" +
positioner.textContent +"')");
}
function* checkInfoBarBelowFindbar(inspector) {
gFindBar.open();
let body = content.document.body;
let farBottom = body.querySelector("#farbottom");
farBottom.scrollIntoView();
// Wait for scrollIntoView
yield waitForTick();
body.scrollTop -= 130;
yield selectNode(farBottom, inspector);
let positioner = getPositioner();
let insideContent = parseInt(positioner.style.top, 10) >= -DOORHANGER_ARROW_HEIGHT;
ok(insideContent, "Infobar does not overlap the findbar (top = " +
parseInt(positioner.style.top, 10) + ", content = '" +
positioner.textContent +"')");
gFindBar.close();
}
function getPositioner() {
let browser = gBrowser.selectedBrowser;
let stack = browser.parentNode;
return stack.querySelector(".highlighter-nodeinfobar-positioner");
}
function waitForTick() {
let deferred = promise.defer();
executeSoon(deferred.resolve);
return deferred.promise;
}

View File

@ -94,7 +94,8 @@ function getNode(nodeOrSelector) {
* loaded in the toolbox
* @param {String} reason Defaults to "test" which instructs the inspector not
* to highlight the node upon selection
* @param {String} reason Defaults to "test" which instructs the inspector not to highlight the node upon selection
* @param {String} reason Defaults to "test" which instructs the inspector not
* to highlight the node upon selection
* @return a promise that resolves when the inspector is updated with the new
* node
*/

View File

@ -925,53 +925,63 @@ BoxModelHighlighter.prototype = Heritage.extend(XULBasedHighlighter.prototype, {
*/
_moveInfobar: function() {
let bounds = this._getOuterBounds();
let winHeight = this.win.innerHeight * this.zoom;
let winWidth = this.win.innerWidth * this.zoom;
if (bounds.width > 0 || bounds.height > 0) {
let winHeight = this.win.innerHeight * this.zoom;
let winWidth = this.win.innerWidth * this.zoom;
// Ensure that positionerBottom and positionerTop are at least zero to avoid
// showing tooltips outside the viewport.
let positionerBottom = Math.max(0, bounds.bottom);
let positionerTop = Math.max(0, bounds.top);
this.nodeInfo.positioner.removeAttribute("disabled");
// Can the bar be above the node?
if (bounds.top < this.nodeInfo.barHeight) {
// No. Can we move the toolbar under the node?
if (bounds.bottom + this.nodeInfo.barHeight > winHeight) {
// No. Let's move it inside.
this.nodeInfo.positioner.style.top = bounds.top + "px";
this.nodeInfo.positioner.setAttribute("position", "overlap");
} else {
// Yes. Let's move it under the node.
this.nodeInfo.positioner.style.top = bounds.bottom - INFO_BAR_OFFSET + "px";
this.nodeInfo.positioner.setAttribute("position", "bottom");
}
// Avoid showing the nodeInfoBar on top of the findbar or awesomebar.
if (this.chromeDoc.defaultView.gBrowser) {
// Get the y co-ordinate of the top of the viewport
let viewportTop = this.browser.getBoundingClientRect().top;
// Get the offset to the top of the findbar
let findbar = this.chromeDoc.defaultView.gBrowser.getFindBar();
let findTop = findbar.getBoundingClientRect().top - viewportTop;
// Either show the positioner where it is or move it above the findbar.
positionerTop = Math.min(positionerTop, findTop);
}
this.nodeInfo.positioner.removeAttribute("disabled");
// Can the bar be above the node?
if (positionerTop < this.nodeInfo.barHeight) {
// No. Can we move the toolbar under the node?
if (positionerBottom + this.nodeInfo.barHeight > winHeight) {
// No. Let's move it inside.
this.nodeInfo.positioner.style.top = positionerTop + "px";
this.nodeInfo.positioner.setAttribute("position", "overlap");
} else {
// Yes. Let's move it on top of the node.
this.nodeInfo.positioner.style.top =
bounds.top + INFO_BAR_OFFSET - this.nodeInfo.barHeight + "px";
this.nodeInfo.positioner.setAttribute("position", "top");
// Yes. Let's move it under the node.
this.nodeInfo.positioner.style.top = positionerBottom - INFO_BAR_OFFSET + "px";
this.nodeInfo.positioner.setAttribute("position", "bottom");
}
} else {
// Yes. Let's move it on top of the node.
this.nodeInfo.positioner.style.top =
positionerTop + INFO_BAR_OFFSET - this.nodeInfo.barHeight + "px";
this.nodeInfo.positioner.setAttribute("position", "top");
}
let barWidth = this.nodeInfo.positioner.getBoundingClientRect().width;
let left = bounds.right - bounds.width / 2 - barWidth / 2;
let barWidth = this.nodeInfo.positioner.getBoundingClientRect().width;
let left = bounds.right - bounds.width / 2 - barWidth / 2;
// Make sure the whole infobar is visible
if (left < 0) {
left = 0;
// Make sure the whole infobar is visible
if (left < 0) {
left = 0;
this.nodeInfo.positioner.setAttribute("hide-arrow", "true");
} else {
if (left + barWidth > winWidth) {
left = winWidth - barWidth;
this.nodeInfo.positioner.setAttribute("hide-arrow", "true");
} else {
if (left + barWidth > winWidth) {
left = winWidth - barWidth;
this.nodeInfo.positioner.setAttribute("hide-arrow", "true");
} else {
this.nodeInfo.positioner.removeAttribute("hide-arrow");
}
this.nodeInfo.positioner.removeAttribute("hide-arrow");
}
this.nodeInfo.positioner.style.left = left + "px";
} else {
this.nodeInfo.positioner.style.left = "0";
this.nodeInfo.positioner.style.top = "0";
this.nodeInfo.positioner.setAttribute("position", "top");
this.nodeInfo.positioner.setAttribute("hide-arrow", "true");
}
this.nodeInfo.positioner.style.left = left + "px";
}
});