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 /* eslint-env mozilla/browser-window */
8 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
12 ChromeUtils.defineESModuleGetters(lazy, {
13 ScreenshotsOverlayChild:
14 "resource:///modules/ScreenshotsOverlayChild.sys.mjs",
17 XPCOMUtils.defineLazyModuleGetters(lazy, {
18 DeferredTask: "resource://gre/modules/DeferredTask.jsm",
21 export class ScreenshotsComponentChild extends JSWindowActorChild {
22 receiveMessage(message) {
23 switch (message.name) {
24 case "Screenshots:ShowOverlay":
25 return this.startScreenshotsOverlay();
26 case "Screenshots:HideOverlay":
27 return this.endScreenshotsOverlay();
28 case "Screenshots:getFullPageBounds":
29 return this.getFullPageBounds();
30 case "Screenshots:getVisibleBounds":
31 return this.getVisibleBounds();
39 if (event.key === "Escape") {
40 this.requestCancelScreenshot();
44 this.requestCancelScreenshot();
47 if (!this._resizeTask && this._overlay?._initialized) {
48 this._resizeTask = new lazy.DeferredTask(() => {
49 this._overlay.updateScreenshotsSize("resize");
52 this._resizeTask.arm();
55 if (!this._scrollTask && this._overlay?._initialized) {
56 this._scrollTask = new lazy.DeferredTask(() => {
57 this._overlay.updateScreenshotsSize("scroll");
60 this._scrollTask.arm();
66 * Send a request to cancel the screenshot to the parent process
68 requestCancelScreenshot() {
69 this.sendAsyncMessage("Screenshots:CancelScreenshot", null);
72 requestCopyScreenshot(box) {
73 this.sendAsyncMessage("Screenshots:CopyScreenshot", box);
76 requestDownloadScreenshot(box) {
77 this.sendAsyncMessage("Screenshots:DownloadScreenshot", {
78 title: this.getTitle(),
84 return this.document.title;
88 * Resolves when the document is ready to have an overlay injected into it.
91 * @resolves {Boolean} true when document is ready or rejects
94 const document = this.document;
95 // Some pages take ages to finish loading - if at all.
96 // We want to respond to enable the screenshots UI as soon that is possible
97 function readyEnough() {
99 document.readyState !== "uninitialized" && document.documentElement
104 return Promise.resolve();
106 return new Promise((resolve, reject) => {
107 function onChange(event) {
108 if (event.type === "pagehide") {
109 document.removeEventListener("readystatechange", onChange);
110 this.contentWindow.removeEventListener("pagehide", onChange);
111 reject(new Error("document unloaded before it was ready"));
112 } else if (readyEnough()) {
113 document.removeEventListener("readystatechange", onChange);
114 this.contentWindow.removeEventListener("pagehide", onChange);
118 document.addEventListener("readystatechange", onChange);
119 this.contentWindow.addEventListener("pagehide", onChange, { once: true });
124 * Wait until the document is ready and then show the screenshots overlay
126 * @returns {Boolean} true when document is ready and the overlay is shown
129 async startScreenshotsOverlay() {
131 await this.documentIsReady();
133 console.warn(`ScreenshotsComponentChild: ${ex.message}`);
136 await this.documentIsReady();
139 (this._overlay = new lazy.ScreenshotsOverlayChild.AnonymousContentOverlay(
143 this.document.addEventListener("keydown", this);
144 this.document.ownerGlobal.addEventListener("beforeunload", this);
145 this.contentWindow.addEventListener("resize", this);
146 this.contentWindow.addEventListener("scroll", this);
147 overlay.initialize();
152 * Remove the screenshots overlay.
155 * true when the overlay has been removed otherwise false
157 endScreenshotsOverlay() {
158 this.document.removeEventListener("keydown", this);
159 this.document.ownerGlobal.removeEventListener("beforeunload", this);
160 this.contentWindow.removeEventListener("resize", this);
161 this.contentWindow.removeEventListener("scroll", this);
162 this._overlay?.tearDown();
163 this._resizeTask?.disarm();
164 this._scrollTask?.disarm();
169 this._resizeTask?.disarm();
170 this._scrollTask?.disarm();
174 * Gets the full page bounds for a full page screenshot.
176 * @returns { object }
177 * The device pixel ratio and a DOMRect of the scrollable content bounds.
179 * devicePixelRatio (float):
180 * The device pixel ratio of the screen
184 * The scroll top position for the content window.
187 * The scroll left position for the content window.
190 * The scroll width of the content window.
193 * The scroll height of the content window.
195 getFullPageBounds() {
196 let doc = this.document.documentElement;
197 let rect = new DOMRect(
203 let devicePixelRatio = this.document.ownerGlobal.devicePixelRatio;
204 return { devicePixelRatio, rect };
208 * Gets the visible page bounds for a visible screenshot.
210 * @returns { object }
211 * The device pixel ratio and a DOMRect of the current visible
214 * devicePixelRatio (float):
215 * The device pixel ratio of the screen
219 * The top position for the content window.
222 * The left position for the content window.
225 * The width of the content window.
228 * The height of the content window.
231 let doc = this.document.documentElement;
232 let rect = new DOMRect(
238 let devicePixelRatio = this.document.ownerGlobal.devicePixelRatio;
239 return { devicePixelRatio, rect };