/* 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";
/** * The tooltip overlays are tooltips that appear when hovering over property values and * editor tooltips that appear when clicking swatch based editors.
*/
/** * Manages all tooltips in the style-inspector. * * @param {CssRuleView|CssComputedView} view * Either the rule-view or computed-view panel
*/ function TooltipsOverlay(view) { this.view = view; this._instances = new Map();
TooltipsOverlay.prototype = {
get isEditing() { for (const [, tooltip] of this._instances) { if (typeof tooltip.isEditing == "function" && tooltip.isEditing()) { returntrue;
}
} returnfalse;
},
/** * Add the tooltips overlay to the view. This will start tracking mouse * movements and display tooltips when needed
*/
addToView() { if (this._isStarted || this._isDestroyed) { return;
}
this._isStarted = true;
this.inactiveCssTooltipHelper = new InactiveCssTooltipHelper(); this.compatibilityTooltipHelper = new CssCompatibilityTooltipHelper(); this.cssQueryContainerTooltipHelper = new CssQueryContainerTooltipHelper(); this.cssSelectorWarningsTooltipHelper = new CssSelectorWarningsTooltipHelper();
// Instantiate the interactiveTooltip and preview tooltip when the // rule/computed view is hovered over in order to call // `tooltip.startTogglingOnHover`. This will allow the tooltip to be shown // when an appropriate element is hovered over. for (const type of ["interactiveTooltip", "previewTooltip"]) { if (flags.testing) { this.getTooltip(type);
} else { // Lazily get the preview tooltip to avoid loading HTMLTooltip. this.view.element.addEventListener( "mousemove",
() => { this.getTooltip(type);
},
{ once: true }
);
}
}
},
/** * Lazily fetch and initialize the different tooltips that are used in the inspector. * These tooltips are attached to the toolbox document if they require a popup panel. * Otherwise, it is attached to the inspector panel document if it is an inline editor. * * @param {String} name * Identifier name for the tooltip
*/
getTooltip(name) {
let tooltip = this._instances.get(name); if (tooltip) { return tooltip;
} const { doc } = this.view.inspector.toolbox; switch (name) { case"colorPicker": const SwatchColorPickerTooltip = require("resource://devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js");
tooltip = new SwatchColorPickerTooltip(doc, this.view.inspector); break; case"cubicBezier": const SwatchCubicBezierTooltip = require("resource://devtools/client/shared/widgets/tooltip/SwatchCubicBezierTooltip.js");
tooltip = new SwatchCubicBezierTooltip(doc); break; case"linearEaseFunction": const SwatchLinearEasingFunctionTooltip = require("devtools/client/shared/widgets/tooltip/SwatchLinearEasingFunctionTooltip");
tooltip = new SwatchLinearEasingFunctionTooltip(doc); break; case"filterEditor": const SwatchFilterTooltip = require("resource://devtools/client/shared/widgets/tooltip/SwatchFilterTooltip.js");
tooltip = new SwatchFilterTooltip(doc); break; case"interactiveTooltip":
tooltip = new HTMLTooltip(doc, {
type: "doorhanger",
useXulWrapper: true,
noAutoHide: true,
});
tooltip.startTogglingOnHover( this.view.element, this.onInteractiveTooltipTargetHover.bind(this),
{
interactive: true,
}
); break; case"previewTooltip":
tooltip = new HTMLTooltip(doc, {
type: "arrow",
useXulWrapper: true,
});
tooltip.startTogglingOnHover( this.view.element, this._onPreviewTooltipTargetHover.bind(this)
); break; default: thrownew Error(`Unsupported tooltip '${name}'`);
} this._instances.set(name, tooltip); return tooltip;
},
/** * Remove the tooltips overlay from the view. This will stop tracking mouse * movements and displaying tooltips
*/
removeFromView() { if (!this._isStarted || this._isDestroyed) { return;
}
for (const [, tooltip] of this._instances) {
tooltip.destroy();
}
/** * Given a hovered node info, find out which type of tooltip should be shown, * if any * * @param {Object} nodeInfo * @return {String} The tooltip type to be shown, or null
*/
_getTooltipType({ type, value: prop }) {
let tooltipType = null;
// Container info tooltip if (type === VIEW_NODE_CSS_QUERY_CONTAINER) {
tooltipType = TOOLTIP_CSS_QUERY_CONTAINER;
}
// Selector warnings info tooltip if (type === VIEW_NODE_CSS_SELECTOR_WARNINGS) {
tooltipType = TOOLTIP_CSS_SELECTOR_WARNINGS;
}
return tooltipType;
},
_removePreviousInstances() { for (const tooltip of this._instances.values()) { if (tooltip.isVisible()) { if (tooltip.revert) {
tooltip.revert();
}
tooltip.hide();
}
}
},
/** * Executed by the tooltip when the pointer hovers over an element of the * view. Used to decide whether the tooltip should be shown or not and to * actually put content in it. * Checks if the hovered target is a css value we support tooltips for. * * @param {DOMNode} target The currently hovered node * @return {Promise}
*/
async _onPreviewTooltipTargetHover(target) { const nodeInfo = this.view.getNodeInfo(target); if (!nodeInfo) { // The hovered node isn't something we care about returnfalse;
}
const type = this._getTooltipType(nodeInfo); if (!type) { // There is no tooltip type defined for the hovered node returnfalse;
}
if (type === TOOLTIP_FONTFAMILY_TYPE) { const font = nodeInfo.value.value; const nodeFront = inspector.selection.nodeFront;
await this._setFontPreviewTooltip(font, nodeFront);
this.sendOpenScalarToTelemetry(type);
if (nodeInfo.type === VIEW_NODE_FONT_TYPE) { // If the hovered element is on the font family span, anchor // the tooltip on the whole property value instead. return target.parentNode;
} returntrue;
}
/** * Executed by the tooltip when the pointer hovers over an element of the * view. Used to decide whether the tooltip should be shown or not and to * actually put content in it. * Checks if the hovered target is a css value we support tooltips for. * * @param {DOMNode} target * The currently hovered node * @return {Boolean} * true if shown, false otherwise.
*/
async onInteractiveTooltipTargetHover(target) { if (target.classList.contains("ruleview-compatibility-warning")) { const nodeCompatibilityInfo =
await this.view.getNodeCompatibilityInfo(target);
const nodeInfo = this.view.getNodeInfo(target); if (!nodeInfo) { // The hovered node isn't something we care about. returnfalse;
}
const type = this._getTooltipType(nodeInfo); if (!type) { // There is no tooltip type defined for the hovered node. returnfalse;
}
this._removePreviousInstances();
if (type === TOOLTIP_INACTIVE_CSS) { // Ensure this is the correct node and not a parent. if (!target.classList.contains("ruleview-unused-warning")) { returnfalse;
}
if (type === TOOLTIP_CSS_QUERY_CONTAINER) { // Ensure this is the correct node and not a parent. if (!target.closest(".container-query .container-query-declaration")) { returnfalse;
}
if (type === TOOLTIP_CSS_SELECTOR_WARNINGS) {
await this.cssSelectorWarningsTooltipHelper.setContent(
nodeInfo.value, this.getTooltip("interactiveTooltip")
);
this.sendOpenScalarToTelemetry(type);
returntrue;
}
returnfalse;
},
/** * Send a telemetry Scalar showing that a tooltip of `type` has been opened. * * @param {String} type * The node type from `devtools/client/inspector/shared/node-types` or the Tooltip type.
*/
sendOpenScalarToTelemetry(type) {
Glean.devtoolsTooltip.shown[type].add(1);
},
/** * Set the content of the preview tooltip to display an image preview. The image URL can * be relative, a call will be made to the debuggee to retrieve the image content as an * imageData URI. * * @param {String} imageUrl * The image url value (may be relative or absolute). * @return {Promise} A promise that resolves when the preview tooltip content is ready
*/
async _setImagePreviewTooltip(imageUrl) { const doc = this.view.inspector.panelDoc; const maxDim = Services.prefs.getIntPref(PREF_IMAGE_TOOLTIP_SIZE);
let naturalWidth, naturalHeight; if (imageUrl.startsWith("data:")) { // If the imageUrl already is a data-url, save ourselves a round-trip const size = await getImageDimensions(doc, imageUrl);
naturalWidth = size.naturalWidth;
naturalHeight = size.naturalHeight;
} else { const inspectorFront = this.view.inspector.inspectorFront; const { data, size } = await inspectorFront.getImageDataFromURL(
imageUrl,
maxDim
);
imageUrl = await data.string();
naturalWidth = size.naturalWidth;
naturalHeight = size.naturalHeight;
}
/** * Set the content of the preview tooltip to display a font family preview. * * @param {String} font * The font family value. * @param {object} nodeFront * The NodeActor that will used to retrieve the dataURL for the font * family tooltip contents. * @return {Promise} A promise that resolves when the preview tooltip content is ready
*/
async _setFontPreviewTooltip(font, nodeFront) { if (
!font ||
!nodeFront || typeof nodeFront.getFontFamilyDataURL !== "function"
) { thrownew Error("Unable to create font preview tooltip content.");
}
font = font.replace(/"/g, "'");
font = font.replace("!important", "");
font = font.trim();
/** * Set the content of the preview tooltip to display a variable preview. * * @param {Object} tooltipParams * See VariableTooltipHelper#setVariableTooltip `params`. * @return {Promise} A promise that resolves when the preview tooltip content is ready
*/
async _setVariablePreviewTooltip(tooltipParams) { const doc = this.view.inspector.panelDoc;
await setVariableTooltip( this.getTooltip("previewTooltip"),
doc,
tooltipParams
);
},
_onNewSelection() { for (const [, tooltip] of this._instances) {
tooltip.hide();
}
},
/** * Destroy this overlay instance, removing it from the view
*/
destroy() { this.removeFromView();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.