ImageTooltipHelper.js (5435B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { LocalizationHelper } = require("resource://devtools/shared/l10n.js"); 8 const L10N = new LocalizationHelper( 9 "devtools/client/locales/inspector.properties" 10 ); 11 12 const XHTML_NS = "http://www.w3.org/1999/xhtml"; 13 14 // Default image tooltip max dimension 15 const MAX_DIMENSION = 200; 16 const CONTAINER_MIN_WIDTH = 100; 17 // Should remain synchronized with tooltips.css --image-tooltip-image-padding 18 const IMAGE_PADDING = 4; 19 // Should remain synchronized with tooltips.css --image-tooltip-label-height 20 const LABEL_HEIGHT = 20; 21 22 /** 23 * Image preview tooltips should be provided with the naturalHeight and 24 * naturalWidth value for the image to display. This helper loads the provided 25 * image URL in an image object in order to retrieve the image dimensions after 26 * the load. 27 * 28 * @param {Document} doc the document element to use to create the image object 29 * @param {string} imageUrl the url of the image to measure 30 * @return {Promise} returns a promise that will resolve after the iamge load: 31 * - {Number} naturalWidth natural width of the loaded image 32 * - {Number} naturalHeight natural height of the loaded image 33 */ 34 function getImageDimensions(doc, imageUrl) { 35 return new Promise(resolve => { 36 const imgObj = new doc.defaultView.Image(); 37 imgObj.onload = () => { 38 imgObj.onload = null; 39 const { naturalWidth, naturalHeight } = imgObj; 40 resolve({ naturalWidth, naturalHeight }); 41 }; 42 imgObj.src = imageUrl; 43 }); 44 } 45 46 /** 47 * Set the tooltip content of a provided HTMLTooltip instance to display an 48 * image preview matching the provided imageUrl. 49 * 50 * @param {HTMLTooltip} tooltip 51 * The tooltip instance on which the image preview content should be set 52 * @param {Document} doc 53 * A document element to create the HTML elements needed for the tooltip 54 * @param {string} imageUrl 55 * Absolute URL of the image to display in the tooltip 56 * @param {object} options 57 * - {Number} naturalWidth mandatory, width of the image to display 58 * - {Number} naturalHeight mandatory, height of the image to display 59 * - {Number} maxDim optional, max width/height of the preview 60 * - {Boolean} hideDimensionLabel optional, pass true to hide the label 61 * - {Boolean} hideCheckeredBackground optional, pass true to hide 62 the checkered background 63 */ 64 function setImageTooltip(tooltip, doc, imageUrl, options) { 65 let { 66 naturalWidth, 67 naturalHeight, 68 hideDimensionLabel, 69 hideCheckeredBackground, 70 maxDim, 71 } = options; 72 maxDim = maxDim || MAX_DIMENSION; 73 74 let imgHeight = naturalHeight; 75 let imgWidth = naturalWidth; 76 if (imgHeight > maxDim || imgWidth > maxDim) { 77 const scale = maxDim / Math.max(imgHeight, imgWidth); 78 // Only allow integer values to avoid rounding errors. 79 imgHeight = Math.floor(scale * naturalHeight); 80 imgWidth = Math.ceil(scale * naturalWidth); 81 } 82 83 // Create tooltip content 84 const container = doc.createElementNS(XHTML_NS, "div"); 85 container.classList.add("devtools-tooltip-image-container"); 86 87 const wrapper = doc.createElementNS(XHTML_NS, "div"); 88 wrapper.classList.add("devtools-tooltip-image-wrapper"); 89 container.appendChild(wrapper); 90 91 const img = doc.createElementNS(XHTML_NS, "img"); 92 img.classList.add("devtools-tooltip-image"); 93 img.classList.toggle( 94 "devtools-checkered-background", 95 !hideCheckeredBackground 96 ); 97 img.style.height = imgHeight; 98 img.src = encodeURI(imageUrl); 99 wrapper.appendChild(img); 100 101 if (!hideDimensionLabel) { 102 const dimensions = doc.createElementNS(XHTML_NS, "div"); 103 dimensions.classList.add("devtools-tooltip-image-dimensions"); 104 container.appendChild(dimensions); 105 106 const label = naturalWidth + " \u00D7 " + naturalHeight; 107 const span = doc.createElementNS(XHTML_NS, "span"); 108 span.classList.add("devtools-tooltip-caption"); 109 span.textContent = label; 110 dimensions.appendChild(span); 111 } 112 113 tooltip.panel.innerHTML = ""; 114 tooltip.panel.appendChild(container); 115 116 // Calculate tooltip dimensions 117 const width = Math.max(CONTAINER_MIN_WIDTH, imgWidth + 2 * IMAGE_PADDING); 118 let height = imgHeight + 2 * IMAGE_PADDING; 119 if (!hideDimensionLabel) { 120 height += parseFloat(LABEL_HEIGHT); 121 } 122 123 tooltip.setContentSize({ width, height }); 124 } 125 126 /** 127 * Set the tooltip content of a provided HTMLTooltip instance to display a 128 * fallback error message when an image preview tooltip can not be displayed. 129 * 130 * @param {HTMLTooltip} tooltip 131 * The tooltip instance on which the image preview content should be set 132 * @param {Document} doc 133 * A document element to create the HTML elements needed for the tooltip 134 */ 135 function setBrokenImageTooltip(tooltip, doc) { 136 const div = doc.createElementNS(XHTML_NS, "div"); 137 div.className = "devtools-tooltip-image-broken"; 138 const message = L10N.getStr("previewTooltip.image.brokenImage"); 139 div.textContent = message; 140 141 tooltip.panel.innerHTML = ""; 142 tooltip.panel.appendChild(div); 143 tooltip.setContentSize({ width: "auto", height: "auto" }); 144 } 145 146 module.exports.getImageDimensions = getImageDimensions; 147 module.exports.setImageTooltip = setImageTooltip; 148 module.exports.setBrokenImageTooltip = setBrokenImageTooltip;