/* 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 global, browser, documentMetadata, util, uicontrol, ui, catcher */ /* globals domainFromUrl, randomString, shot, blobConverters */ "use strict"; this.shooter = (function () { // eslint-disable-line no-unused-vars const exports = {}; const { AbstractShot } = shot; const RANDOM_STRING_LENGTH = 16; let backend; let shotObject; const callBackground = global.callBackground; function regexpEscape(str) { // http://stackoverflow.com/questions/3115150/how-to-escape-regular-expression-special-characters-using-javascript return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); } function sanitizeError(data) { const href = new RegExp(regexpEscape(window.location.href), "g"); const origin = new RegExp( `${regexpEscape(window.location.origin)}[^ \t\n\r",>]*`, "g" ); const json = JSON.stringify(data) .replace(href, "REDACTED_HREF") .replace(origin, "REDACTED_URL"); const result = JSON.parse(json); return result; } catcher.registerHandler(errorObj => { callBackground("reportError", sanitizeError(errorObj)); }); function hideUIFrame() { ui.iframe.hide(); return Promise.resolve(null); } function screenshotPage(dataUrl, selectedPos, type, screenshotTaskFn) { let promise = Promise.resolve(dataUrl); if (!dataUrl) { promise = callBackground( "screenshotPage", selectedPos.toJSON(), type, window.devicePixelRatio ); } catcher.watchPromise( promise.then(dataLoc => { screenshotTaskFn(dataLoc); }) ); } exports.downloadShot = function (selectedPos, previewDataUrl, type) { const shotPromise = previewDataUrl ? Promise.resolve(previewDataUrl) : hideUIFrame(); catcher.watchPromise( shotPromise.then(dataUrl => { screenshotPage(dataUrl, selectedPos, type, url => { let typeFromDataUrl = blobConverters.getTypeFromDataUrl(url); typeFromDataUrl = typeFromDataUrl ? typeFromDataUrl.split("/", 2)[1] : null; shotObject.delAllClips(); shotObject.addClip({ createdDate: Date.now(), image: { url, type: typeFromDataUrl, location: selectedPos, }, }); ui.triggerDownload(url, shotObject.filename); uicontrol.deactivate(); }); }) ); }; exports.preview = function (selectedPos, type) { catcher.watchPromise( hideUIFrame().then(dataUrl => { screenshotPage(dataUrl, selectedPos, type, url => { ui.iframe.usePreview(); ui.Preview.display(url); }); }) ); }; let copyInProgress = null; exports.copyShot = function (selectedPos, previewDataUrl, type) { // This is pretty slow. We'll ignore additional user triggered copy events // while it is in progress. if (copyInProgress) { return; } // A max of five seconds in case some error occurs. copyInProgress = setTimeout(() => { copyInProgress = null; }, 5000); const unsetCopyInProgress = () => { if (copyInProgress) { clearTimeout(copyInProgress); copyInProgress = null; } }; const shotPromise = previewDataUrl ? Promise.resolve(previewDataUrl) : hideUIFrame(); catcher.watchPromise( shotPromise.then(dataUrl => { screenshotPage(dataUrl, selectedPos, type, url => { const blob = blobConverters.dataUrlToBlob(url); catcher.watchPromise( callBackground("copyShotToClipboard", blob).then(() => { uicontrol.deactivate(); unsetCopyInProgress(); }, unsetCopyInProgress) ); }); }) ); }; exports.sendEvent = function (...args) { const maybeOptions = args[args.length - 1]; if (typeof maybeOptions === "object") { maybeOptions.incognito = browser.extension.inIncognitoContext; } else { args.push({ incognito: browser.extension.inIncognitoContext }); } }; catcher.watchFunction(() => { shotObject = new AbstractShot( backend, randomString(RANDOM_STRING_LENGTH) + "/" + domainFromUrl(location), { origin: shot.originFromUrl(location.href), } ); shotObject.update(documentMetadata()); })(); return exports; })(); null;