From 72b35ceb5e33e66b46f4e537acd39433a09baa6e Mon Sep 17 00:00:00 2001 From: Michael Ratcliffe Date: Fri, 20 Jun 2014 12:25:09 +0100 Subject: [PATCH] Bug 1003569 - Hide node infobar when it is outside the viewport r=jwalker --- browser/devtools/inspector/test/browser.ini | 1 + .../test/browser_inspector_infobar.html | 43 +++ .../test/browser_inspector_infobar.js | 250 +++++++++--------- browser/devtools/inspector/test/head.js | 3 +- toolkit/devtools/server/actors/highlighter.js | 84 +++--- 5 files changed, 223 insertions(+), 158 deletions(-) create mode 100644 browser/devtools/inspector/test/browser_inspector_infobar.html diff --git a/browser/devtools/inspector/test/browser.ini b/browser/devtools/inspector/test/browser.ini index 99c7fd90a8e6..a83a42c5843f 100644 --- a/browser/devtools/inspector/test/browser.ini +++ b/browser/devtools/inspector/test/browser.ini @@ -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 diff --git a/browser/devtools/inspector/test/browser_inspector_infobar.html b/browser/devtools/inspector/test/browser_inspector_infobar.html new file mode 100644 index 000000000000..137b3487f7de --- /dev/null +++ b/browser/devtools/inspector/test/browser_inspector_infobar.html @@ -0,0 +1,43 @@ + + + + + + + + +
+
+
+
+
+ + diff --git a/browser/devtools/inspector/test/browser_inspector_infobar.js b/browser/devtools/inspector/test/browser_inspector_infobar.js index efd8d61ef826..4845918a4ac8 100644 --- a/browser/devtools/inspector/test/browser_inspector_infobar.js +++ b/browser/devtools/inspector/test/browser_inspector_infobar.js @@ -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 = "
" + - "
" + - "
" + 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; +} diff --git a/browser/devtools/inspector/test/head.js b/browser/devtools/inspector/test/head.js index eabbe3cbec34..4f5586cf4dc2 100644 --- a/browser/devtools/inspector/test/head.js +++ b/browser/devtools/inspector/test/head.js @@ -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 */ diff --git a/toolkit/devtools/server/actors/highlighter.js b/toolkit/devtools/server/actors/highlighter.js index f4af5c490fa9..8a30065dc71a 100644 --- a/toolkit/devtools/server/actors/highlighter.js +++ b/toolkit/devtools/server/actors/highlighter.js @@ -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"; } });