forked from mirrors/gecko-dev
Added flag hideCheckeredBackground to hide checkered background if pass true. MozReview-Commit-ID: FOQO59xqGvt --HG-- extra : rebase_source : 839fdba629d3eac77ea37509bb049a233c72a163
322 lines
10 KiB
JavaScript
322 lines
10 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
/* 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.
|
|
*/
|
|
|
|
const { Task } = require("devtools/shared/task");
|
|
const Services = require("Services");
|
|
const {
|
|
VIEW_NODE_VALUE_TYPE,
|
|
VIEW_NODE_IMAGE_URL_TYPE,
|
|
} = require("devtools/client/inspector/shared/node-types");
|
|
const { getColor } = require("devtools/client/shared/theme");
|
|
const { getCssProperties } = require("devtools/shared/fronts/css-properties");
|
|
const CssDocsTooltip = require("devtools/client/shared/widgets/tooltip/CssDocsTooltip");
|
|
const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
|
|
const {
|
|
getImageDimensions,
|
|
setImageTooltip,
|
|
setBrokenImageTooltip,
|
|
} = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
|
|
const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
|
|
const SwatchCubicBezierTooltip = require("devtools/client/shared/widgets/tooltip/SwatchCubicBezierTooltip");
|
|
const SwatchFilterTooltip = require("devtools/client/shared/widgets/tooltip/SwatchFilterTooltip");
|
|
|
|
const PREF_IMAGE_TOOLTIP_SIZE = "devtools.inspector.imagePreviewTooltipSize";
|
|
|
|
// Types of existing tooltips
|
|
const TOOLTIP_IMAGE_TYPE = "image";
|
|
const TOOLTIP_FONTFAMILY_TYPE = "font-family";
|
|
|
|
/**
|
|
* 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;
|
|
|
|
let {CssRuleView} = require("devtools/client/inspector/rules/rules");
|
|
this.isRuleView = view instanceof CssRuleView;
|
|
this._cssProperties = getCssProperties(this.view.inspector.toolbox);
|
|
|
|
this._onNewSelection = this._onNewSelection.bind(this);
|
|
this.view.inspector.selection.on("new-node-front", this._onNewSelection);
|
|
}
|
|
|
|
TooltipsOverlay.prototype = {
|
|
get isEditing() {
|
|
return this.colorPicker.tooltip.isVisible() ||
|
|
this.colorPicker.eyedropperOpen ||
|
|
this.cubicBezier.tooltip.isVisible() ||
|
|
this.filterEditor.tooltip.isVisible();
|
|
},
|
|
|
|
/**
|
|
* Add the tooltips overlay to the view. This will start tracking mouse
|
|
* movements and display tooltips when needed
|
|
*/
|
|
addToView: function () {
|
|
if (this._isStarted || this._isDestroyed) {
|
|
return;
|
|
}
|
|
|
|
let { toolbox } = this.view.inspector;
|
|
|
|
// Initializing 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.
|
|
this.previewTooltip = new HTMLTooltip(toolbox.doc, {
|
|
type: "arrow",
|
|
useXulWrapper: true
|
|
});
|
|
this.previewTooltip.startTogglingOnHover(this.view.element,
|
|
this._onPreviewTooltipTargetHover.bind(this));
|
|
|
|
// MDN CSS help tooltip
|
|
this.cssDocs = new CssDocsTooltip(toolbox.doc);
|
|
|
|
if (this.isRuleView) {
|
|
// Color picker tooltip
|
|
this.colorPicker = new SwatchColorPickerTooltip(toolbox.doc,
|
|
this.view.inspector,
|
|
this._cssProperties);
|
|
// Cubic bezier tooltip
|
|
this.cubicBezier = new SwatchCubicBezierTooltip(toolbox.doc);
|
|
// Filter editor tooltip
|
|
this.filterEditor = new SwatchFilterTooltip(toolbox.doc,
|
|
this._cssProperties.getValidityChecker(this.view.inspector.panelDoc));
|
|
}
|
|
|
|
this._isStarted = true;
|
|
},
|
|
|
|
/**
|
|
* Remove the tooltips overlay from the view. This will stop tracking mouse
|
|
* movements and displaying tooltips
|
|
*/
|
|
removeFromView: function () {
|
|
if (!this._isStarted || this._isDestroyed) {
|
|
return;
|
|
}
|
|
|
|
this.previewTooltip.stopTogglingOnHover(this.view.element);
|
|
this.previewTooltip.destroy();
|
|
|
|
if (this.colorPicker) {
|
|
this.colorPicker.destroy();
|
|
}
|
|
|
|
if (this.cubicBezier) {
|
|
this.cubicBezier.destroy();
|
|
}
|
|
|
|
if (this.cssDocs) {
|
|
this.cssDocs.destroy();
|
|
}
|
|
|
|
if (this.filterEditor) {
|
|
this.filterEditor.destroy();
|
|
}
|
|
|
|
this._isStarted = false;
|
|
},
|
|
|
|
/**
|
|
* 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: function ({type, value: prop}) {
|
|
let tooltipType = null;
|
|
let inspector = this.view.inspector;
|
|
|
|
// Image preview tooltip
|
|
if (type === VIEW_NODE_IMAGE_URL_TYPE &&
|
|
inspector.hasUrlToImageDataResolver) {
|
|
tooltipType = TOOLTIP_IMAGE_TYPE;
|
|
}
|
|
|
|
// Font preview tooltip
|
|
if (type === VIEW_NODE_VALUE_TYPE && prop.property === "font-family") {
|
|
let value = prop.value.toLowerCase();
|
|
if (value !== "inherit" && value !== "unset" && value !== "initial") {
|
|
tooltipType = TOOLTIP_FONTFAMILY_TYPE;
|
|
}
|
|
}
|
|
|
|
return tooltipType;
|
|
},
|
|
|
|
/**
|
|
* 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}
|
|
*/
|
|
_onPreviewTooltipTargetHover: Task.async(function* (target) {
|
|
let nodeInfo = this.view.getNodeInfo(target);
|
|
if (!nodeInfo) {
|
|
// The hovered node isn't something we care about
|
|
return false;
|
|
}
|
|
|
|
let type = this._getTooltipType(nodeInfo);
|
|
if (!type) {
|
|
// There is no tooltip type defined for the hovered node
|
|
return false;
|
|
}
|
|
|
|
if (this.isRuleView && this.colorPicker.tooltip.isVisible()) {
|
|
this.colorPicker.revert();
|
|
this.colorPicker.hide();
|
|
}
|
|
|
|
if (this.isRuleView && this.cubicBezier.tooltip.isVisible()) {
|
|
this.cubicBezier.revert();
|
|
this.cubicBezier.hide();
|
|
}
|
|
|
|
if (this.isRuleView && this.cssDocs.tooltip.isVisible()) {
|
|
this.cssDocs.hide();
|
|
}
|
|
|
|
if (this.isRuleView && this.filterEditor.tooltip.isVisible()) {
|
|
this.filterEditor.revert();
|
|
this.filterEdtior.hide();
|
|
}
|
|
|
|
let inspector = this.view.inspector;
|
|
|
|
if (type === TOOLTIP_IMAGE_TYPE) {
|
|
try {
|
|
yield this._setImagePreviewTooltip(nodeInfo.value.url);
|
|
} catch (e) {
|
|
yield setBrokenImageTooltip(this.previewTooltip, this.view.inspector.panelDoc);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (type === TOOLTIP_FONTFAMILY_TYPE) {
|
|
let font = nodeInfo.value.value;
|
|
let nodeFront = inspector.selection.nodeFront;
|
|
yield this._setFontPreviewTooltip(font, nodeFront);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}),
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
_setImagePreviewTooltip: Task.async(function* (imageUrl) {
|
|
let doc = this.view.inspector.panelDoc;
|
|
let 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
|
|
let size = yield getImageDimensions(doc, imageUrl);
|
|
naturalWidth = size.naturalWidth;
|
|
naturalHeight = size.naturalHeight;
|
|
} else {
|
|
let inspectorFront = this.view.inspector.inspector;
|
|
let {data, size} = yield inspectorFront.getImageDataFromURL(imageUrl, maxDim);
|
|
imageUrl = yield data.string();
|
|
naturalWidth = size.naturalWidth;
|
|
naturalHeight = size.naturalHeight;
|
|
}
|
|
|
|
yield setImageTooltip(this.previewTooltip, doc, imageUrl,
|
|
{maxDim, naturalWidth, 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
|
|
*/
|
|
_setFontPreviewTooltip: Task.async(function* (font, nodeFront) {
|
|
if (!font || !nodeFront || typeof nodeFront.getFontFamilyDataURL !== "function") {
|
|
throw new Error("Unable to create font preview tooltip content.");
|
|
}
|
|
|
|
font = font.replace(/"/g, "'");
|
|
font = font.replace("!important", "");
|
|
font = font.trim();
|
|
|
|
let fillStyle = getColor("body-color");
|
|
let {data, size: maxDim} = yield nodeFront.getFontFamilyDataURL(font, fillStyle);
|
|
|
|
let imageUrl = yield data.string();
|
|
let doc = this.view.inspector.panelDoc;
|
|
let {naturalWidth, naturalHeight} = yield getImageDimensions(doc, imageUrl);
|
|
|
|
yield setImageTooltip(this.previewTooltip, doc, imageUrl,
|
|
{hideDimensionLabel: true, hideCheckeredBackground: true,
|
|
maxDim, naturalWidth, naturalHeight});
|
|
}),
|
|
|
|
_onNewSelection: function () {
|
|
if (this.previewTooltip) {
|
|
this.previewTooltip.hide();
|
|
}
|
|
|
|
if (this.colorPicker) {
|
|
this.colorPicker.hide();
|
|
}
|
|
|
|
if (this.cubicBezier) {
|
|
this.cubicBezier.hide();
|
|
}
|
|
|
|
if (this.cssDocs) {
|
|
this.cssDocs.hide();
|
|
}
|
|
|
|
if (this.filterEditor) {
|
|
this.filterEditor.hide();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Destroy this overlay instance, removing it from the view
|
|
*/
|
|
destroy: function () {
|
|
this.removeFromView();
|
|
|
|
this.view.inspector.selection.off("new-node-front", this._onNewSelection);
|
|
this.view = null;
|
|
|
|
this._isDestroyed = true;
|
|
}
|
|
};
|
|
|
|
module.exports = TooltipsOverlay;
|