Bug 1791785 - When dumping symbols never emit INLINE_ORIGIN directives with an empty...
[gecko.git] / browser / actors / ScreenshotsComponentChild.sys.mjs
blobd5e92e3b4a53b8d419469b657a4ab7a3b01ab630
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 */
6 "use strict";
8 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
10 const lazy = {};
12 ChromeUtils.defineESModuleGetters(lazy, {
13   ScreenshotsOverlayChild:
14     "resource:///modules/ScreenshotsOverlayChild.sys.mjs",
15 });
17 XPCOMUtils.defineLazyModuleGetters(lazy, {
18   DeferredTask: "resource://gre/modules/DeferredTask.jsm",
19 });
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();
32     }
33     return null;
34   }
36   handleEvent(event) {
37     switch (event.type) {
38       case "keydown":
39         if (event.key === "Escape") {
40           this.requestCancelScreenshot();
41         }
42         break;
43       case "beforeunload":
44         this.requestCancelScreenshot();
45         break;
46       case "resize":
47         if (!this._resizeTask && this._overlay?._initialized) {
48           this._resizeTask = new lazy.DeferredTask(() => {
49             this._overlay.updateScreenshotsSize("resize");
50           }, 16);
51         }
52         this._resizeTask.arm();
53         break;
54       case "scroll":
55         if (!this._scrollTask && this._overlay?._initialized) {
56           this._scrollTask = new lazy.DeferredTask(() => {
57             this._overlay.updateScreenshotsSize("scroll");
58           }, 16);
59         }
60         this._scrollTask.arm();
61         break;
62     }
63   }
65   /**
66    * Send a request to cancel the screenshot to the parent process
67    */
68   requestCancelScreenshot() {
69     this.sendAsyncMessage("Screenshots:CancelScreenshot", null);
70   }
72   requestCopyScreenshot(box) {
73     this.sendAsyncMessage("Screenshots:CopyScreenshot", box);
74   }
76   requestDownloadScreenshot(box) {
77     this.sendAsyncMessage("Screenshots:DownloadScreenshot", {
78       title: this.getTitle(),
79       downloadBox: box,
80     });
81   }
83   getTitle() {
84     return this.document.title;
85   }
87   /**
88    * Resolves when the document is ready to have an overlay injected into it.
89    *
90    * @returns {Promise}
91    * @resolves {Boolean} true when document is ready or rejects
92    */
93   documentIsReady() {
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() {
98       return (
99         document.readyState !== "uninitialized" && document.documentElement
100       );
101     }
103     if (readyEnough()) {
104       return Promise.resolve();
105     }
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);
115           resolve();
116         }
117       }
118       document.addEventListener("readystatechange", onChange);
119       this.contentWindow.addEventListener("pagehide", onChange, { once: true });
120     });
121   }
123   /**
124    * Wait until the document is ready and then show the screenshots overlay
125    *
126    * @returns {Boolean} true when document is ready and the overlay is shown
127    * otherwise false
128    */
129   async startScreenshotsOverlay() {
130     try {
131       await this.documentIsReady();
132     } catch (ex) {
133       console.warn(`ScreenshotsComponentChild: ${ex.message}`);
134       return false;
135     }
136     await this.documentIsReady();
137     let overlay =
138       this._overlay ||
139       (this._overlay = new lazy.ScreenshotsOverlayChild.AnonymousContentOverlay(
140         this.document,
141         this
142       ));
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();
148     return true;
149   }
151   /**
152    * Remove the screenshots overlay.
153    *
154    * @returns {Boolean}
155    *   true when the overlay has been removed otherwise false
156    */
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();
165     return true;
166   }
168   didDestroy() {
169     this._resizeTask?.disarm();
170     this._scrollTask?.disarm();
171   }
173   /**
174    * Gets the full page bounds for a full page screenshot.
175    *
176    * @returns { object }
177    *   The device pixel ratio and a DOMRect of the scrollable content bounds.
178    *
179    *   devicePixelRatio (float):
180    *      The device pixel ratio of the screen
181    *
182    *   rect (object):
183    *      top (int):
184    *        The scroll top position for the content window.
185    *
186    *      left (int):
187    *        The scroll left position for the content window.
188    *
189    *      width (int):
190    *        The scroll width of the content window.
191    *
192    *      height (int):
193    *        The scroll height of the content window.
194    */
195   getFullPageBounds() {
196     let doc = this.document.documentElement;
197     let rect = new DOMRect(
198       doc.clientLeft,
199       doc.clientTop,
200       doc.scrollWidth,
201       doc.scrollHeight
202     );
203     let devicePixelRatio = this.document.ownerGlobal.devicePixelRatio;
204     return { devicePixelRatio, rect };
205   }
207   /**
208    * Gets the visible page bounds for a visible screenshot.
209    *
210    * @returns { object }
211    *   The device pixel ratio and a DOMRect of the current visible
212    *   content bounds.
213    *
214    *   devicePixelRatio (float):
215    *      The device pixel ratio of the screen
216    *
217    *   rect (object):
218    *      top (int):
219    *        The top position for the content window.
220    *
221    *      left (int):
222    *        The left position for the content window.
223    *
224    *      width (int):
225    *        The width of the content window.
226    *
227    *      height (int):
228    *        The height of the content window.
229    */
230   getVisibleBounds() {
231     let doc = this.document.documentElement;
232     let rect = new DOMRect(
233       doc.scrollLeft,
234       doc.scrollTop,
235       doc.clientWidth,
236       doc.clientHeight
237     );
238     let devicePixelRatio = this.document.ownerGlobal.devicePixelRatio;
239     return { devicePixelRatio, rect };
240   }