2018-06-04 15:46:48 +00:00
|
|
|
/* 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";
|
2018-07-24 23:47:41 +00:00
|
|
|
const { Cu } = require("chrome");
|
2018-06-08 18:14:36 +00:00
|
|
|
const { LocalizationHelper } = require("devtools/shared/l10n");
|
2018-06-04 15:46:48 +00:00
|
|
|
|
|
|
|
const CONTAINER_FLASHING_DURATION = 500;
|
|
|
|
const STRINGS_URI = "devtools/shared/locales/screenshot.properties";
|
|
|
|
const L10N = new LocalizationHelper(STRINGS_URI);
|
|
|
|
|
2018-08-27 15:14:10 +00:00
|
|
|
loader.lazyRequireGetter(this, "getRect", "devtools/shared/layout/utils", true);
|
2018-06-04 15:46:48 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This function is called to simulate camera effects
|
2018-08-27 15:14:10 +00:00
|
|
|
* @param object document
|
|
|
|
* The target document.
|
2018-06-04 15:46:48 +00:00
|
|
|
*/
|
|
|
|
function simulateCameraFlash(document) {
|
|
|
|
const window = document.defaultView;
|
|
|
|
const frames = Cu.cloneInto({ opacity: [ 0, 1 ] }, window);
|
|
|
|
document.documentElement.animate(frames, CONTAINER_FLASHING_DURATION);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function simply handles the --delay argument before calling
|
|
|
|
* createScreenshotData
|
|
|
|
*/
|
|
|
|
function captureScreenshot(args, document) {
|
2018-08-27 15:14:10 +00:00
|
|
|
if (args.help) {
|
|
|
|
return null;
|
|
|
|
}
|
2018-06-04 15:46:48 +00:00
|
|
|
if (args.delay > 0) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
document.defaultView.setTimeout(() => {
|
2018-08-27 15:14:10 +00:00
|
|
|
createScreenshotDataURL(document, args).then(resolve, reject);
|
2018-06-04 15:46:48 +00:00
|
|
|
}, args.delay * 1000);
|
|
|
|
});
|
|
|
|
}
|
2018-08-27 15:14:10 +00:00
|
|
|
return createScreenshotDataURL(document, args);
|
2018-06-04 15:46:48 +00:00
|
|
|
}
|
|
|
|
|
2018-08-27 15:14:10 +00:00
|
|
|
exports.captureScreenshot = captureScreenshot;
|
|
|
|
|
2018-06-04 15:46:48 +00:00
|
|
|
/**
|
|
|
|
* This does the dirty work of creating a base64 string out of an
|
|
|
|
* area of the browser window
|
|
|
|
*/
|
2018-08-27 15:14:10 +00:00
|
|
|
function createScreenshotDataURL(document, args) {
|
2018-06-04 15:46:48 +00:00
|
|
|
const window = document.defaultView;
|
|
|
|
let left = 0;
|
|
|
|
let top = 0;
|
|
|
|
let width;
|
|
|
|
let height;
|
|
|
|
const currentX = window.scrollX;
|
|
|
|
const currentY = window.scrollY;
|
|
|
|
|
|
|
|
let filename = getFilename(args.filename);
|
|
|
|
|
|
|
|
if (args.fullpage) {
|
2018-09-06 11:42:57 +00:00
|
|
|
// Bug 961832: Screenshot shows fixed position element in wrong
|
2018-06-04 15:46:48 +00:00
|
|
|
// position if we don't scroll to top
|
|
|
|
window.scrollTo(0, 0);
|
|
|
|
width = window.innerWidth + window.scrollMaxX - window.scrollMinX;
|
|
|
|
height = window.innerHeight + window.scrollMaxY - window.scrollMinY;
|
|
|
|
filename = filename.replace(".png", "-fullpage.png");
|
|
|
|
} else if (args.selector) {
|
|
|
|
const node = window.document.querySelector(args.selector);
|
|
|
|
({ top, left, width, height } = getRect(window, node, window));
|
|
|
|
} else {
|
|
|
|
left = window.scrollX;
|
|
|
|
top = window.scrollY;
|
|
|
|
width = window.innerWidth;
|
|
|
|
height = window.innerHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only adjust for scrollbars when considering the full window
|
|
|
|
if (!args.selector) {
|
2018-07-24 23:47:41 +00:00
|
|
|
const winUtils = window.windowUtils;
|
2018-06-04 15:46:48 +00:00
|
|
|
const scrollbarHeight = {};
|
|
|
|
const scrollbarWidth = {};
|
|
|
|
winUtils.getScrollbarSize(false, scrollbarWidth, scrollbarHeight);
|
|
|
|
width -= scrollbarWidth.value;
|
|
|
|
height -= scrollbarHeight.value;
|
|
|
|
}
|
|
|
|
|
|
|
|
const canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
|
|
|
const ctx = canvas.getContext("2d");
|
|
|
|
const ratio = args.dpr ? args.dpr : window.devicePixelRatio;
|
|
|
|
canvas.width = width * ratio;
|
|
|
|
canvas.height = height * ratio;
|
|
|
|
ctx.scale(ratio, ratio);
|
|
|
|
ctx.drawWindow(window, left, top, width, height, "#fff");
|
|
|
|
const data = canvas.toDataURL("image/png", "");
|
|
|
|
|
|
|
|
// See comment above on bug 961832
|
|
|
|
if (args.fullpage) {
|
|
|
|
window.scrollTo(currentX, currentY);
|
|
|
|
}
|
|
|
|
|
|
|
|
simulateCameraFlash(document);
|
|
|
|
|
|
|
|
return Promise.resolve({
|
|
|
|
destinations: [],
|
|
|
|
data: data,
|
|
|
|
height: height,
|
|
|
|
width: width,
|
|
|
|
filename: filename,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-08-27 15:14:10 +00:00
|
|
|
exports.createScreenshotDataURL = createScreenshotDataURL;
|
|
|
|
|
2018-06-04 15:46:48 +00:00
|
|
|
/**
|
|
|
|
* We may have a filename specified in args, or we might have to generate
|
|
|
|
* one.
|
|
|
|
*/
|
|
|
|
function getFilename(defaultName) {
|
|
|
|
// Create a name for the file if not present
|
|
|
|
if (defaultName) {
|
|
|
|
return defaultName;
|
|
|
|
}
|
|
|
|
|
|
|
|
const date = new Date();
|
|
|
|
let dateString = date.getFullYear() + "-" + (date.getMonth() + 1) +
|
|
|
|
"-" + date.getDate();
|
|
|
|
dateString = dateString.split("-").map(function(part) {
|
|
|
|
if (part.length == 1) {
|
|
|
|
part = "0" + part;
|
|
|
|
}
|
|
|
|
return part;
|
|
|
|
}).join("-");
|
|
|
|
|
|
|
|
const timeString = date.toTimeString().replace(/:/g, ".").split(" ")[0];
|
|
|
|
return L10N.getFormatStr(
|
|
|
|
"screenshotGeneratedFilename",
|
|
|
|
dateString,
|
|
|
|
timeString
|
|
|
|
) + ".png";
|
|
|
|
}
|
2018-08-27 15:14:10 +00:00
|
|
|
|