Bug 1267403 - HTMLTooltip: add useXulWrapper option when displayed in a XUL document;r=ochameau
authorJulian Descottes <jdescottes@mozilla.com>
Wed, 06 Jul 2016 14:50:44 +0200
changeset 384508 eb169031125a30477cd3cb6324c1ee95d4b14b39
parent 384502 4eedf5c4e7e25be86832759f4a834608fb88ac6c
child 384509 5dc96caed88143bdfd03983a1a293ea90c0e672d
push id22283
push userjdescottes@mozilla.com
push dateWed, 06 Jul 2016 12:52:33 +0000
reviewersochameau
bugs1267403
milestone50.0a1
Bug 1267403 - HTMLTooltip: add useXulWrapper option when displayed in a XUL document;r=ochameau The HTMLTooltip supports an additional configuration parameter "useXulWrapper". When set to true, if the tooltip is displayed in a XUL document, a XUL panel will be used as an additional container for the tooltip. This allows the tooltip to be displayed anywhere on the screen and can be useful when displayed in small toolboxes. MozReview-Commit-ID: 63kv4vAeW5R
devtools/client/shared/test/browser.ini
devtools/client/shared/test/browser_html_tooltip-01.js
devtools/client/shared/test/browser_html_tooltip-02.js
devtools/client/shared/test/browser_html_tooltip-03.js
devtools/client/shared/test/browser_html_tooltip-04.js
devtools/client/shared/test/browser_html_tooltip-05.js
devtools/client/shared/test/browser_html_tooltip_arrow-01.js
devtools/client/shared/test/browser_html_tooltip_arrow-02.js
devtools/client/shared/test/browser_html_tooltip_consecutive-show.js
devtools/client/shared/test/browser_html_tooltip_offset.js
devtools/client/shared/test/browser_html_tooltip_variable-height.js
devtools/client/shared/test/browser_html_tooltip_width-auto.js
devtools/client/shared/test/browser_html_tooltip_xul-wrapper.js
devtools/client/shared/widgets/HTMLTooltip.js
devtools/client/themes/tooltips.css
--- a/devtools/client/shared/test/browser.ini
+++ b/devtools/client/shared/test/browser.ini
@@ -119,16 +119,17 @@ skip-if = e10s # Bug 1221911, bug 122228
 [browser_html_tooltip-04.js]
 [browser_html_tooltip-05.js]
 [browser_html_tooltip_arrow-01.js]
 [browser_html_tooltip_arrow-02.js]
 [browser_html_tooltip_consecutive-show.js]
 [browser_html_tooltip_offset.js]
 [browser_html_tooltip_variable-height.js]
 [browser_html_tooltip_width-auto.js]
+[browser_html_tooltip_xul-wrapper.js]
 [browser_inplace-editor-01.js]
 [browser_inplace-editor-02.js]
 [browser_inplace-editor_autocomplete_01.js]
 [browser_inplace-editor_autocomplete_02.js]
 [browser_inplace-editor_autocomplete_offset.js]
 [browser_inplace-editor_maxwidth.js]
 [browser_key_shortcuts.js]
 [browser_layoutHelpers.js]
--- a/devtools/client/shared/test/browser_html_tooltip-01.js
+++ b/devtools/client/shared/test/browser_html_tooltip-01.js
@@ -4,63 +4,57 @@
 
 "use strict";
 
 /**
  * Test the HTMLTooltip show & hide methods.
  */
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
-const TEST_WINDOW_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
+const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
   <?xml-stylesheet href="chrome://global/skin/global.css"?>
   <?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
   <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    title="Tooltip test">
     <vbox flex="1">
       <hbox id="box1" flex="1">test1</hbox>
       <hbox id="box2" flex="1">test2</hbox>
       <hbox id="box3" flex="1">test3</hbox>
       <hbox id="box4" flex="1">test4</hbox>
     </vbox>
   </window>`;
 
-const TEST_PAGE_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
-  <?xml-stylesheet href="chrome://global/skin/global.css"?>
-  <?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
-  <page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-   title="Tooltip test with document using a Page element">
-    <vbox flex="1">
-      <hbox id="box1" flex="1">test1</hbox>
-    </vbox>
-  </page>`;
-
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 function getTooltipContent(doc) {
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "50px";
   div.style.boxSizing = "border-box";
   div.textContent = "tooltip";
   return div;
 }
 
 add_task(function* () {
-  info("Test showing a basic tooltip in XUL document using <window>");
-  yield testTooltipForUri(TEST_WINDOW_URI);
+  let [,, doc] = yield createHost("bottom", TEST_URI);
 
-  info("Test showing a basic tooltip in XUL document using <page>");
-  yield testTooltipForUri(TEST_PAGE_URI);
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
 });
 
-function* testTooltipForUri(uri) {
-  let tab = yield addTab("about:blank");
-  let [,, doc] = yield createHost("bottom", uri);
-
-  let tooltip = new HTMLTooltip({doc}, {});
+function* runTests(doc) {
+  yield addTab("about:blank");
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
 
   info("Set tooltip content");
   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
 
   is(tooltip.isVisible(), false, "Tooltip is not visible");
 
   info("Show the tooltip and check the expected events are fired.");
 
@@ -85,10 +79,10 @@ function* testTooltipForUri(uri) {
   tooltip.hide();
 
   yield onPopupHidden;
   is(hidden, 1, "Event hidden was fired once");
 
   yield waitForReflow(tooltip);
   is(tooltip.isVisible(), false, "Tooltip is not visible");
 
-  yield removeTab(tab);
+  tooltip.destroy();
 }
--- a/devtools/client/shared/test/browser_html_tooltip-02.js
+++ b/devtools/client/shared/test/browser_html_tooltip-02.js
@@ -22,47 +22,59 @@ const TEST_URI = `data:text/xml;charset=
       <hbox id="box4" flex="1">test4</hbox>
       <iframe id="frame" width="200"></iframe>
     </vbox>
   </window>`;
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 add_task(function* () {
   yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
+});
+
+function* runTests(doc) {
   yield testClickInTooltipContent(doc);
   yield testConsumeOutsideClicksFalse(doc);
   yield testConsumeOutsideClicksTrue(doc);
   yield testClickInOuterIframe(doc);
   yield testClickInInnerIframe(doc);
-});
+}
 
 function* testClickInTooltipContent(doc) {
   info("Test a tooltip is not closed when clicking inside itself");
 
-  let tooltip = new HTMLTooltip({doc}, {});
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
   let onTooltipContainerClick = once(tooltip.container, "click");
   EventUtils.synthesizeMouseAtCenter(tooltip.container, {}, doc.defaultView);
   yield onTooltipContainerClick;
   is(tooltip.isVisible(), true, "Tooltip is still visible");
 
   tooltip.destroy();
 }
 
 function* testConsumeOutsideClicksFalse(doc) {
   info("Test closing a tooltip via click with consumeOutsideClicks: false");
   let box4 = doc.getElementById("box4");
 
-  let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: false});
+  let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: false, useXulWrapper});
   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
   let onBox4Clicked = once(box4, "click");
   let onHidden = once(tooltip, "hidden");
   EventUtils.synthesizeMouseAtCenter(box4, {}, doc.defaultView);
   yield onHidden;
   yield onBox4Clicked;
@@ -75,17 +87,17 @@ function* testConsumeOutsideClicksFalse(
 function* testConsumeOutsideClicksTrue(doc) {
   info("Test closing a tooltip via click with consumeOutsideClicks: true");
   let box4 = doc.getElementById("box4");
 
   // Count clicks on box4
   let box4clicks = 0;
   box4.addEventListener("click", () => box4clicks++);
 
-  let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: true});
+  let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: true, useXulWrapper});
   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
   let onHidden = once(tooltip, "hidden");
   EventUtils.synthesizeMouseAtCenter(box4, {}, doc.defaultView);
   yield onHidden;
 
   is(box4clicks, 0, "box4 catched no click event");
@@ -93,32 +105,32 @@ function* testConsumeOutsideClicksTrue(d
 
   tooltip.destroy();
 }
 
 function* testClickInOuterIframe(doc) {
   info("Test clicking an iframe outside of the tooltip closes the tooltip");
   let frame = doc.getElementById("frame");
 
-  let tooltip = new HTMLTooltip({doc});
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
   let onHidden = once(tooltip, "hidden");
   EventUtils.synthesizeMouseAtCenter(frame, {}, doc.defaultView);
   yield onHidden;
 
   is(tooltip.isVisible(), false, "Tooltip is hidden");
   tooltip.destroy();
 }
 
 function* testClickInInnerIframe(doc) {
   info("Test clicking an iframe inside the tooltip content does not close the tooltip");
 
-  let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: false});
+  let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: false, useXulWrapper});
 
   let iframe = doc.createElementNS(HTML_NS, "iframe");
   iframe.style.width = "100px";
   iframe.style.height = "50px";
   tooltip.setContent(iframe, {width: 100, height: 50});
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
   let onTooltipContainerClick = once(tooltip.container, "click");
--- a/devtools/client/shared/test/browser_html_tooltip-03.js
+++ b/devtools/client/shared/test/browser_html_tooltip-03.js
@@ -26,24 +26,36 @@ const TEST_URI = `data:text/xml;charset=
         <textbox id="box4-input"></textbox>
       </hbox>
     </vbox>
   </window>`;
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 add_task(function* () {
   yield addTab("about:blank");
   let [, , doc] = yield createHost("bottom", TEST_URI);
 
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
+});
+
+function* runTests(doc) {
   yield testNoAutoFocus(doc);
   yield testAutoFocus(doc);
   yield testAutoFocusPreservesFocusChange(doc);
-});
+}
 
 function* testNoAutoFocus(doc) {
   yield focusNode(doc, "#box4-input");
   ok(doc.activeElement.closest("#box4-input"), "Focus is in the #box4-input");
 
   info("Test a tooltip without autofocus will not take focus");
   let tooltip = yield createTooltip(doc, false);
 
@@ -121,17 +133,17 @@ function blurNode(doc, selector) {
  *
  * @param {Document} doc
  *        Document in which the tooltip should be created
  * @param {Boolean} autofocus
  * @return {Promise} promise that will resolve the HTMLTooltip instance created when the
  *         tooltip content will be ready.
  */
 function* createTooltip(doc, autofocus) {
-  let tooltip = new HTMLTooltip({doc}, {autofocus});
+  let tooltip = new HTMLTooltip({doc}, {autofocus, useXulWrapper});
   let div = doc.createElementNS(HTML_NS, "div");
   div.classList.add("tooltip-content");
   div.style.height = "50px";
   div.innerHTML = '<input type="text"></input>';
 
   tooltip.setContent(div, {width: 150, height: 50});
   return tooltip;
 }
--- a/devtools/client/shared/test/browser_html_tooltip-04.js
+++ b/devtools/client/shared/test/browser_html_tooltip-04.js
@@ -35,17 +35,17 @@ const TOOLTIP_WIDTH = 100;
 add_task(function* () {
   // Force the toolbox to be 400px high;
   yield pushPref("devtools.toolbox.footer.height", 400);
 
   yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
   info("Create HTML tooltip");
-  let tooltip = new HTMLTooltip({doc}, {});
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper: false});
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "100%";
   tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
 
   let box1 = doc.getElementById("box1");
   let box2 = doc.getElementById("box2");
   let box3 = doc.getElementById("box3");
   let box4 = doc.getElementById("box4");
--- a/devtools/client/shared/test/browser_html_tooltip-05.js
+++ b/devtools/client/shared/test/browser_html_tooltip-05.js
@@ -27,22 +27,21 @@ const {HTMLTooltip} = require("devtools/
 loadHelperScript("helper_html_tooltip.js");
 
 const TOOLTIP_HEIGHT = 200;
 const TOOLTIP_WIDTH = 200;
 
 add_task(function* () {
   // Force the toolbox to be 200px high;
   yield pushPref("devtools.toolbox.footer.height", 200);
-
   yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
   info("Create HTML tooltip");
-  let tooltip = new HTMLTooltip({doc}, {});
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper: false});
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "100%";
   tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
 
   let box1 = doc.getElementById("box1");
   let box2 = doc.getElementById("box2");
   let box3 = doc.getElementById("box3");
   let box4 = doc.getElementById("box4");
@@ -52,17 +51,17 @@ add_task(function* () {
   // height of 150px.
   info("Display the tooltip on box1.");
   yield showTooltip(tooltip, box1);
   let expectedTooltipGeometry = {position: "bottom", height: 150, width};
   checkTooltipGeometry(tooltip, box1, expectedTooltipGeometry);
   yield hideTooltip(tooltip);
 
   info("Try to display the tooltip on top of box1.");
-  yield showTooltip(tooltip, box1, "top");
+  yield showTooltip(tooltip, box1, {position: "top"});
   expectedTooltipGeometry = {position: "bottom", height: 150, width};
   checkTooltipGeometry(tooltip, box1, expectedTooltipGeometry);
   yield hideTooltip(tooltip);
 
   // box2: Can not fit above or below box2, default to bottom with a reduced
   // height of 100px.
   info("Try to display the tooltip on box2.");
   yield showTooltip(tooltip, box2);
--- a/devtools/client/shared/test/browser_html_tooltip_arrow-01.js
+++ b/devtools/client/shared/test/browser_html_tooltip_arrow-01.js
@@ -44,25 +44,37 @@ const TEST_URI = `data:text/xml;charset=
       ${getAnchor("top: 0; right: 50px;")}
       ${getAnchor("top: 0; right: 75px;")}
     </vbox>
   </window>`;
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 add_task(function* () {
   // Force the toolbox to be 200px high;
   yield pushPref("devtools.toolbox.footer.height", 200);
 
   yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
+});
+
+function* runTests(doc) {
   info("Create HTML tooltip");
-  let tooltip = new HTMLTooltip({doc}, {type: "arrow"});
+  let tooltip = new HTMLTooltip({doc}, {type: "arrow", useXulWrapper});
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "35px";
   tooltip.setContent(div, {width: 200, height: 35});
 
   let {right: docRight} = doc.documentElement.getBoundingClientRect();
 
   let elements = [...doc.querySelectorAll(".anchor")];
   for (let el of elements) {
@@ -86,9 +98,11 @@ add_task(function* () {
 
     let isInPanel = arrowBounds.left >= panelBounds.left &&
                     arrowBounds.right <= panelBounds.right;
     ok(isInPanel,
       "The tooltip arrow remains inside the tooltip panel horizontally");
 
     yield hideTooltip(tooltip);
   }
-});
+
+  tooltip.destroy();
+}
--- a/devtools/client/shared/test/browser_html_tooltip_arrow-02.js
+++ b/devtools/client/shared/test/browser_html_tooltip_arrow-02.js
@@ -38,25 +38,36 @@ const TEST_URI = `data:text/xml;charset=
       ${getAnchor("top: 130px; width: 200px; right: 0;")}
       ${getAnchor("top: 140px; width: 250px; right: 0;")}
     </vbox>
   </window>`;
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 add_task(function* () {
   // Force the toolbox to be 200px high;
   yield pushPref("devtools.toolbox.footer.height", 200);
 
-  yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
+});
+
+function* runTests(doc) {
   info("Create HTML tooltip");
-  let tooltip = new HTMLTooltip({doc}, {type: "arrow"});
+  let tooltip = new HTMLTooltip({doc}, {type: "arrow", useXulWrapper});
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "35px";
   tooltip.setContent(div, {width: 200, height: 35});
 
   let {right: docRight} = doc.documentElement.getBoundingClientRect();
 
   let elements = [...doc.querySelectorAll(".anchor")];
   for (let el of elements) {
@@ -79,9 +90,9 @@ add_task(function* () {
       "Tooltip arrow is aligned with the anchor, or stuck on viewport's edge.");
 
     let isInPanel = arrowBounds.left >= panelBounds.left &&
                     arrowBounds.right <= panelBounds.right;
     ok(isInPanel,
       "The tooltip arrow remains inside the tooltip panel horizontally");
     yield hideTooltip(tooltip);
   }
-});
+}
--- a/devtools/client/shared/test/browser_html_tooltip_consecutive-show.js
+++ b/devtools/client/shared/test/browser_html_tooltip_consecutive-show.js
@@ -29,27 +29,26 @@ loadHelperScript("helper_html_tooltip.js
 function getTooltipContent(doc) {
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "50px";
   div.textContent = "tooltip";
   return div;
 }
 
 add_task(function* () {
-  yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
   let box1 = doc.getElementById("box1");
   let box2 = doc.getElementById("box2");
   let box3 = doc.getElementById("box3");
   let box4 = doc.getElementById("box4");
 
   let width = 100, height = 50;
 
-  let tooltip = new HTMLTooltip({doc}, {});
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper: false});
   tooltip.setContent(getTooltipContent(doc), {width, height});
 
   info("Show the tooltip on each of the 4 hbox, without calling hide in between");
 
   info("Show tooltip on box1");
   tooltip.show(box1);
   checkTooltipGeometry(tooltip, box1, {position: "bottom", width, height});
 
--- a/devtools/client/shared/test/browser_html_tooltip_offset.js
+++ b/devtools/client/shared/test/browser_html_tooltip_offset.js
@@ -25,27 +25,26 @@ const TEST_URI = `data:text/xml;charset=
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
 add_task(function* () {
   // Force the toolbox to be 200px high;
   yield pushPref("devtools.toolbox.footer.height", 200);
 
-  yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
   info("Test a tooltip is not closed when clicking inside itself");
 
   let box1 = doc.getElementById("box1");
   let box2 = doc.getElementById("box2");
   let box3 = doc.getElementById("box3");
   let box4 = doc.getElementById("box4");
 
-  let tooltip = new HTMLTooltip({doc}, {});
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper: false});
 
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "100px";
   div.style.boxSizing = "border-box";
   div.textContent = "tooltip";
   tooltip.setContent(div, {width: 50, height: 100});
 
   info("Display the tooltip on box1.");
--- a/devtools/client/shared/test/browser_html_tooltip_variable-height.js
+++ b/devtools/client/shared/test/browser_html_tooltip_variable-height.js
@@ -24,24 +24,36 @@ const TEST_URI = `data:text/xml;charset=
 
 const CONTAINER_HEIGHT = 200;
 const CONTAINER_WIDTH = 200;
 const TOOLTIP_HEIGHT = 50;
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 add_task(function* () {
   // Force the toolbox to be 400px tall => 50px for each box.
   yield pushPref("devtools.toolbox.footer.height", 400);
 
   yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
-  let tooltip = new HTMLTooltip({doc}, {});
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
+});
+
+function* runTests(doc) {
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
   info("Set tooltip content 50px tall, but request a container 200px tall");
   let tooltipContent = doc.createElementNS(HTML_NS, "div");
   tooltipContent.style.cssText = "height: " + TOOLTIP_HEIGHT + "px; background: red;";
   tooltip.setContent(tooltipContent, {width: CONTAINER_WIDTH, height: CONTAINER_HEIGHT});
 
   info("Show the tooltip and check the container and panel height.");
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
@@ -66,9 +78,9 @@ add_task(function* () {
   yield onPanelClick;
   is(tooltip.isVisible(), true, "Tooltip is still visible");
 
   info("Click below the tooltip container, the tooltip should be closed.");
   onHidden = once(tooltip, "hidden");
   EventUtils.synthesizeMouse(tooltip.container, 100, CONTAINER_HEIGHT + 10,
     {}, doc.defaultView);
   yield onHidden;
-});
+}
--- a/devtools/client/shared/test/browser_html_tooltip_width-auto.js
+++ b/devtools/client/shared/test/browser_html_tooltip_width-auto.js
@@ -20,28 +20,40 @@ const TEST_URI = `data:text/xml;charset=
       <hbox id="box3" flex="1">test3</hbox>
       <hbox id="box4" flex="1">test4</hbox>
     </vbox>
   </window>`;
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 add_task(function* () {
   yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
-  let tooltip = new HTMLTooltip({doc}, {});
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
+});
+
+function* runTests(doc) {
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
   info("Create tooltip content width to 150px");
   let tooltipContent = doc.createElementNS(HTML_NS, "div");
   tooltipContent.style.cssText = "height: 100%; width: 150px; background: red;";
 
   info("Set tooltip content using width:auto");
   tooltip.setContent(tooltipContent, {width: "auto", height: 50});
 
   info("Show the tooltip and check the tooltip panel width.");
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
   let panelRect = tooltip.panel.getBoundingClientRect();
   is(panelRect.width, 150, "Tooltip panel has the expected width.");
 
   yield hideTooltip(tooltip);
-});
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_xul-wrapper.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip can overflow out of the toolbox when using a XUL panel wrapper.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
+  <?xml-stylesheet href="chrome://global/skin/global.css"?>
+  <?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
+  <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+   title="Tooltip test">
+    <vbox flex="1">
+      <hbox id="box1" style="height: 50px">test1</hbox>
+      <hbox id="box2" style="height: 50px">test2</hbox>
+      <hbox id="box3" style="height: 50px">test3</hbox>
+      <hbox id="box4" style="height: 50px">test4</hbox>
+    </vbox>
+  </window>`;
+
+const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
+loadHelperScript("helper_html_tooltip.js");
+
+// The test toolbox will be 200px tall, the anchors are 50px tall, therefore, the maximum
+// tooltip height that could fit in the toolbox is 150px. Setting 160px, the tooltip will
+// either have to overflow or to be resized.
+const TOOLTIP_HEIGHT = 160;
+const TOOLTIP_WIDTH = 200;
+
+add_task(function* () {
+  // Force the toolbox to be 200px high;
+  yield pushPref("devtools.toolbox.footer.height", 200);
+
+  let [, win, doc] = yield createHost("bottom", TEST_URI);
+
+  info("Resizing window to have some space below the window.");
+  let originalWidth = win.top.outerWidth;
+  let originalHeight = win.top.outerHeight;
+  win.top.resizeBy(0, -100);
+
+  info("Create HTML tooltip");
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper: true});
+  let div = doc.createElementNS(HTML_NS, "div");
+  div.style.height = "200px";
+  div.style.background = "red";
+  tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
+
+  let box1 = doc.getElementById("box1");
+
+  // Above box1: check that the tooltip can overflow onto the content page.
+  info("Display the tooltip above box1.");
+  yield showTooltip(tooltip, box1, {position: "top"});
+  checkTooltip(tooltip, "top", TOOLTIP_HEIGHT);
+  yield hideTooltip(tooltip);
+
+  // Below box1: check that the tooltip can overflow out of the browser window.
+  info("Display the tooltip below box1.");
+  yield showTooltip(tooltip, box1, {position: "bottom"});
+  checkTooltip(tooltip, "bottom", TOOLTIP_HEIGHT);
+  yield hideTooltip(tooltip);
+
+  is(tooltip.isVisible(), false, "Tooltip is not visible");
+
+  info("Restore original window dimensions.");
+  win.top.resizeTo(originalWidth, originalHeight);
+});
+
+function checkTooltip(tooltip, position, height) {
+  is(tooltip.position, position, "Actual tooltip position is " + position);
+  let rect = tooltip.container.getBoundingClientRect();
+  is(rect.height, height, "Actual tooltip height is " + height);
+  // Testing the actual left/top offsets is not relevant here as it is handled by the XUL
+  // panel.
+}
--- a/devtools/client/shared/widgets/HTMLTooltip.js
+++ b/devtools/client/shared/widgets/HTMLTooltip.js
@@ -3,16 +3,18 @@
 /* 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";
 
 const EventEmitter = require("devtools/shared/event-emitter");
 const {TooltipToggle} = require("devtools/client/shared/widgets/tooltip/TooltipToggle");
+const {listenOnce} = require("devtools/shared/async-utils");
+const {Task} = require("devtools/shared/task");
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 
 const POSITION = {
   TOP: "top",
   BOTTOM: "bottom",
 };
@@ -44,37 +46,41 @@ const EXTRA_BORDER = {
 
 /**
  * Calculate the vertical position & offsets to use for the tooltip. Will attempt to
  * respect the provided height and position preferences, unless the available height
  * prevents this.
  *
  * @param {DOMRect} anchorRect
  *        Bounding rectangle for the anchor, relative to the tooltip document.
- * @param {DOMRect} docRect
- *        Bounding rectange for the tooltip document owner.
+ * @param {DOMRect} viewportRect
+ *        Bounding rectangle for the viewport. top/left can be different from 0 if some
+ *        space should not be used by tooltips (for instance OS toolbars, taskbars etc.).
  * @param {Number} height
  *        Preferred height for the tooltip.
  * @param {String} pos
  *        Preferred position for the tooltip. Possible values: "top" or "bottom".
  * @return {Object}
  *         - {Number} top: the top offset for the tooltip.
  *         - {Number} height: the height to use for the tooltip container.
  *         - {String} computedPosition: Can differ from the preferred position depending
  *           on the available height). "top" or "bottom"
  */
-const calculateVerticalPosition = function (anchorRect, docRect, height, pos, offset) {
+const calculateVerticalPosition =
+function (anchorRect, viewportRect, height, pos, offset) {
   let {TOP, BOTTOM} = POSITION;
 
   let {top: anchorTop, height: anchorHeight} = anchorRect;
-  let {bottom: docBottom} = docRect;
+
+  // Translate to the available viewport space before calculating dimensions and position.
+  anchorTop -= viewportRect.top;
 
   // Calculate available space for the tooltip.
   let availableTop = anchorTop;
-  let availableBottom = docBottom - (anchorTop + anchorHeight);
+  let availableBottom = viewportRect.height - (anchorTop + anchorHeight);
 
   // Find POSITION
   let keepPosition = false;
   if (pos === TOP) {
     keepPosition = availableTop >= height + offset;
   } else if (pos === BOTTOM) {
     keepPosition = availableBottom >= height + offset;
   }
@@ -85,47 +91,53 @@ const calculateVerticalPosition = functi
   // Calculate HEIGHT.
   let availableHeight = pos === TOP ? availableTop : availableBottom;
   height = Math.min(height, availableHeight - offset);
   height = Math.floor(height);
 
   // Calculate TOP.
   let top = pos === TOP ? anchorTop - height - offset : anchorTop + anchorHeight + offset;
 
+  // Translate back to absolute coordinates by re-including viewport top margin.
+  top += viewportRect.top;
+
   return {top, height, computedPosition: pos};
 };
 
 /**
  * Calculate the vertical position & offsets to use for the tooltip. Will attempt to
  * respect the provided height and position preferences, unless the available height
  * prevents this.
  *
  * @param {DOMRect} anchorRect
  *        Bounding rectangle for the anchor, relative to the tooltip document.
- * @param {DOMRect} docRect
- *        Bounding rectange for the tooltip document owner.
+ * @param {DOMRect} viewportRect
+ *        Bounding rectangle for the viewport. top/left can be different from 0 if some
+ *        space should not be used by tooltips (for instance OS toolbars, taskbars etc.).
  * @param {Number} width
  *        Preferred width for the tooltip.
  * @return {Object}
  *         - {Number} left: the left offset for the tooltip.
  *         - {Number} width: the width to use for the tooltip container.
  *         - {Number} arrowLeft: the left offset to use for the arrow element.
  */
-const calculateHorizontalPosition = function (anchorRect, docRect, width, type, offset) {
+const calculateHorizontalPosition =
+function (anchorRect, viewportRect, width, type, offset) {
   let {left: anchorLeft, width: anchorWidth} = anchorRect;
-  let {right: docRight} = docRect;
+
+  // Translate to the available viewport space before calculating dimensions and position.
+  anchorLeft -= viewportRect.left;
 
   // Calculate WIDTH.
-  let availableWidth = docRight;
-  width = Math.min(width, availableWidth);
+  width = Math.min(width, viewportRect.width);
 
   // Calculate LEFT.
   // By default the tooltip is aligned with the anchor left edge. Unless this
   // makes it overflow the viewport, in which case is shifts to the left.
-  let left = Math.min(anchorLeft + offset, docRight - width);
+  let left = Math.min(anchorLeft + offset, viewportRect.width - width);
 
   // Calculate ARROW LEFT (tooltip's LEFT might be updated)
   let arrowLeft;
   // Arrow style tooltips may need to be shifted to the left
   if (type === TYPE.ARROW) {
     let arrowCenter = left + ARROW_OFFSET + ARROW_WIDTH / 2;
     let anchorCenter = anchorLeft + anchorWidth / 2;
     // If the anchor is too narrow, align the arrow and the anchor center.
@@ -136,16 +148,19 @@ const calculateHorizontalPosition = func
     arrowLeft = Math.min(ARROW_OFFSET, (anchorWidth - ARROW_WIDTH) / 2) | 0;
     // Translate the coordinate to tooltip container
     arrowLeft += anchorLeft - left;
     // Make sure the arrow remains in the tooltip container.
     arrowLeft = Math.min(arrowLeft, width - ARROW_WIDTH);
     arrowLeft = Math.max(arrowLeft, 0);
   }
 
+  // Translate back to absolute coordinates by re-including viewport left margin.
+  left += viewportRect.left;
+
   return {left, width, arrowLeft};
 };
 
 /**
  * Get the bounding client rectangle for a given node, relative to a custom
  * reference element (instead of the default for getBoundingClientRect which
  * is always the element's ownerDocument).
  */
@@ -172,38 +187,61 @@ const getRelativeRect = function (node, 
  * @param {Object}
  *        - {String} type
  *          Display type of the tooltip. Possible values: "normal", "arrow"
  *        - {Boolean} autofocus
  *          Defaults to false. Should the tooltip be focused when opening it.
  *        - {Boolean} consumeOutsideClicks
  *          Defaults to true. The tooltip is closed when clicking outside.
  *          Should this event be stopped and consumed or not.
+ *        - {Boolean} useXulWrapper
+ *          Defaults to true. If the tooltip is hosted in a XUL document, use a XUL panel
+ *          in order to use all the screen viewport available.
  */
-function HTMLTooltip(toolbox,
-  {type = "normal", autofocus = false, consumeOutsideClicks = true} = {}) {
+function HTMLTooltip(toolbox, {
+    type = "normal",
+    autofocus = false,
+    consumeOutsideClicks = true,
+    useXulWrapper = true,
+  } = {}) {
   EventEmitter.decorate(this);
 
   this.doc = toolbox.doc;
   this.type = type;
   this.autofocus = autofocus;
   this.consumeOutsideClicks = consumeOutsideClicks;
+  this.useXulWrapper = useXulWrapper;
+
+  this._position = null;
 
   // Use the topmost window to listen for click events to close the tooltip
   this.topWindow = this.doc.defaultView.top;
 
   this._onClick = this._onClick.bind(this);
 
   this._toggle = new TooltipToggle(this);
   this.startTogglingOnHover = this._toggle.start.bind(this._toggle);
   this.stopTogglingOnHover = this._toggle.stop.bind(this._toggle);
 
   this.container = this._createContainer();
 
-  if (this._isXUL()) {
+  if (this._isXUL() && this.useXulWrapper) {
+    // When using a XUL panel as the wrapper, the actual markup for the tooltip is as
+    // follows :
+    // <panel> <!-- XUL panel used to position the tooltip anywhere on screen -->
+    //   <div> <!-- div wrapper used to isolate the tooltip container -->
+    //     <div> <! the actual tooltip.container element -->
+    this.xulPanelWrapper = this._createXulPanelWrapper();
+    let inner = this.doc.createElementNS(XHTML_NS, "div");
+    inner.classList.add("tooltip-xul-wrapper-inner");
+
+    this.doc.documentElement.appendChild(this.xulPanelWrapper);
+    this.xulPanelWrapper.appendChild(inner);
+    inner.appendChild(this.container);
+  } else if (this._isXUL()) {
     this.doc.documentElement.appendChild(this.container);
   } else {
     // In non-XUL context the container is ready to use as is.
     this.doc.body.appendChild(this.container);
   }
 }
 
 module.exports.HTMLTooltip = HTMLTooltip;
@@ -220,16 +258,23 @@ HTMLTooltip.prototype = {
   /**
    * The arrow element. Might be null depending on the tooltip type.
    */
   get arrow() {
     return this.container.querySelector(".tooltip-arrow");
   },
 
   /**
+   * Retrieve the displayed position used for the tooltip. Null if the tooltip is hidden.
+   */
+  get position() {
+    return this.isVisible() ? this._position : null;
+  },
+
+  /**
    * Set the tooltip content element. The preferred width/height should also be
    * specified here.
    *
    * @param {Element} content
    *        The tooltip content, should be a HTML element.
    * @param {Object}
    *        - {Number} width: preferred width for the tooltip container. If not specified
    *          the tooltip container will be measured before being displayed, and the
@@ -255,107 +300,167 @@ HTMLTooltip.prototype = {
    * @param {Object}
    *        - {String} position: optional, possible values: top|bottom
    *          If layout permits, the tooltip will be displayed on top/bottom
    *          of the anchor. If ommitted, the tooltip will be displayed where
    *          more space is available.
    *        - {Number} x: optional, horizontal offset between the anchor and the tooltip
    *        - {Number} y: optional, vertical offset between the anchor and the tooltip
    */
-  show: function (anchor, {position, x = 0, y = 0} = {}) {
+  show: Task.async(function* (anchor, {position, x = 0, y = 0} = {}) {
     // Get anchor geometry
     let anchorRect = getRelativeRect(anchor, this.doc);
-    // Get document geometry
-    let docRect = this.doc.documentElement.getBoundingClientRect();
+    if (this.useXulWrapper) {
+      anchorRect = this._convertToScreenRect(anchorRect);
+    }
+
+    // Get viewport size
+    let viewportRect = this._getViewportRect();
 
     let themeHeight = EXTRA_HEIGHT[this.type] + 2 * EXTRA_BORDER[this.type];
     let preferredHeight = this.preferredHeight + themeHeight;
 
     let {top, height, computedPosition} =
-      calculateVerticalPosition(anchorRect, docRect, preferredHeight, position, y);
+      calculateVerticalPosition(anchorRect, viewportRect, preferredHeight, position, y);
 
-    // Apply height and top information before measuring the content width (if "auto").
+    this._position = computedPosition;
+    // Apply height before measuring the content width (if width="auto").
     let isTop = computedPosition === POSITION.TOP;
     this.container.classList.toggle("tooltip-top", isTop);
     this.container.classList.toggle("tooltip-bottom", !isTop);
     this.container.style.height = height + "px";
-    this.container.style.top = top + "px";
 
-    let themeWidth = 2 * EXTRA_BORDER[this.type];
-    let preferredWidth = this.preferredWidth === "auto" ?
-      this._measureContainerWidth() : this.preferredWidth + themeWidth;
+    let preferredWidth;
+    if (this.preferredWidth === "auto") {
+      preferredWidth = this._measureContainerWidth();
+    } else {
+      let themeWidth = 2 * EXTRA_BORDER[this.type];
+      preferredWidth = this.preferredWidth + themeWidth;
+    }
 
     let {left, width, arrowLeft} =
-      calculateHorizontalPosition(anchorRect, docRect, preferredWidth, this.type, x);
+      calculateHorizontalPosition(anchorRect, viewportRect, preferredWidth, this.type, x);
 
     this.container.style.width = width + "px";
-    this.container.style.left = left + "px";
 
     if (this.type === TYPE.ARROW) {
       this.arrow.style.left = arrowLeft + "px";
     }
 
+    if (this.useXulWrapper) {
+      this._showXulWrapperAt(left, top);
+    } else {
+      this.container.style.left = left + "px";
+      this.container.style.top = top + "px";
+    }
+
     this.container.classList.add("tooltip-visible");
 
     // Keep a pointer on the focused element to refocus it when hiding the tooltip.
     this._focusedElement = this.doc.activeElement;
 
     this.doc.defaultView.clearTimeout(this.attachEventsTimer);
     this.attachEventsTimer = this.doc.defaultView.setTimeout(() => {
       this._maybeFocusTooltip();
       this.topWindow.addEventListener("click", this._onClick, true);
       this.emit("shown");
     }, 0);
+  }),
+
+  /**
+   * Calculate the rect of the viewport that limits the tooltip dimensions. When using a
+   * XUL panel wrapper, the viewport will be able to use the whole screen (excluding space
+   * reserved by the OS for toolbars etc.). Otherwise, the viewport is limited to the
+   * tooltip's document.
+   *
+   * @return {Object} DOMRect-like object with the Number properties: top, right, bottom,
+   *         left, width, height
+   */
+  _getViewportRect: function () {
+    if (this.useXulWrapper) {
+      // availLeft/Top are the coordinates first pixel available on the screen for
+      // applications (excluding space dedicated for OS toolbars, menus etc...)
+      // availWidth/Height are the dimensions available to applications excluding all
+      // the OS reserved space
+      let {availLeft, availTop, availHeight, availWidth} = this.doc.defaultView.screen;
+      return {
+        top: availTop,
+        right: availLeft + availWidth,
+        bottom: availTop + availHeight,
+        left: availLeft,
+        width: availWidth,
+        height: availHeight,
+      };
+    }
+
+    return this.doc.documentElement.getBoundingClientRect();
   },
 
   _measureContainerWidth: function () {
+    let xulParent = this.container.parentNode;
+    if (this.useXulWrapper && !this.isVisible()) {
+      // Move the container out of the XUL Panel to measure it.
+      this.doc.documentElement.appendChild(this.container);
+    }
+
     this.container.classList.add("tooltip-hidden");
-    this.container.style.left = "0px";
     this.container.style.width = "auto";
     let width = this.container.getBoundingClientRect().width;
     this.container.classList.remove("tooltip-hidden");
+
+    if (this.useXulWrapper && !this.isVisible()) {
+      xulParent.appendChild(this.container);
+    }
+
     return width;
   },
 
   /**
    * Hide the current tooltip. The event "hidden" will be fired when the tooltip
    * is hidden.
    */
-  hide: function () {
+  hide: Task.async(function* () {
     this.doc.defaultView.clearTimeout(this.attachEventsTimer);
     if (!this.isVisible()) {
       return;
     }
 
     this.topWindow.removeEventListener("click", this._onClick, true);
     this.container.classList.remove("tooltip-visible");
+    if (this.useXulWrapper) {
+      yield this._hideXulWrapper();
+    }
+
     this.emit("hidden");
 
     let tooltipHasFocus = this.container.contains(this.doc.activeElement);
     if (tooltipHasFocus && this._focusedElement) {
       this._focusedElement.focus();
       this._focusedElement = null;
     }
-  },
+  }),
 
   /**
    * Check if the tooltip is currently displayed.
    * @return {Boolean} true if the tooltip is visible
    */
   isVisible: function () {
     return this.container.classList.contains("tooltip-visible");
   },
 
   /**
    * Destroy the tooltip instance. Hide the tooltip if displayed, remove the
    * tooltip container from the document.
    */
   destroy: function () {
     this.hide();
     this.container.remove();
+    if (this.xulPanelWrapper) {
+      this.xulPanelWrapper.remove();
+    }
   },
 
   _createContainer: function () {
     let container = this.doc.createElementNS(XHTML_NS, "div");
     container.setAttribute("type", this.type);
     container.classList.add("tooltip-container");
 
     let html = '<div class="tooltip-filler"></div>';
@@ -403,28 +508,69 @@ HTMLTooltip.prototype = {
       }
       win = win.parent;
     }
 
     return false;
   },
 
   /**
-   * Check if the tooltip's owner document is a XUL document.
-   */
-  _isXUL: function () {
-    return this.doc.documentElement.namespaceURI === XUL_NS;
-  },
-
-  /**
    * If the tootlip is configured to autofocus and a focusable element can be found,
    * focus it.
    */
   _maybeFocusTooltip: function () {
     // Simplied selector targetting elements that can receive the focus, full version at
     // http://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus .
     let focusableSelector = "a, button, iframe, input, select, textarea";
     let focusableElement = this.panel.querySelector(focusableSelector);
     if (this.autofocus && focusableElement) {
       focusableElement.focus();
     }
   },
+
+  /**
+   * Check if the tooltip's owner document is a XUL document.
+   */
+  _isXUL: function () {
+    return this.doc.documentElement.namespaceURI === XUL_NS;
+  },
+
+  _createXulPanelWrapper: function () {
+    let panel = this.doc.createElementNS(XUL_NS, "panel");
+
+    // XUL panel is only a way to display DOM elements outside of the document viewport,
+    // so disable all features that impact the behavior.
+    panel.setAttribute("animate", false);
+    panel.setAttribute("consumeoutsideclicks", false);
+    panel.setAttribute("noautofocus", true);
+    panel.setAttribute("ignorekeys", true);
+
+    panel.setAttribute("level", "float");
+    panel.setAttribute("class", "tooltip-xul-wrapper");
+
+    return panel;
+  },
+
+  _showXulWrapperAt: function (left, top) {
+    let onPanelShown = listenOnce(this.xulPanelWrapper, "popupshown");
+    this.xulPanelWrapper.openPopupAtScreen(left, top, false);
+    return onPanelShown;
+  },
+
+  _hideXulWrapper: function () {
+    let onPanelHidden = listenOnce(this.xulPanelWrapper, "popuphidden");
+    this.xulPanelWrapper.hidePopup();
+    return onPanelHidden;
+  },
+
+  /**
+   * Convert from coordinates relative to the tooltip's document, to coordinates relative
+   * to the "available" screen. By "available" we mean the screen, excluding the OS bars
+   * display on screen edges.
+   */
+  _convertToScreenRect: function ({left, top, width, height}) {
+    // mozInnerScreenX/Y are the coordinates of the top left corner of the window's
+    // viewport, excluding chrome UI.
+    left += this.doc.defaultView.mozInnerScreenX;
+    top += this.doc.defaultView.mozInnerScreenY;
+    return {top, right: left + width, bottom: top + height, left, width, height};
+  },
 };
--- a/devtools/client/themes/tooltips.css
+++ b/devtools/client/themes/tooltips.css
@@ -104,16 +104,27 @@
   position: fixed;
   z-index: 9999;
   display: none;
   background: transparent;
   pointer-events: none;
   overflow: hidden;
 }
 
+.tooltip-xul-wrapper {
+  -moz-appearance: none;
+  background: transparent;
+  overflow: visible;
+  border-style: none;
+}
+
+.tooltip-xul-wrapper .tooltip-container {
+  position: absolute;
+}
+
 .tooltip-top {
   flex-direction: column;
 }
 
 .tooltip-bottom {
   flex-direction: column-reverse;
 }
 
@@ -132,16 +143,21 @@
 }
 
 /* Tooltip : arrow style */
 
 .tooltip-container[type="arrow"] {
   filter: drop-shadow(0 3px 4px var(--theme-tooltip-shadow));
 }
 
+.tooltip-xul-wrapper .tooltip-container[type="arrow"] {
+  /* When displayed in a XUL panel the drop shadow would be abruptly cut by the panel */
+  filter: none;
+}
+
 .tooltip-container[type="arrow"] > .tooltip-panel {
   position: relative;
   flex-grow: 0;
   min-height: 10px;
   box-sizing: border-box;
   width: 100%;
 
   border: 3px solid var(--theme-tooltip-border);
@@ -260,17 +276,17 @@
 }
 
 .event-tooltip-debugger-icon:hover {
   opacity: 1;
 }
 
 .event-tooltip-content-box {
   display: none;
-  height: 54px;
+  height: 100px;
   overflow: hidden;
   margin-inline-end: 0;
   border: 1px solid var(--theme-splitter-color);
   border-width: 1px 0 0 0;
 }
 
 .event-toolbox-content-box iframe {
   height: 100%;