mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-02 22:37:50 +00:00
Bug 1003569 - Hide node infobar when it is outside the viewport r=jwalker
This commit is contained in:
parent
1220898967
commit
72b35ceb5e
@ -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
|
||||
|
@ -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>
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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";
|
||||
}
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user