forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			241 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			241 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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/. */
 | |
| /* eslint-env mozilla/browser-window */
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
 | |
| 
 | |
| const lazy = {};
 | |
| 
 | |
| ChromeUtils.defineESModuleGetters(lazy, {
 | |
|   ScreenshotsOverlayChild:
 | |
|     "resource:///modules/ScreenshotsOverlayChild.sys.mjs",
 | |
| });
 | |
| 
 | |
| XPCOMUtils.defineLazyModuleGetters(lazy, {
 | |
|   DeferredTask: "resource://gre/modules/DeferredTask.jsm",
 | |
| });
 | |
| 
 | |
| export class ScreenshotsComponentChild extends JSWindowActorChild {
 | |
|   receiveMessage(message) {
 | |
|     switch (message.name) {
 | |
|       case "Screenshots:ShowOverlay":
 | |
|         return this.startScreenshotsOverlay();
 | |
|       case "Screenshots:HideOverlay":
 | |
|         return this.endScreenshotsOverlay();
 | |
|       case "Screenshots:getFullPageBounds":
 | |
|         return this.getFullPageBounds();
 | |
|       case "Screenshots:getVisibleBounds":
 | |
|         return this.getVisibleBounds();
 | |
|     }
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   handleEvent(event) {
 | |
|     switch (event.type) {
 | |
|       case "keydown":
 | |
|         if (event.key === "Escape") {
 | |
|           this.requestCancelScreenshot();
 | |
|         }
 | |
|         break;
 | |
|       case "beforeunload":
 | |
|         this.requestCancelScreenshot();
 | |
|         break;
 | |
|       case "resize":
 | |
|         if (!this._resizeTask && this._overlay?._initialized) {
 | |
|           this._resizeTask = new lazy.DeferredTask(() => {
 | |
|             this._overlay.updateScreenshotsSize("resize");
 | |
|           }, 16);
 | |
|         }
 | |
|         this._resizeTask.arm();
 | |
|         break;
 | |
|       case "scroll":
 | |
|         if (!this._scrollTask && this._overlay?._initialized) {
 | |
|           this._scrollTask = new lazy.DeferredTask(() => {
 | |
|             this._overlay.updateScreenshotsSize("scroll");
 | |
|           }, 16);
 | |
|         }
 | |
|         this._scrollTask.arm();
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Send a request to cancel the screenshot to the parent process
 | |
|    */
 | |
|   requestCancelScreenshot() {
 | |
|     this.sendAsyncMessage("Screenshots:CancelScreenshot", null);
 | |
|   }
 | |
| 
 | |
|   requestCopyScreenshot(box) {
 | |
|     this.sendAsyncMessage("Screenshots:CopyScreenshot", box);
 | |
|   }
 | |
| 
 | |
|   requestDownloadScreenshot(box) {
 | |
|     this.sendAsyncMessage("Screenshots:DownloadScreenshot", {
 | |
|       title: this.getTitle(),
 | |
|       downloadBox: box,
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   getTitle() {
 | |
|     return this.document.title;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Resolves when the document is ready to have an overlay injected into it.
 | |
|    *
 | |
|    * @returns {Promise}
 | |
|    * @resolves {Boolean} true when document is ready or rejects
 | |
|    */
 | |
|   documentIsReady() {
 | |
|     const document = this.document;
 | |
|     // Some pages take ages to finish loading - if at all.
 | |
|     // We want to respond to enable the screenshots UI as soon that is possible
 | |
|     function readyEnough() {
 | |
|       return (
 | |
|         document.readyState !== "uninitialized" && document.documentElement
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     if (readyEnough()) {
 | |
|       return Promise.resolve();
 | |
|     }
 | |
|     return new Promise((resolve, reject) => {
 | |
|       function onChange(event) {
 | |
|         if (event.type === "pagehide") {
 | |
|           document.removeEventListener("readystatechange", onChange);
 | |
|           this.contentWindow.removeEventListener("pagehide", onChange);
 | |
|           reject(new Error("document unloaded before it was ready"));
 | |
|         } else if (readyEnough()) {
 | |
|           document.removeEventListener("readystatechange", onChange);
 | |
|           this.contentWindow.removeEventListener("pagehide", onChange);
 | |
|           resolve();
 | |
|         }
 | |
|       }
 | |
|       document.addEventListener("readystatechange", onChange);
 | |
|       this.contentWindow.addEventListener("pagehide", onChange, { once: true });
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Wait until the document is ready and then show the screenshots overlay
 | |
|    *
 | |
|    * @returns {Boolean} true when document is ready and the overlay is shown
 | |
|    * otherwise false
 | |
|    */
 | |
|   async startScreenshotsOverlay() {
 | |
|     try {
 | |
|       await this.documentIsReady();
 | |
|     } catch (ex) {
 | |
|       console.warn(`ScreenshotsComponentChild: ${ex.message}`);
 | |
|       return false;
 | |
|     }
 | |
|     await this.documentIsReady();
 | |
|     let overlay =
 | |
|       this._overlay ||
 | |
|       (this._overlay = new lazy.ScreenshotsOverlayChild.AnonymousContentOverlay(
 | |
|         this.document,
 | |
|         this
 | |
|       ));
 | |
|     this.document.addEventListener("keydown", this);
 | |
|     this.document.ownerGlobal.addEventListener("beforeunload", this);
 | |
|     this.contentWindow.addEventListener("resize", this);
 | |
|     this.contentWindow.addEventListener("scroll", this);
 | |
|     overlay.initialize();
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Remove the screenshots overlay.
 | |
|    *
 | |
|    * @returns {Boolean}
 | |
|    *   true when the overlay has been removed otherwise false
 | |
|    */
 | |
|   endScreenshotsOverlay() {
 | |
|     this.document.removeEventListener("keydown", this);
 | |
|     this.document.ownerGlobal.removeEventListener("beforeunload", this);
 | |
|     this.contentWindow.removeEventListener("resize", this);
 | |
|     this.contentWindow.removeEventListener("scroll", this);
 | |
|     this._overlay?.tearDown();
 | |
|     this._resizeTask?.disarm();
 | |
|     this._scrollTask?.disarm();
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   didDestroy() {
 | |
|     this._resizeTask?.disarm();
 | |
|     this._scrollTask?.disarm();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Gets the full page bounds for a full page screenshot.
 | |
|    *
 | |
|    * @returns { object }
 | |
|    *   The device pixel ratio and a DOMRect of the scrollable content bounds.
 | |
|    *
 | |
|    *   devicePixelRatio (float):
 | |
|    *      The device pixel ratio of the screen
 | |
|    *
 | |
|    *   rect (object):
 | |
|    *      top (int):
 | |
|    *        The scroll top position for the content window.
 | |
|    *
 | |
|    *      left (int):
 | |
|    *        The scroll left position for the content window.
 | |
|    *
 | |
|    *      width (int):
 | |
|    *        The scroll width of the content window.
 | |
|    *
 | |
|    *      height (int):
 | |
|    *        The scroll height of the content window.
 | |
|    */
 | |
|   getFullPageBounds() {
 | |
|     let doc = this.document.documentElement;
 | |
|     let rect = new DOMRect(
 | |
|       doc.clientLeft,
 | |
|       doc.clientTop,
 | |
|       doc.scrollWidth,
 | |
|       doc.scrollHeight
 | |
|     );
 | |
|     let devicePixelRatio = this.document.ownerGlobal.devicePixelRatio;
 | |
|     return { devicePixelRatio, rect };
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Gets the visible page bounds for a visible screenshot.
 | |
|    *
 | |
|    * @returns { object }
 | |
|    *   The device pixel ratio and a DOMRect of the current visible
 | |
|    *   content bounds.
 | |
|    *
 | |
|    *   devicePixelRatio (float):
 | |
|    *      The device pixel ratio of the screen
 | |
|    *
 | |
|    *   rect (object):
 | |
|    *      top (int):
 | |
|    *        The top position for the content window.
 | |
|    *
 | |
|    *      left (int):
 | |
|    *        The left position for the content window.
 | |
|    *
 | |
|    *      width (int):
 | |
|    *        The width of the content window.
 | |
|    *
 | |
|    *      height (int):
 | |
|    *        The height of the content window.
 | |
|    */
 | |
|   getVisibleBounds() {
 | |
|     let doc = this.document.documentElement;
 | |
|     let rect = new DOMRect(
 | |
|       doc.scrollLeft,
 | |
|       doc.scrollTop,
 | |
|       doc.clientWidth,
 | |
|       doc.clientHeight
 | |
|     );
 | |
|     let devicePixelRatio = this.document.ownerGlobal.devicePixelRatio;
 | |
|     return { devicePixelRatio, rect };
 | |
|   }
 | |
| }
 | 
