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/. */
"use strict";
const { Actor } = require("resource://devtools/shared/protocol.js");
const {
screenshotContentSpec,
} = require("resource://devtools/shared/specs/screenshot-content.js");
const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
const STRINGS_URI = "devtools/shared/locales/screenshot.properties";
const L10N = new LocalizationHelper(STRINGS_URI);
loader.lazyRequireGetter(
this,
["getCurrentZoom", "getRect"],
"resource://devtools/shared/layout/utils.js",
true
);
exports.ScreenshotContentActor = class ScreenshotContentActor extends Actor {
constructor(conn, targetActor) {
super(conn, screenshotContentSpec);
this.targetActor = targetActor;
}
_getRectForNode(node) {
const originWindow = this.targetActor.ignoreSubFrames
? node.ownerGlobal
: node.ownerGlobal.top;
return getRect(originWindow, node, node.ownerGlobal);
}
/**
* Retrieve some window-related information that will be passed to the parent process
* to actually generate the screenshot.
*
* @param {Object} args
* @param {Boolean} args.fullpage: Should the screenshot be the height of the whole page
* @param {String} args.selector: A CSS selector for the element we should take the
* screenshot of. The function will return true for the `error` property
* if the screenshot does not match any element.
* @param {String} args.nodeActorID: The actorID of the node actor matching the element
* we should take the screenshot of.
* @returns {Object} An object with the following properties:
* - error {Boolean}: Set to true if an issue was encountered that prevents
* taking the screenshot
* - messages {Array<Object{text, level}>}: An array of objects representing
* the messages emitted throught the process and their level.
* - windowDpr {Number}: Value of window.devicePixelRatio
* - windowZoom {Number}: The page current zoom level
* - rect {Object}: Object with left, top, width and height properties
* representing the rect **inside the browser element** that should be rendered.
* For screenshot of the current viewport, we return null, as expected by the
* `drawSnapshot` API.
*/
prepareCapture({ fullpage, selector, nodeActorID }) {
const { window } = this.targetActor;
// Use the override if set, note that the override is not returned by
// devicePixelRatio on privileged code, see bug 1759962.
//
// FIXME(bug 1760711): Whether zoom is included in devicePixelRatio depends
// on whether there's an override, this is a bit suspect.
const windowDpr =
window.browsingContext.top.overrideDPPX || window.devicePixelRatio;
const windowZoom = getCurrentZoom(window);
const messages = [];
let left;
let top;
let width;
let height;
// If we're going to take the current view of the page (!fullpage && !selector && !nodeActorID),
// we could leave the computation of the rect to drawSnapshot since that is its default behaviour.
// However, drawSnapshot computes the wrong size for a page without a viewport meta tag in RDM
// (Bug 1972770). Until that bug is fixed, we work around it by always computing the rect here.
if (fullpage || (!selector && !nodeActorID)) {
// We don't want to render the scrollbars
const winUtils = window.windowUtils;
const scrollbarHeight = {};
const scrollbarWidth = {};
winUtils.getScrollbarSize(false, scrollbarWidth, scrollbarHeight);
left = 0;
top = 0;
width = window.innerWidth - scrollbarWidth.value;
height = window.innerHeight - scrollbarHeight.value;
if (fullpage) {
width = width + window.scrollMaxX - window.scrollMinX;
height = height + window.scrollMaxY - window.scrollMinY;
}
} else if (selector) {
const node = window.document.querySelector(selector);
if (!node) {
messages.push({
level: "warn",
text: L10N.getFormatStr("screenshotNoSelectorMatchWarning", selector),
});
return {
error: true,
messages,
};
}
({ left, top, width, height } = this._getRectForNode(node));
} else if (nodeActorID) {
const nodeActor = this.conn.getActor(nodeActorID);
if (!nodeActor) {
messages.push({
level: "error",
text: `Screenshot actor failed to find Node actor for '${nodeActorID}'`,
});
return {
error: true,
messages,
};
}
({ left, top, width, height } = this._getRectForNode(nodeActor.rawNode));
}
return {
windowDpr,
windowZoom,
rect: { left, top, width, height },
messages,
};
}
};