summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/widgets/tooltip/ImageTooltipHelper.js
blob: c73bd4b6b6d48a147ab6c40c0843762d668fcca2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* 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";

const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
const L10N = new LocalizationHelper(
  "devtools/client/locales/inspector.properties"
);

const XHTML_NS = "http://www.w3.org/1999/xhtml";

// Default image tooltip max dimension
const MAX_DIMENSION = 200;
const CONTAINER_MIN_WIDTH = 100;
// Should remain synchronized with tooltips.css --image-tooltip-image-padding
const IMAGE_PADDING = 4;
// Should remain synchronized with tooltips.css --image-tooltip-label-height
const LABEL_HEIGHT = 20;

/**
 * Image preview tooltips should be provided with the naturalHeight and
 * naturalWidth value for the image to display. This helper loads the provided
 * image URL in an image object in order to retrieve the image dimensions after
 * the load.
 *
 * @param {Document} doc the document element to use to create the image object
 * @param {String} imageUrl the url of the image to measure
 * @return {Promise} returns a promise that will resolve after the iamge load:
 *         - {Number} naturalWidth natural width of the loaded image
 *         - {Number} naturalHeight natural height of the loaded image
 */
function getImageDimensions(doc, imageUrl) {
  return new Promise(resolve => {
    const imgObj = new doc.defaultView.Image();
    imgObj.onload = () => {
      imgObj.onload = null;
      const { naturalWidth, naturalHeight } = imgObj;
      resolve({ naturalWidth, naturalHeight });
    };
    imgObj.src = imageUrl;
  });
}

/**
 * Set the tooltip content of a provided HTMLTooltip instance to display an
 * image preview matching the provided imageUrl.
 *
 * @param {HTMLTooltip} tooltip
 *        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
 * @param {String} imageUrl
 *        Absolute URL of the image to display in the tooltip
 * @param {Object} options
 *        - {Number} naturalWidth mandatory, width of the image to display
 *        - {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
 *        - {Boolean} hideCheckeredBackground optional, pass true to hide
                      the checkered background
 */
function setImageTooltip(tooltip, doc, imageUrl, options) {
  let {
    naturalWidth,
    naturalHeight,
    hideDimensionLabel,
    hideCheckeredBackground,
    maxDim,
  } = options;
  maxDim = maxDim || MAX_DIMENSION;

  let imgHeight = naturalHeight;
  let imgWidth = naturalWidth;
  if (imgHeight > maxDim || imgWidth > maxDim) {
    const scale = maxDim / Math.max(imgHeight, imgWidth);
    // Only allow integer values to avoid rounding errors.
    imgHeight = Math.floor(scale * naturalHeight);
    imgWidth = Math.ceil(scale * naturalWidth);
  }

  // Create tooltip content
  const container = doc.createElementNS(XHTML_NS, "div");
  container.classList.add("devtools-tooltip-image-container");

  const wrapper = doc.createElementNS(XHTML_NS, "div");
  wrapper.classList.add("devtools-tooltip-image-wrapper");
  container.appendChild(wrapper);

  const img = doc.createElementNS(XHTML_NS, "img");
  img.classList.add("devtools-tooltip-image");
  img.classList.toggle("devtools-tooltip-tiles", !hideCheckeredBackground);
  img.style.height = imgHeight;
  img.src = encodeURI(imageUrl);
  wrapper.appendChild(img);

  if (!hideDimensionLabel) {
    const dimensions = doc.createElementNS(XHTML_NS, "div");
    dimensions.classList.add("devtools-tooltip-image-dimensions");
    container.appendChild(dimensions);

    const label = naturalWidth + " \u00D7 " + naturalHeight;
    const span = doc.createElementNS(XHTML_NS, "span");
    span.classList.add("devtools-tooltip-caption");
    span.textContent = label;
    dimensions.appendChild(span);
  }

  tooltip.panel.innerHTML = "";
  tooltip.panel.appendChild(container);

  // Calculate tooltip dimensions
  const width = Math.max(CONTAINER_MIN_WIDTH, imgWidth + 2 * IMAGE_PADDING);
  let height = imgHeight + 2 * IMAGE_PADDING;
  if (!hideDimensionLabel) {
    height += parseFloat(LABEL_HEIGHT);
  }

  tooltip.setContentSize({ width, height });
}

/*
 * Set the tooltip content of a provided HTMLTooltip instance to display a
 * fallback error message when an image preview tooltip can not be displayed.
 *
 * @param {HTMLTooltip} tooltip
 *        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
 */
function setBrokenImageTooltip(tooltip, doc) {
  const div = doc.createElementNS(XHTML_NS, "div");
  div.className = "devtools-tooltip-image-broken";
  const message = L10N.getStr("previewTooltip.image.brokenImage");
  div.textContent = message;

  tooltip.panel.innerHTML = "";
  tooltip.panel.appendChild(div);
  tooltip.setContentSize({ width: "auto", height: "auto" });
}

module.exports.getImageDimensions = getImageDimensions;
module.exports.setImageTooltip = setImageTooltip;
module.exports.setBrokenImageTooltip = setBrokenImageTooltip;