forked from mirrors/gecko-dev
904 lines
28 KiB
JavaScript
904 lines
28 KiB
JavaScript
/* 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/. */
|
|
|
|
/* globals browser, log, util, catcher, inlineSelectionCss, callBackground, assertIsTrusted, assertIsBlankDocument, blobConverters */
|
|
|
|
"use strict";
|
|
|
|
this.ui = (function () {
|
|
// eslint-disable-line no-unused-vars
|
|
const exports = {};
|
|
const SAVE_BUTTON_HEIGHT = 50;
|
|
|
|
const { watchFunction } = catcher;
|
|
|
|
exports.isHeader = function (el) {
|
|
while (el) {
|
|
if (
|
|
el.classList &&
|
|
(el.classList.contains("visible") ||
|
|
el.classList.contains("full-page") ||
|
|
el.classList.contains("cancel-shot"))
|
|
) {
|
|
return true;
|
|
}
|
|
el = el.parentNode;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const substitutedCss = inlineSelectionCss.replace(
|
|
/MOZ_EXTENSION([^"]+)/g,
|
|
(match, filename) => {
|
|
return browser.runtime.getURL(filename);
|
|
}
|
|
);
|
|
|
|
function makeEl(tagName, className) {
|
|
if (!iframe.document()) {
|
|
throw new Error("Attempted makeEl before iframe was initialized");
|
|
}
|
|
const el = iframe.document().createElement(tagName);
|
|
if (className) {
|
|
el.className = className;
|
|
}
|
|
return el;
|
|
}
|
|
|
|
function onResize() {
|
|
if (this.sizeTracking.windowDelayer) {
|
|
clearTimeout(this.sizeTracking.windowDelayer);
|
|
}
|
|
this.sizeTracking.windowDelayer = setTimeout(
|
|
watchFunction(() => {
|
|
this.updateElementSize(true);
|
|
}),
|
|
50
|
|
);
|
|
}
|
|
|
|
function initializeIframe() {
|
|
const el = document.createElement("iframe");
|
|
el.src = browser.runtime.getURL("blank.html");
|
|
el.style.zIndex = "99999999999";
|
|
el.style.border = "none";
|
|
el.style.top = "0";
|
|
el.style.left = "0";
|
|
el.style.margin = "0";
|
|
el.scrolling = "no";
|
|
el.style.clip = "auto";
|
|
el.style.backgroundColor = "transparent";
|
|
el.style.colorScheme = "light";
|
|
return el;
|
|
}
|
|
|
|
const iframeSelection = (exports.iframeSelection = {
|
|
element: null,
|
|
addClassName: "",
|
|
sizeTracking: {
|
|
timer: null,
|
|
windowDelayer: null,
|
|
lastHeight: null,
|
|
lastWidth: null,
|
|
},
|
|
document: null,
|
|
window: null,
|
|
display(installHandlerOnDocument) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!this.element) {
|
|
this.element = initializeIframe();
|
|
this.element.id = "firefox-screenshots-selection-iframe";
|
|
this.element.style.display = "none";
|
|
this.element.style.setProperty("max-width", "none", "important");
|
|
this.element.style.setProperty("max-height", "none", "important");
|
|
this.element.style.setProperty("position", "absolute", "important");
|
|
this.element.setAttribute("role", "dialog");
|
|
this.updateElementSize();
|
|
this.element.addEventListener(
|
|
"load",
|
|
watchFunction(() => {
|
|
this.document = this.element.contentDocument;
|
|
this.window = this.element.contentWindow;
|
|
assertIsBlankDocument(this.document);
|
|
// eslint-disable-next-line no-unsanitized/property
|
|
this.document.documentElement.innerHTML = `
|
|
<head>
|
|
<style>${substitutedCss}</style>
|
|
<title></title>
|
|
</head>
|
|
<body></body>`;
|
|
installHandlerOnDocument(this.document);
|
|
if (this.addClassName) {
|
|
this.document.body.className = this.addClassName;
|
|
}
|
|
this.document.documentElement.dir =
|
|
browser.i18n.getMessage("@@bidi_dir");
|
|
this.document.documentElement.lang =
|
|
browser.i18n.getMessage("@@ui_locale");
|
|
resolve();
|
|
}),
|
|
{ once: true }
|
|
);
|
|
document.body.appendChild(this.element);
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
},
|
|
|
|
hide() {
|
|
this.element.style.display = "none";
|
|
this.stopSizeWatch();
|
|
},
|
|
|
|
unhide() {
|
|
this.updateElementSize();
|
|
this.element.style.display = "block";
|
|
this.initSizeWatch();
|
|
this.element.focus();
|
|
},
|
|
|
|
updateElementSize(force) {
|
|
// Note: if someone sizes down the page, then the iframe will keep the
|
|
// document from naturally shrinking. We use force to temporarily hide
|
|
// the element so that we can tell if the document shrinks
|
|
const visible = this.element.style.display !== "none";
|
|
if (force && visible) {
|
|
this.element.style.display = "none";
|
|
}
|
|
const height = Math.max(
|
|
document.documentElement.clientHeight,
|
|
document.body.clientHeight,
|
|
document.documentElement.scrollHeight,
|
|
document.body.scrollHeight
|
|
);
|
|
if (height !== this.sizeTracking.lastHeight) {
|
|
this.sizeTracking.lastHeight = height;
|
|
this.element.style.height = height + "px";
|
|
}
|
|
// Do not use window.innerWidth since that includes the width of the
|
|
// scroll bar.
|
|
const width = Math.max(
|
|
document.documentElement.clientWidth,
|
|
document.body.clientWidth,
|
|
document.documentElement.scrollWidth,
|
|
document.body.scrollWidth
|
|
);
|
|
if (width !== this.sizeTracking.lastWidth) {
|
|
this.sizeTracking.lastWidth = width;
|
|
this.element.style.width = width + "px";
|
|
// Since this frame has an absolute position relative to the parent
|
|
// document, if the parent document's body has a relative position and
|
|
// left and/or top not at 0, then the left and/or top of the parent
|
|
// document's body is not at (0, 0) of the viewport. That makes the
|
|
// frame shifted relative to the viewport. These margins negates that.
|
|
if (window.getComputedStyle(document.body).position === "relative") {
|
|
const docBoundingRect =
|
|
document.documentElement.getBoundingClientRect();
|
|
const bodyBoundingRect = document.body.getBoundingClientRect();
|
|
this.element.style.marginLeft = `-${
|
|
bodyBoundingRect.left - docBoundingRect.left
|
|
}px`;
|
|
this.element.style.marginTop = `-${
|
|
bodyBoundingRect.top - docBoundingRect.top
|
|
}px`;
|
|
}
|
|
}
|
|
if (force && visible) {
|
|
this.element.style.display = "block";
|
|
}
|
|
},
|
|
|
|
initSizeWatch() {
|
|
this.stopSizeWatch();
|
|
this.sizeTracking.timer = setInterval(
|
|
watchFunction(this.updateElementSize.bind(this)),
|
|
2000
|
|
);
|
|
window.addEventListener("resize", this.onResize, true);
|
|
},
|
|
|
|
stopSizeWatch() {
|
|
if (this.sizeTracking.timer) {
|
|
clearTimeout(this.sizeTracking.timer);
|
|
this.sizeTracking.timer = null;
|
|
}
|
|
if (this.sizeTracking.windowDelayer) {
|
|
clearTimeout(this.sizeTracking.windowDelayer);
|
|
this.sizeTracking.windowDelayer = null;
|
|
}
|
|
this.sizeTracking.lastHeight = this.sizeTracking.lastWidth = null;
|
|
window.removeEventListener("resize", this.onResize, true);
|
|
},
|
|
|
|
getElementFromPoint(x, y) {
|
|
this.element.style.pointerEvents = "none";
|
|
let el;
|
|
try {
|
|
el = document.elementFromPoint(x, y);
|
|
} finally {
|
|
this.element.style.pointerEvents = "";
|
|
}
|
|
return el;
|
|
},
|
|
|
|
remove() {
|
|
this.stopSizeWatch();
|
|
util.removeNode(this.element);
|
|
this.element = this.document = this.window = null;
|
|
},
|
|
});
|
|
|
|
iframeSelection.onResize = watchFunction(
|
|
assertIsTrusted(onResize.bind(iframeSelection)),
|
|
true
|
|
);
|
|
|
|
const iframePreSelection = (exports.iframePreSelection = {
|
|
element: null,
|
|
document: null,
|
|
window: null,
|
|
display(installHandlerOnDocument, standardOverlayCallbacks) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!this.element) {
|
|
this.element = initializeIframe();
|
|
this.element.id = "firefox-screenshots-preselection-iframe";
|
|
this.element.style.setProperty("position", "fixed", "important");
|
|
this.element.style.width = "100%";
|
|
this.element.style.height = "100%";
|
|
this.element.style.setProperty("max-width", "none", "important");
|
|
this.element.style.setProperty("max-height", "none", "important");
|
|
this.element.setAttribute("role", "dialog");
|
|
this.element.addEventListener(
|
|
"load",
|
|
watchFunction(() => {
|
|
this.document = this.element.contentDocument;
|
|
this.window = this.element.contentWindow;
|
|
assertIsBlankDocument(this.document);
|
|
// eslint-disable-next-line no-unsanitized/property
|
|
this.document.documentElement.innerHTML = `
|
|
<head>
|
|
<link rel="localization" href="browser/screenshots.ftl">
|
|
<style>${substitutedCss}</style>
|
|
<title></title>
|
|
</head>
|
|
<body>
|
|
<div class="preview-overlay precision-cursor">
|
|
<div class="fixed-container">
|
|
<div class="face-container">
|
|
<div class="eye left"><div class="eyeball"></div></div>
|
|
<div class="eye right"><div class="eyeball"></div></div>
|
|
<div class="face"></div>
|
|
</div>
|
|
<div class="preview-instructions" data-l10n-id="screenshots-instructions"></div>
|
|
<button class="cancel-shot" data-l10n-id="screenshots-cancel-button"></button>
|
|
<div class="all-buttons-container">
|
|
<button class="visible" tabindex="2" data-l10n-id="screenshots-save-visible-button"></button>
|
|
<button class="full-page" tabindex="1" data-l10n-id="screenshots-save-page-button"></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>`;
|
|
installHandlerOnDocument(this.document);
|
|
if (this.addClassName) {
|
|
this.document.body.className = this.addClassName;
|
|
}
|
|
this.document.documentElement.dir =
|
|
browser.i18n.getMessage("@@bidi_dir");
|
|
this.document.documentElement.lang =
|
|
browser.i18n.getMessage("@@ui_locale");
|
|
const overlay = this.document.querySelector(".preview-overlay");
|
|
overlay
|
|
.querySelector(".visible")
|
|
.addEventListener(
|
|
"click",
|
|
watchFunction(
|
|
assertIsTrusted(standardOverlayCallbacks.onClickVisible)
|
|
)
|
|
);
|
|
overlay
|
|
.querySelector(".full-page")
|
|
.addEventListener(
|
|
"click",
|
|
watchFunction(
|
|
assertIsTrusted(standardOverlayCallbacks.onClickFullPage)
|
|
)
|
|
);
|
|
overlay
|
|
.querySelector(".cancel-shot")
|
|
.addEventListener(
|
|
"click",
|
|
watchFunction(
|
|
assertIsTrusted(standardOverlayCallbacks.onClickCancel)
|
|
)
|
|
);
|
|
|
|
resolve();
|
|
}),
|
|
{ once: true }
|
|
);
|
|
document.body.appendChild(this.element);
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
},
|
|
|
|
hide() {
|
|
window.removeEventListener(
|
|
"scroll",
|
|
watchFunction(assertIsTrusted(this.onScroll))
|
|
);
|
|
window.removeEventListener("resize", this.onResize, true);
|
|
if (this.element) {
|
|
this.element.style.display = "none";
|
|
}
|
|
},
|
|
|
|
unhide() {
|
|
window.addEventListener(
|
|
"scroll",
|
|
watchFunction(assertIsTrusted(this.onScroll))
|
|
);
|
|
window.addEventListener("resize", this.onResize, true);
|
|
this.element.style.display = "block";
|
|
this.element.focus();
|
|
},
|
|
|
|
onScroll() {
|
|
exports.HoverBox.hide();
|
|
},
|
|
|
|
getElementFromPoint(x, y) {
|
|
this.element.style.pointerEvents = "none";
|
|
let el;
|
|
try {
|
|
el = document.elementFromPoint(x, y);
|
|
} finally {
|
|
this.element.style.pointerEvents = "";
|
|
}
|
|
return el;
|
|
},
|
|
|
|
remove() {
|
|
this.hide();
|
|
util.removeNode(this.element);
|
|
this.element = this.document = this.window = null;
|
|
},
|
|
});
|
|
|
|
let msgsPromise = callBackground("getStrings", [
|
|
"screenshots-cancel-button",
|
|
"screenshots-copy-button-tooltip",
|
|
"screenshots-download-button-tooltip",
|
|
"screenshots-copy-button",
|
|
"screenshots-download-button",
|
|
]);
|
|
|
|
const iframePreview = (exports.iframePreview = {
|
|
element: null,
|
|
document: null,
|
|
window: null,
|
|
display(installHandlerOnDocument, standardOverlayCallbacks) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!this.element) {
|
|
this.element = initializeIframe();
|
|
this.element.id = "firefox-screenshots-preview-iframe";
|
|
this.element.style.display = "none";
|
|
this.element.style.setProperty("position", "fixed", "important");
|
|
this.element.style.height = "100%";
|
|
this.element.style.width = "100%";
|
|
this.element.style.setProperty("max-width", "none", "important");
|
|
this.element.style.setProperty("max-height", "none", "important");
|
|
this.element.setAttribute("role", "dialog");
|
|
this.element.onload = watchFunction(() => {
|
|
msgsPromise.then(([cancelTitle, copyTitle, downloadTitle]) => {
|
|
assertIsBlankDocument(this.element.contentDocument);
|
|
this.document = this.element.contentDocument;
|
|
this.window = this.element.contentWindow;
|
|
// eslint-disable-next-line no-unsanitized/property
|
|
this.document.documentElement.innerHTML = `
|
|
<head>
|
|
<link rel="localization" href="browser/screenshots.ftl">
|
|
<style>${substitutedCss}</style>
|
|
<title></title>
|
|
</head>
|
|
<body>
|
|
<div class="preview-overlay">
|
|
<div class="preview-image">
|
|
<div class="preview-buttons">
|
|
<button class="highlight-button-cancel" title="${cancelTitle}">
|
|
<img src="chrome://browser/content/screenshots/cancel.svg"/>
|
|
</button>
|
|
<button class="highlight-button-copy" title="${copyTitle}">
|
|
<img src="chrome://browser/content/screenshots/copy.svg"/>
|
|
<span data-l10n-id="screenshots-copy-button"/>
|
|
</button>
|
|
<button class="highlight-button-download" title="${downloadTitle}">
|
|
<img src="chrome://browser/content/screenshots/download-white.svg"/>
|
|
<span data-l10n-id="screenshots-download-button"/>
|
|
</button>
|
|
</div>
|
|
<div class="preview-image-wrapper"></div>
|
|
</div>
|
|
<div class="loader" style="display:none">
|
|
<div class="loader-inner"></div>
|
|
</div>
|
|
</div>
|
|
</body>`;
|
|
|
|
installHandlerOnDocument(this.document);
|
|
this.document.documentElement.dir =
|
|
browser.i18n.getMessage("@@bidi_dir");
|
|
this.document.documentElement.lang =
|
|
browser.i18n.getMessage("@@ui_locale");
|
|
|
|
const overlay = this.document.querySelector(".preview-overlay");
|
|
overlay
|
|
.querySelector(".highlight-button-copy")
|
|
.addEventListener(
|
|
"click",
|
|
watchFunction(
|
|
assertIsTrusted(standardOverlayCallbacks.onCopyPreview)
|
|
)
|
|
);
|
|
overlay
|
|
.querySelector(".highlight-button-download")
|
|
.addEventListener(
|
|
"click",
|
|
watchFunction(
|
|
assertIsTrusted(standardOverlayCallbacks.onDownloadPreview)
|
|
)
|
|
);
|
|
overlay
|
|
.querySelector(".highlight-button-cancel")
|
|
.addEventListener(
|
|
"click",
|
|
watchFunction(
|
|
assertIsTrusted(standardOverlayCallbacks.cancel)
|
|
)
|
|
);
|
|
resolve();
|
|
});
|
|
});
|
|
document.body.appendChild(this.element);
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
},
|
|
|
|
hide() {
|
|
if (this.element) {
|
|
this.element.style.display = "none";
|
|
}
|
|
},
|
|
|
|
unhide() {
|
|
this.element.style.display = "block";
|
|
this.element.focus();
|
|
},
|
|
|
|
showLoader() {
|
|
this.document.body.querySelector(".preview-image").style.display = "none";
|
|
this.document.body.querySelector(".loader").style.display = "";
|
|
},
|
|
|
|
remove() {
|
|
this.hide();
|
|
util.removeNode(this.element);
|
|
this.element = null;
|
|
this.document = null;
|
|
},
|
|
});
|
|
|
|
iframePreSelection.onResize = watchFunction(
|
|
onResize.bind(iframePreSelection),
|
|
true
|
|
);
|
|
|
|
const iframe = (exports.iframe = {
|
|
currentIframe: iframePreSelection,
|
|
display(installHandlerOnDocument, standardOverlayCallbacks) {
|
|
return iframeSelection
|
|
.display(installHandlerOnDocument)
|
|
.then(() =>
|
|
iframePreSelection.display(
|
|
installHandlerOnDocument,
|
|
standardOverlayCallbacks
|
|
)
|
|
)
|
|
.then(() =>
|
|
iframePreview.display(
|
|
installHandlerOnDocument,
|
|
standardOverlayCallbacks
|
|
)
|
|
);
|
|
},
|
|
|
|
hide() {
|
|
this.currentIframe.hide();
|
|
},
|
|
|
|
unhide() {
|
|
this.currentIframe.unhide();
|
|
},
|
|
|
|
showLoader() {
|
|
if (this.currentIframe.showLoader) {
|
|
this.currentIframe.showLoader();
|
|
this.currentIframe.unhide();
|
|
}
|
|
},
|
|
|
|
getElementFromPoint(x, y) {
|
|
return this.currentIframe.getElementFromPoint(x, y);
|
|
},
|
|
|
|
remove() {
|
|
iframeSelection.remove();
|
|
iframePreSelection.remove();
|
|
iframePreview.remove();
|
|
},
|
|
|
|
getContentWindow() {
|
|
return this.currentIframe.element.contentWindow;
|
|
},
|
|
|
|
document() {
|
|
return this.currentIframe.document;
|
|
},
|
|
|
|
useSelection() {
|
|
if (
|
|
this.currentIframe === iframePreSelection ||
|
|
this.currentIframe === iframePreview
|
|
) {
|
|
this.hide();
|
|
}
|
|
this.currentIframe = iframeSelection;
|
|
this.unhide();
|
|
},
|
|
|
|
usePreSelection() {
|
|
if (
|
|
this.currentIframe === iframeSelection ||
|
|
this.currentIframe === iframePreview
|
|
) {
|
|
this.hide();
|
|
}
|
|
this.currentIframe = iframePreSelection;
|
|
this.unhide();
|
|
},
|
|
|
|
usePreview() {
|
|
if (
|
|
this.currentIframe === iframeSelection ||
|
|
this.currentIframe === iframePreSelection
|
|
) {
|
|
this.hide();
|
|
}
|
|
this.currentIframe = iframePreview;
|
|
this.unhide();
|
|
},
|
|
});
|
|
|
|
const movements = [
|
|
"topLeft",
|
|
"top",
|
|
"topRight",
|
|
"left",
|
|
"right",
|
|
"bottomLeft",
|
|
"bottom",
|
|
"bottomRight",
|
|
];
|
|
|
|
/** Creates the selection box */
|
|
exports.Box = {
|
|
async display(pos, callbacks) {
|
|
await this._createEl();
|
|
if (callbacks !== undefined && callbacks.cancel) {
|
|
// We use onclick here because we don't want addEventListener
|
|
// to add multiple event handlers to the same button
|
|
this.cancel.onclick = watchFunction(assertIsTrusted(callbacks.cancel));
|
|
this.cancel.style.display = "";
|
|
} else {
|
|
this.cancel.style.display = "none";
|
|
}
|
|
if (callbacks !== undefined && callbacks.download) {
|
|
this.download.removeAttribute("disabled");
|
|
this.download.onclick = watchFunction(
|
|
assertIsTrusted(e => {
|
|
this.download.setAttribute("disabled", true);
|
|
callbacks.download(e);
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
return false;
|
|
})
|
|
);
|
|
this.download.style.display = "";
|
|
} else {
|
|
this.download.style.display = "none";
|
|
}
|
|
if (callbacks !== undefined && callbacks.copy) {
|
|
this.copy.removeAttribute("disabled");
|
|
this.copy.onclick = watchFunction(
|
|
assertIsTrusted(e => {
|
|
this.copy.setAttribute("disabled", true);
|
|
callbacks.copy(e);
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
})
|
|
);
|
|
this.copy.style.display = "";
|
|
} else {
|
|
this.copy.style.display = "none";
|
|
}
|
|
|
|
const winBottom = window.innerHeight;
|
|
const pageYOffset = window.pageYOffset;
|
|
|
|
if (pos.right - pos.left < 78 || pos.bottom - pos.top < 78) {
|
|
this.el.classList.add("small-selection");
|
|
} else {
|
|
this.el.classList.remove("small-selection");
|
|
}
|
|
|
|
// if the selection bounding box is w/in SAVE_BUTTON_HEIGHT px of the bottom of
|
|
// the window, flip controls into the box
|
|
if (pos.bottom > winBottom + pageYOffset - SAVE_BUTTON_HEIGHT) {
|
|
this.el.classList.add("bottom-selection");
|
|
} else {
|
|
this.el.classList.remove("bottom-selection");
|
|
}
|
|
|
|
if (pos.right < 200) {
|
|
this.el.classList.add("left-selection");
|
|
} else {
|
|
this.el.classList.remove("left-selection");
|
|
}
|
|
this.el.style.top = `${pos.top}px`;
|
|
this.el.style.left = `${pos.left}px`;
|
|
this.el.style.height = `${pos.bottom - pos.top}px`;
|
|
this.el.style.width = `${pos.right - pos.left}px`;
|
|
this.bgTop.style.top = "0px";
|
|
this.bgTop.style.height = `${pos.top}px`;
|
|
this.bgTop.style.left = "0px";
|
|
this.bgTop.style.width = "100%";
|
|
this.bgBottom.style.top = `${pos.bottom}px`;
|
|
this.bgBottom.style.height = `calc(100vh - ${pos.bottom}px)`;
|
|
this.bgBottom.style.left = "0px";
|
|
this.bgBottom.style.width = "100%";
|
|
this.bgLeft.style.top = `${pos.top}px`;
|
|
this.bgLeft.style.height = `${pos.bottom - pos.top}px`;
|
|
this.bgLeft.style.left = "0px";
|
|
this.bgLeft.style.width = `${pos.left}px`;
|
|
this.bgRight.style.top = `${pos.top}px`;
|
|
this.bgRight.style.height = `${pos.bottom - pos.top}px`;
|
|
this.bgRight.style.left = `${pos.right}px`;
|
|
this.bgRight.style.width = `calc(100% - ${pos.right}px)`;
|
|
},
|
|
|
|
// used to eventually move the download-only warning
|
|
// when a user ends scrolling or ends resizing a window
|
|
delayExecution(delay, cb) {
|
|
let timer;
|
|
return function () {
|
|
if (typeof timer !== "undefined") {
|
|
clearTimeout(timer);
|
|
}
|
|
timer = setTimeout(cb, delay);
|
|
};
|
|
},
|
|
|
|
remove() {
|
|
for (const name of ["el", "bgTop", "bgLeft", "bgRight", "bgBottom"]) {
|
|
if (name in this) {
|
|
util.removeNode(this[name]);
|
|
this[name] = null;
|
|
}
|
|
}
|
|
},
|
|
|
|
async _createEl() {
|
|
let boxEl = this.el;
|
|
if (boxEl) {
|
|
return;
|
|
}
|
|
let [cancelTitle, copyTitle, downloadTitle, copyText, downloadText] =
|
|
await msgsPromise;
|
|
boxEl = makeEl("div", "highlight");
|
|
const buttons = makeEl("div", "highlight-buttons");
|
|
const cancel = makeEl("button", "highlight-button-cancel");
|
|
const cancelImg = makeEl("img");
|
|
cancelImg.src = "chrome://browser/content/screenshots/cancel.svg";
|
|
cancel.title = cancelTitle;
|
|
cancel.appendChild(cancelImg);
|
|
buttons.appendChild(cancel);
|
|
|
|
const copy = makeEl("button", "highlight-button-copy");
|
|
copy.title = copyTitle;
|
|
const copyImg = makeEl("img");
|
|
const copyString = makeEl("span");
|
|
copyString.textContent = copyText;
|
|
copyImg.src = "chrome://browser/content/screenshots/copy.svg";
|
|
copy.appendChild(copyImg);
|
|
copy.appendChild(copyString);
|
|
buttons.appendChild(copy);
|
|
|
|
const download = makeEl("button", "highlight-button-download");
|
|
const downloadImg = makeEl("img");
|
|
downloadImg.src =
|
|
"chrome://browser/content/screenshots/download-white.svg";
|
|
download.appendChild(downloadImg);
|
|
download.append(downloadText);
|
|
download.title = downloadTitle;
|
|
buttons.appendChild(download);
|
|
this.cancel = cancel;
|
|
this.download = download;
|
|
this.copy = copy;
|
|
|
|
boxEl.appendChild(buttons);
|
|
for (const name of movements) {
|
|
const elTarget = makeEl("div", "mover-target direction-" + name);
|
|
const elMover = makeEl("div", "mover");
|
|
elTarget.appendChild(elMover);
|
|
boxEl.appendChild(elTarget);
|
|
}
|
|
this.bgTop = makeEl("div", "bghighlight");
|
|
iframe.document().body.appendChild(this.bgTop);
|
|
this.bgLeft = makeEl("div", "bghighlight");
|
|
iframe.document().body.appendChild(this.bgLeft);
|
|
this.bgRight = makeEl("div", "bghighlight");
|
|
iframe.document().body.appendChild(this.bgRight);
|
|
this.bgBottom = makeEl("div", "bghighlight");
|
|
iframe.document().body.appendChild(this.bgBottom);
|
|
iframe.document().body.appendChild(boxEl);
|
|
this.el = boxEl;
|
|
},
|
|
|
|
draggerDirection(target) {
|
|
while (target) {
|
|
if (target.nodeType === document.ELEMENT_NODE) {
|
|
if (target.classList.contains("mover-target")) {
|
|
for (const name of movements) {
|
|
if (target.classList.contains("direction-" + name)) {
|
|
return name;
|
|
}
|
|
}
|
|
catcher.unhandled(new Error("Surprising mover element"), {
|
|
element: target.outerHTML,
|
|
});
|
|
log.warn("Got mover-target that wasn't a specific direction");
|
|
}
|
|
}
|
|
target = target.parentNode;
|
|
}
|
|
return null;
|
|
},
|
|
|
|
isSelection(target) {
|
|
while (target) {
|
|
if (target.tagName === "BUTTON") {
|
|
return false;
|
|
}
|
|
if (
|
|
target.nodeType === document.ELEMENT_NODE &&
|
|
target.classList.contains("highlight")
|
|
) {
|
|
return true;
|
|
}
|
|
target = target.parentNode;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
isControl(target) {
|
|
while (target) {
|
|
if (
|
|
target.nodeType === document.ELEMENT_NODE &&
|
|
target.classList.contains("highlight-buttons")
|
|
) {
|
|
return true;
|
|
}
|
|
target = target.parentNode;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
el: null,
|
|
boxTopEl: null,
|
|
boxLeftEl: null,
|
|
boxRightEl: null,
|
|
boxBottomEl: null,
|
|
};
|
|
|
|
exports.HoverBox = {
|
|
el: null,
|
|
|
|
display(rect) {
|
|
if (!this.el) {
|
|
this.el = makeEl("div", "hover-highlight");
|
|
iframe.document().body.appendChild(this.el);
|
|
}
|
|
this.el.style.display = "";
|
|
this.el.style.top = rect.top - 1 + "px";
|
|
this.el.style.left = rect.left - 1 + "px";
|
|
this.el.style.width = rect.right - rect.left + 2 + "px";
|
|
this.el.style.height = rect.bottom - rect.top + 2 + "px";
|
|
},
|
|
|
|
hide() {
|
|
if (this.el) {
|
|
this.el.style.display = "none";
|
|
}
|
|
},
|
|
|
|
remove() {
|
|
util.removeNode(this.el);
|
|
this.el = null;
|
|
},
|
|
};
|
|
|
|
exports.PixelDimensions = {
|
|
el: null,
|
|
xEl: null,
|
|
yEl: null,
|
|
display(xPos, yPos, x, y) {
|
|
if (!this.el) {
|
|
this.el = makeEl("div", "pixel-dimensions");
|
|
this.xEl = makeEl("div");
|
|
this.el.appendChild(this.xEl);
|
|
this.yEl = makeEl("div");
|
|
this.el.appendChild(this.yEl);
|
|
iframe.document().body.appendChild(this.el);
|
|
}
|
|
this.xEl.textContent = Math.round(x);
|
|
this.yEl.textContent = Math.round(y);
|
|
this.el.style.top = yPos + 12 + "px";
|
|
this.el.style.left = xPos + 12 + "px";
|
|
},
|
|
remove() {
|
|
util.removeNode(this.el);
|
|
this.el = this.xEl = this.yEl = null;
|
|
},
|
|
};
|
|
|
|
exports.Preview = {
|
|
display(dataUrl) {
|
|
const img = makeEl("IMG");
|
|
const imgBlob = blobConverters.dataUrlToBlob(dataUrl);
|
|
img.src = iframe.getContentWindow().URL.createObjectURL(imgBlob);
|
|
iframe
|
|
.document()
|
|
.querySelector(".preview-image-wrapper")
|
|
.appendChild(img);
|
|
},
|
|
};
|
|
|
|
/** Removes every UI this module creates */
|
|
exports.remove = function () {
|
|
for (const name in exports) {
|
|
if (name.startsWith("iframe")) {
|
|
continue;
|
|
}
|
|
if (typeof exports[name] === "object" && exports[name].remove) {
|
|
exports[name].remove();
|
|
}
|
|
}
|
|
exports.iframe.remove();
|
|
};
|
|
|
|
exports.triggerDownload = function (url, filename) {
|
|
return catcher.watchPromise(
|
|
callBackground("downloadShot", { url, filename })
|
|
);
|
|
};
|
|
|
|
exports.unload = exports.remove;
|
|
|
|
return exports;
|
|
})();
|
|
null;
|