screenshot-content.js (4955B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 "use strict"; 5 6 const { Actor } = require("resource://devtools/shared/protocol.js"); 7 const { 8 screenshotContentSpec, 9 } = require("resource://devtools/shared/specs/screenshot-content.js"); 10 11 const { LocalizationHelper } = require("resource://devtools/shared/l10n.js"); 12 const STRINGS_URI = "devtools/shared/locales/screenshot.properties"; 13 const L10N = new LocalizationHelper(STRINGS_URI); 14 loader.lazyRequireGetter( 15 this, 16 ["getCurrentZoom", "getRect"], 17 "resource://devtools/shared/layout/utils.js", 18 true 19 ); 20 21 exports.ScreenshotContentActor = class ScreenshotContentActor extends Actor { 22 constructor(conn, targetActor) { 23 super(conn, screenshotContentSpec); 24 this.targetActor = targetActor; 25 } 26 27 _getRectForNode(node) { 28 const originWindow = this.targetActor.ignoreSubFrames 29 ? node.ownerGlobal 30 : node.ownerGlobal.top; 31 return getRect(originWindow, node, node.ownerGlobal); 32 } 33 34 /** 35 * Retrieve some window-related information that will be passed to the parent process 36 * to actually generate the screenshot. 37 * 38 * @param {object} args 39 * @param {boolean} args.fullpage: Should the screenshot be the height of the whole page 40 * @param {string} args.selector: A CSS selector for the element we should take the 41 * screenshot of. The function will return true for the `error` property 42 * if the screenshot does not match any element. 43 * @param {string} args.nodeActorID: The actorID of the node actor matching the element 44 * we should take the screenshot of. 45 * @returns {object} An object with the following properties: 46 * - error {Boolean}: Set to true if an issue was encountered that prevents 47 * taking the screenshot 48 * - messages {Array<Object{text, level}>}: An array of objects representing 49 * the messages emitted throught the process and their level. 50 * - windowDpr {Number}: Value of window.devicePixelRatio 51 * - windowZoom {Number}: The page current zoom level 52 * - rect {Object}: Object with left, top, width and height properties 53 * representing the rect **inside the browser element** that should be rendered. 54 * For screenshot of the current viewport, we return null, as expected by the 55 * `drawSnapshot` API. 56 */ 57 prepareCapture({ fullpage, selector, nodeActorID }) { 58 const { window } = this.targetActor; 59 // Use the override if set, note that the override is not returned by 60 // devicePixelRatio on privileged code, see bug 1759962. 61 // 62 // FIXME(bug 1760711): Whether zoom is included in devicePixelRatio depends 63 // on whether there's an override, this is a bit suspect. 64 const windowDpr = 65 window.browsingContext.top.overrideDPPX || window.devicePixelRatio; 66 const windowZoom = getCurrentZoom(window); 67 const messages = []; 68 69 // If we're going to take the current view of the page, we don't need to compute a rect, 70 // since it's the default behaviour of drawSnapshot. 71 if (!fullpage && !selector && !nodeActorID) { 72 return { 73 rect: null, 74 messages, 75 windowDpr, 76 windowZoom, 77 }; 78 } 79 80 let left; 81 let top; 82 let width; 83 let height; 84 85 if (fullpage) { 86 // We don't want to render the scrollbars 87 const winUtils = window.windowUtils; 88 const scrollbarHeight = {}; 89 const scrollbarWidth = {}; 90 winUtils.getScrollbarSize(false, scrollbarWidth, scrollbarHeight); 91 92 left = 0; 93 top = 0; 94 width = 95 window.innerWidth + 96 window.scrollMaxX - 97 window.scrollMinX - 98 scrollbarWidth.value; 99 height = 100 window.innerHeight + 101 window.scrollMaxY - 102 window.scrollMinY - 103 scrollbarHeight.value; 104 } else if (selector) { 105 const node = window.document.querySelector(selector); 106 107 if (!node) { 108 messages.push({ 109 level: "warn", 110 text: L10N.getFormatStr("screenshotNoSelectorMatchWarning", selector), 111 }); 112 113 return { 114 error: true, 115 messages, 116 }; 117 } 118 119 ({ left, top, width, height } = this._getRectForNode(node)); 120 } else if (nodeActorID) { 121 const nodeActor = this.conn.getActor(nodeActorID); 122 if (!nodeActor) { 123 messages.push({ 124 level: "error", 125 text: `Screenshot actor failed to find Node actor for '${nodeActorID}'`, 126 }); 127 128 return { 129 error: true, 130 messages, 131 }; 132 } 133 134 ({ left, top, width, height } = this._getRectForNode(nodeActor.rawNode)); 135 } 136 137 return { 138 windowDpr, 139 windowZoom, 140 rect: { left, top, width, height }, 141 messages, 142 }; 143 } 144 };