--- a/devtools/client/shared/test/browser_html_tooltip-03.js
+++ b/devtools/client/shared/test/browser_html_tooltip-03.js
@@ -12,83 +12,136 @@ const HTML_NS = "http://www.w3.org/1999/
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/common.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="box3" flex="1">
+ <textbox id="box3-input"></textbox>
+ </hbox>
<hbox id="box4" flex="1">
<textbox id="box4-input"></textbox>
</hbox>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
add_task(function* () {
yield addTab("about:blank");
let [,, doc] = yield createHost("bottom", TEST_URI);
- yield testTooltipWithAutoFocus(doc);
- yield testTooltipWithoutAutoFocus(doc);
+ yield testNoAutoFocus(doc);
+ yield testAutoFocus(doc);
+ yield testAutoFocusPreservesFocusChange(doc);
});
-function* testTooltipWithAutoFocus(doc) {
- info("Test a tooltip with autofocus takes focus when displayed");
- let textbox = doc.querySelector("textbox");
-
- info("Focus a XUL textbox");
- let onInputFocus = once(textbox, "focus");
- EventUtils.synthesizeMouseAtCenter(textbox, {}, doc.defaultView);
- yield onInputFocus;
-
+function* testNoAutoFocus(doc) {
+ yield focusNode(doc, "#box4-input");
is(getFocusedDocument(doc), doc, "Focus is in the XUL document");
- let tooltip = new HTMLTooltip({doc}, {autofocus: true});
- let tooltipNode = getTooltipContent(doc);
- yield tooltip.setContent(tooltipNode, 150, 50);
+ info("Test a tooltip without autofocus will not take focus");
+ let tooltip = yield createTooltip(doc, false);
yield showTooltip(tooltip, doc.getElementById("box1"));
- is(getFocusedDocument(doc), tooltipNode.ownerDocument,
+ is(getFocusedDocument(doc), doc, "Focus is still in the XUL document");
+ ok(doc.activeElement.closest("#box4-input"), "Focus is in the #box4-input");
+
+ yield hideTooltip(tooltip);
+ yield blurNode(doc, "#box4-input");
+}
+
+function* testAutoFocus(doc) {
+ yield focusNode(doc, "#box4-input");
+ is(getFocusedDocument(doc), doc, "Focus is in the XUL document");
+
+ info("Test autofocus tooltip takes focus when displayed, " +
+ "and restores the focus when hidden");
+ let tooltip = yield createTooltip(doc, true);
+
+ yield showTooltip(tooltip, doc.getElementById("box1"));
+ is(getFocusedDocument(doc), tooltip.panel.ownerDocument,
"Focus is in the tooltip document");
yield hideTooltip(tooltip);
+ is(getFocusedDocument(doc), doc, "Focus is back in the XUL document");
+ ok(doc.activeElement.closest("#box4-input"), "Focus is in the #box4-input");
+
+ info("Blur the textbox before moving to the next test to reset the state.");
+ yield blurNode(doc, "#box4-input");
}
-function* testTooltipWithoutAutoFocus(doc) {
- info("Test a tooltip can be closed by clicking outside");
- let textbox = doc.querySelector("textbox");
-
- info("Focus a XUL textbox");
- let onInputFocus = once(textbox, "focus");
- EventUtils.synthesizeMouseAtCenter(textbox, {}, doc.defaultView);
- yield onInputFocus;
-
+function* testAutoFocusPreservesFocusChange(doc) {
+ yield focusNode(doc, "#box4-input");
is(getFocusedDocument(doc), doc, "Focus is in the XUL document");
- let tooltip = new HTMLTooltip({doc}, {autofocus: false});
- let tooltipNode = getTooltipContent(doc);
- yield tooltip.setContent(tooltipNode, 150, 50);
+ info("Test autofocus tooltip takes focus when displayed, " +
+ "but does not try to restore the active element if it is not focused when hidden");
+ let tooltip = yield createTooltip(doc, true);
yield showTooltip(tooltip, doc.getElementById("box1"));
+ is(getFocusedDocument(doc), tooltip.panel.ownerDocument,
+ "Focus is in the tooltip document");
+
+ info("Move the focus to #box3-input while the tooltip is displayed");
+ yield focusNode(doc, "#box3-input");
+ is(getFocusedDocument(doc), doc, "Focus is back in the XUL document");
+ ok(doc.activeElement.closest("#box3-input"), "Focus is in the #box3-input");
+
+ yield hideTooltip(tooltip);
is(getFocusedDocument(doc), doc, "Focus is still in the XUL document");
- yield hideTooltip(tooltip);
+ ok(doc.activeElement.closest("#box3-input"), "Focus is still in the #box3-input");
+
+ info("Blur the textbox before moving to the next test to reset the state.");
+ yield blurNode(doc, "#box3-input");
}
function getFocusedDocument(doc) {
let activeElement = doc.activeElement;
while (activeElement && activeElement.contentDocument) {
activeElement = activeElement.contentDocument.activeElement;
}
return activeElement.ownerDocument;
}
-function getTooltipContent(doc) {
+/**
+ * Fpcus the node corresponding to the provided selector in the provided document. Returns
+ * a promise that will resolve when receiving the focus event on the node.
+ */
+function focusNode(doc, selector) {
+ let node = doc.querySelector(selector);
+ let onFocus = once(node, "focus");
+ node.focus();
+ return onFocus;
+}
+
+/**
+ * Blur the node corresponding to the provided selector in the provided document. Returns
+ * a promise that will resolve when receiving the blur event on the node.
+ */
+function blurNode(doc, selector) {
+ let node = doc.querySelector(selector);
+ let onBlur = once(node, "blur");
+ node.blur();
+ return onBlur;
+}
+
+/**
+ * Create an HTMLTooltip instance with the provided autofocus setting.
+ *
+ * @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 div = doc.createElementNS(HTML_NS, "div");
div.style.height = "50px";
- div.style.boxSizing = "border-box";
- return div;
+ yield tooltip.setContent(div, 150, 50);
+ return tooltip;
}
--- a/devtools/client/shared/widgets/HTMLTooltip.js
+++ b/devtools/client/shared/widgets/HTMLTooltip.js
@@ -186,16 +186,17 @@ HTMLTooltip.prototype = {
if (this.type === TYPE.ARROW) {
this.arrow.style.left = computedPosition.arrowLeft + "px";
}
this.container.classList.add("tooltip-visible");
this.attachEventsTimer = this.doc.defaultView.setTimeout(() => {
+ this._focusedElement = this.doc.activeElement;
if (this.autofocus) {
this.frame.focus();
}
this.topWindow.addEventListener("click", this._onClick, true);
this.emit("shown");
}, 0);
});
},
@@ -206,16 +207,21 @@ HTMLTooltip.prototype = {
*/
hide: function () {
this.doc.defaultView.clearTimeout(this.attachEventsTimer);
if (this.isVisible()) {
this.topWindow.removeEventListener("click", this._onClick, true);
this.container.classList.remove("tooltip-visible");
this.emit("hidden");
+
+ if (this.container.contains(this.doc.activeElement) && 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 () {