mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
Bug 1266450 - part2: remove iframe container for HTML tooltip;r=bgrins
In order to have tooltips with a variable height, the tooltip container should be allowed to resize itself on the fly, which cannot be achieved with an iframe. This changeset makes the HTMLTooltip rely on a HTML container inserted in the XUL document directly. This allows to go back to a synchronous API which also simplifies the implementation. MozReview-Commit-ID: EDcsnVSKmeU --HG-- extra : rebase_source : 80a22bc558468b69ff099602ab2364a55bcdd2f7 extra : source : 1794dbc179a093b26d06eadc18086c8f138dc008
This commit is contained in:
parent
973205986c
commit
b46591f37c
@ -2704,10 +2704,10 @@ MarkupElementContainer.prototype = Heritage.extend(MarkupContainer.prototype, {
|
||||
maxDim: Services.prefs.getIntPref(PREVIEW_MAX_DIM_PREF)
|
||||
};
|
||||
|
||||
yield setImageTooltip(tooltip, this.markup.doc, data, options);
|
||||
setImageTooltip(tooltip, this.markup.doc, data, options);
|
||||
} catch (e) {
|
||||
// Indicate the failure but show the tooltip anyway.
|
||||
yield setBrokenImageTooltip(tooltip, this.markup.doc);
|
||||
setBrokenImageTooltip(tooltip, this.markup.doc);
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
|
@ -128,7 +128,6 @@ devtools.jar:
|
||||
content/shared/widgets/graphs-frame.xhtml (shared/widgets/graphs-frame.xhtml)
|
||||
content/shared/widgets/spectrum-frame.xhtml (shared/widgets/spectrum-frame.xhtml)
|
||||
content/shared/widgets/cubic-bezier-frame.xhtml (shared/widgets/cubic-bezier-frame.xhtml)
|
||||
content/shared/widgets/tooltip-frame.xhtml (shared/widgets/tooltip-frame.xhtml)
|
||||
content/shared/widgets/cubic-bezier.css (shared/widgets/cubic-bezier.css)
|
||||
content/shared/widgets/mdn-docs-frame.xhtml (shared/widgets/mdn-docs-frame.xhtml)
|
||||
content/shared/widgets/mdn-docs.css (shared/widgets/mdn-docs.css)
|
||||
|
@ -15,7 +15,9 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
|
||||
<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="box1" flex="1">
|
||||
<textbox></textbox>
|
||||
</hbox>
|
||||
<hbox id="box2" flex="1">test2</hbox>
|
||||
<hbox id="box3" flex="1">
|
||||
<textbox id="box3-input"></textbox>
|
||||
@ -31,7 +33,7 @@ loadHelperScript("helper_html_tooltip.js");
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab("about:blank");
|
||||
let [,, doc] = yield createHost("bottom", TEST_URI);
|
||||
let [, , doc] = yield createHost("bottom", TEST_URI);
|
||||
|
||||
yield testNoAutoFocus(doc);
|
||||
yield testAutoFocus(doc);
|
||||
|
@ -72,9 +72,9 @@ add_task(function* () {
|
||||
let arrow = tooltip.arrow;
|
||||
ok(arrow, "Tooltip has an arrow");
|
||||
|
||||
// Get the geometry of the anchor, the tooltip frame & arrow.
|
||||
// Get the geometry of the anchor, the tooltip panel & arrow.
|
||||
let arrowBounds = arrow.getBoxQuads({relativeTo: doc})[0].bounds;
|
||||
let frameBounds = tooltip.frame.getBoxQuads({relativeTo: doc})[0].bounds;
|
||||
let panelBounds = tooltip.panel.getBoxQuads({relativeTo: doc})[0].bounds;
|
||||
let anchorBounds = el.getBoxQuads({relativeTo: doc})[0].bounds;
|
||||
|
||||
let intersects = arrowBounds.left <= anchorBounds.right &&
|
||||
@ -84,10 +84,10 @@ add_task(function* () {
|
||||
ok(intersects || isBlockedByViewport,
|
||||
"Tooltip arrow is aligned with the anchor, or stuck on viewport's edge.");
|
||||
|
||||
let isInFrame = arrowBounds.left >= frameBounds.left &&
|
||||
arrowBounds.right <= frameBounds.right;
|
||||
ok(isInFrame,
|
||||
"The tooltip arrow remains inside the tooltip frame horizontally");
|
||||
let isInPanel = arrowBounds.left >= panelBounds.left &&
|
||||
arrowBounds.right <= panelBounds.right;
|
||||
ok(isInPanel,
|
||||
"The tooltip arrow remains inside the tooltip panel horizontally");
|
||||
|
||||
yield hideTooltip(tooltip);
|
||||
}
|
||||
|
@ -66,9 +66,9 @@ add_task(function* () {
|
||||
let arrow = tooltip.arrow;
|
||||
ok(arrow, "Tooltip has an arrow");
|
||||
|
||||
// Get the geometry of the anchor, the tooltip frame & arrow.
|
||||
// Get the geometry of the anchor, the tooltip panel & arrow.
|
||||
let arrowBounds = arrow.getBoxQuads({relativeTo: doc})[0].bounds;
|
||||
let frameBounds = tooltip.frame.getBoxQuads({relativeTo: doc})[0].bounds;
|
||||
let panelBounds = tooltip.panel.getBoxQuads({relativeTo: doc})[0].bounds;
|
||||
let anchorBounds = el.getBoxQuads({relativeTo: doc})[0].bounds;
|
||||
|
||||
let intersects = arrowBounds.left <= anchorBounds.right &&
|
||||
@ -78,10 +78,10 @@ add_task(function* () {
|
||||
ok(intersects || isBlockedByViewport,
|
||||
"Tooltip arrow is aligned with the anchor, or stuck on viewport's edge.");
|
||||
|
||||
let isInFrame = arrowBounds.left >= frameBounds.left &&
|
||||
arrowBounds.right <= frameBounds.right;
|
||||
ok(isInFrame,
|
||||
"The tooltip arrow remains inside the tooltip frame horizontally");
|
||||
let isInPanel = arrowBounds.left >= panelBounds.left &&
|
||||
arrowBounds.right <= panelBounds.right;
|
||||
ok(isInPanel,
|
||||
"The tooltip arrow remains inside the tooltip panel horizontally");
|
||||
yield hideTooltip(tooltip);
|
||||
}
|
||||
});
|
||||
|
@ -12,9 +12,6 @@ const {TooltipToggle} = require("devtools/client/shared/widgets/tooltip/TooltipT
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
const IFRAME_URL = "chrome://devtools/content/shared/widgets/tooltip-frame.xhtml";
|
||||
const IFRAME_CONTAINER_ID = "tooltip-iframe-container";
|
||||
|
||||
const POSITION = {
|
||||
TOP: "top",
|
||||
BOTTOM: "bottom",
|
||||
@ -79,51 +76,23 @@ function HTMLTooltip(toolbox,
|
||||
|
||||
this.container = this._createContainer();
|
||||
|
||||
// Promise that will resolve when the container can be filled with content.
|
||||
this.containerReady = new Promise(resolve => {
|
||||
if (this._isXUL()) {
|
||||
// In XUL context, load a placeholder document in the iframe container.
|
||||
let onLoad = () => {
|
||||
this.frame.removeEventListener("load", onLoad, true);
|
||||
resolve();
|
||||
};
|
||||
|
||||
this.frame.addEventListener("load", onLoad, true);
|
||||
this.frame.setAttribute("src", IFRAME_URL);
|
||||
this.doc.querySelector("window").appendChild(this.container);
|
||||
} else {
|
||||
// In non-XUL context the container is ready to use as is.
|
||||
this.doc.body.appendChild(this.container);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
if (this._isXUL()) {
|
||||
this.doc.querySelector("window").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;
|
||||
|
||||
HTMLTooltip.prototype = {
|
||||
/**
|
||||
* The tooltip frame is the child of the tooltip container that will only
|
||||
* contain the tooltip content (and not the arrow or any other tooltip styling
|
||||
* element).
|
||||
* In XUL contexts, this is an iframe. In non XUL contexts this is a div,
|
||||
* which also happens to be the tooltip.panel property.
|
||||
*/
|
||||
get frame() {
|
||||
return this.container.querySelector(".tooltip-panel");
|
||||
},
|
||||
|
||||
/**
|
||||
* The tooltip panel is the parentNode of the tooltip content provided in
|
||||
* setContent().
|
||||
*/
|
||||
get panel() {
|
||||
if (!this._isXUL()) {
|
||||
return this.frame;
|
||||
}
|
||||
// In XUL context, the content is wrapped in an iframe.
|
||||
let win = this.frame.contentWindow.wrappedJSObject;
|
||||
return win.document.getElementById(IFRAME_CONTAINER_ID);
|
||||
return this.container.querySelector(".tooltip-panel");
|
||||
},
|
||||
|
||||
/**
|
||||
@ -153,10 +122,8 @@ HTMLTooltip.prototype = {
|
||||
this.preferredWidth = width + themeWidth;
|
||||
this.preferredHeight = height + themeHeight;
|
||||
|
||||
return this.containerReady.then(() => {
|
||||
this.panel.innerHTML = "";
|
||||
this.panel.appendChild(content);
|
||||
});
|
||||
this.panel.innerHTML = "";
|
||||
this.panel.appendChild(content);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -172,33 +139,31 @@ HTMLTooltip.prototype = {
|
||||
* more space is available.
|
||||
*/
|
||||
show: function (anchor, {position} = {}) {
|
||||
this.containerReady.then(() => {
|
||||
let computedPosition = this._findBestPosition(anchor, position);
|
||||
let computedPosition = this._findBestPosition(anchor, position);
|
||||
|
||||
let isTop = computedPosition.position === POSITION.TOP;
|
||||
this.container.classList.toggle("tooltip-top", isTop);
|
||||
this.container.classList.toggle("tooltip-bottom", !isTop);
|
||||
let isTop = computedPosition.position === POSITION.TOP;
|
||||
this.container.classList.toggle("tooltip-top", isTop);
|
||||
this.container.classList.toggle("tooltip-bottom", !isTop);
|
||||
|
||||
this.container.style.width = computedPosition.width + "px";
|
||||
this.container.style.height = computedPosition.height + "px";
|
||||
this.container.style.top = computedPosition.top + "px";
|
||||
this.container.style.left = computedPosition.left + "px";
|
||||
this.container.style.width = computedPosition.width + "px";
|
||||
this.container.style.height = computedPosition.height + "px";
|
||||
this.container.style.top = computedPosition.top + "px";
|
||||
this.container.style.left = computedPosition.left + "px";
|
||||
|
||||
if (this.type === TYPE.ARROW) {
|
||||
this.arrow.style.left = computedPosition.arrowLeft + "px";
|
||||
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.panel.focus();
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
this.topWindow.addEventListener("click", this._onClick, true);
|
||||
this.emit("shown");
|
||||
}, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -242,12 +207,7 @@ HTMLTooltip.prototype = {
|
||||
container.setAttribute("type", this.type);
|
||||
container.classList.add("tooltip-container");
|
||||
|
||||
let html;
|
||||
if (this._isXUL()) {
|
||||
html = '<iframe class="devtools-tooltip-iframe tooltip-panel"></iframe>';
|
||||
} else {
|
||||
html = '<div class="tooltip-panel"></div>';
|
||||
}
|
||||
let html = '<div class="tooltip-panel"></div>';
|
||||
|
||||
if (this.type === TYPE.ARROW) {
|
||||
html += '<div class="tooltip-arrow"></div>';
|
||||
|
@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- 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/. -->
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"/>
|
||||
<style>
|
||||
html, body, #tooltip-iframe-container {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
color: var(--theme-body-color);
|
||||
}
|
||||
|
||||
:root[platform="linux"] body {
|
||||
font-size: 80%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body role="application">
|
||||
<div id="tooltip-iframe-container"></div>
|
||||
</body>
|
||||
</html>
|
@ -62,8 +62,6 @@ function getImageDimensions(doc, imageUrl) {
|
||||
* - {Number} naturalHeight mandatory, height of the image to display
|
||||
* - {Number} maxDim optional, max width/height of the preview
|
||||
* - {Boolean} hideDimensionLabel optional, pass true to hide the label
|
||||
* @return {Promise} promise that will resolve when the tooltip content has been
|
||||
* set
|
||||
*/
|
||||
function setImageTooltip(tooltip, doc, imageUrl, options) {
|
||||
let {naturalWidth, naturalHeight, hideDimensionLabel, maxDim} = options;
|
||||
@ -113,7 +111,7 @@ function setImageTooltip(tooltip, doc, imageUrl, options) {
|
||||
}
|
||||
let width = Math.max(CONTAINER_MIN_WIDTH, imgWidth + 2 * IMAGE_PADDING);
|
||||
|
||||
return tooltip.setContent(div, width, height);
|
||||
tooltip.setContent(div, width, height);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -124,8 +122,6 @@ function setImageTooltip(tooltip, doc, imageUrl, options) {
|
||||
* The tooltip instance on which the image preview content should be set
|
||||
* @param {Document} doc
|
||||
* A document element to create the HTML elements needed for the tooltip
|
||||
* @return {Promise} promise that will resolve when the tooltip content has been
|
||||
* set
|
||||
*/
|
||||
function setBrokenImageTooltip(tooltip, doc) {
|
||||
let div = doc.createElementNS(XHTML_NS, "div");
|
||||
@ -137,7 +133,7 @@ function setBrokenImageTooltip(tooltip, doc) {
|
||||
|
||||
let message = GetStringFromName("previewTooltip.image.brokenImage");
|
||||
div.textContent = message;
|
||||
return tooltip.setContent(div, 150, 30);
|
||||
tooltip.setContent(div, 150, 30);
|
||||
}
|
||||
|
||||
module.exports.getImageDimensions = getImageDimensions;
|
||||
|
Loading…
Reference in New Issue
Block a user