Source code

Revision control

Copy as Markdown

Other Tools

/* 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) {
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;