forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			601 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			601 lines
		
	
	
	
		
			19 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/. */
 | |
| 
 | |
| import { getFilename } from "chrome://browser/content/screenshots/fileHelpers.mjs";
 | |
| import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
 | |
| 
 | |
| const lazy = {};
 | |
| 
 | |
| ChromeUtils.defineESModuleGetters(lazy, {
 | |
|   Downloads: "resource://gre/modules/Downloads.sys.mjs",
 | |
|   FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
 | |
|   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
 | |
| });
 | |
| 
 | |
| XPCOMUtils.defineLazyServiceGetters(lazy, {
 | |
|   AlertsService: ["@mozilla.org/alerts-service;1", "nsIAlertsService"],
 | |
| });
 | |
| 
 | |
| ChromeUtils.defineLazyGetter(lazy, "screenshotsLocalization", () => {
 | |
|   return new Localization(["browser/screenshots.ftl"], true);
 | |
| });
 | |
| 
 | |
| const PanelPosition = "bottomright topright";
 | |
| const PanelOffsetX = -33;
 | |
| const PanelOffsetY = -8;
 | |
| // The max dimension for a canvas is defined https://searchfox.org/mozilla-central/rev/f40d29a11f2eb4685256b59934e637012ea6fb78/gfx/cairo/cairo/src/cairo-image-surface.c#62.
 | |
| // The max number of pixels for a canvas is 124925329 or 11177 x 11177.
 | |
| // We have to limit screenshots to these dimensions otherwise it will cause an error.
 | |
| const MAX_CAPTURE_DIMENSION = 32767;
 | |
| const MAX_CAPTURE_AREA = 124925329;
 | |
| 
 | |
| export class ScreenshotsComponentParent extends JSWindowActorParent {
 | |
|   async receiveMessage(message) {
 | |
|     let browser = message.target.browsingContext.topFrameElement;
 | |
|     let region, title;
 | |
|     switch (message.name) {
 | |
|       case "Screenshots:CancelScreenshot":
 | |
|         await ScreenshotsUtils.closePanel(browser);
 | |
|         let { reason } = message.data;
 | |
|         ScreenshotsUtils.recordTelemetryEvent("canceled", reason, {});
 | |
|         break;
 | |
|       case "Screenshots:CopyScreenshot":
 | |
|         await ScreenshotsUtils.closePanel(browser);
 | |
|         ({ region } = message.data);
 | |
|         ScreenshotsUtils.copyScreenshotFromRegion(region, browser);
 | |
|         break;
 | |
|       case "Screenshots:DownloadScreenshot":
 | |
|         await ScreenshotsUtils.closePanel(browser);
 | |
|         ({ title, region } = message.data);
 | |
|         ScreenshotsUtils.downloadScreenshotFromRegion(title, region, browser);
 | |
|         break;
 | |
|       case "Screenshots:ShowPanel":
 | |
|         ScreenshotsUtils.openPanel(browser);
 | |
|         break;
 | |
|       case "Screenshots:HidePanel":
 | |
|         ScreenshotsUtils.closePanel(browser);
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   didDestroy() {
 | |
|     // When restoring a crashed tab the browser is null
 | |
|     let browser = this.browsingContext.topFrameElement;
 | |
|     if (browser) {
 | |
|       ScreenshotsUtils.closePanel(browser);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| export var ScreenshotsUtils = {
 | |
|   initialized: false,
 | |
| 
 | |
|   initialize() {
 | |
|     if (!this.initialized) {
 | |
|       if (
 | |
|         !Services.prefs.getBoolPref(
 | |
|           "screenshots.browser.component.enabled",
 | |
|           false
 | |
|         )
 | |
|       ) {
 | |
|         return;
 | |
|       }
 | |
|       Services.telemetry.setEventRecordingEnabled("screenshots", true);
 | |
|       Services.obs.addObserver(this, "menuitem-screenshot");
 | |
|       Services.obs.addObserver(this, "screenshots-take-screenshot");
 | |
|       this.initialized = true;
 | |
|       if (Cu.isInAutomation) {
 | |
|         Services.obs.notifyObservers(null, "screenshots-component-initialized");
 | |
|       }
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   uninitialize() {
 | |
|     if (this.initialized) {
 | |
|       Services.obs.removeObserver(this, "menuitem-screenshot");
 | |
|       Services.obs.removeObserver(this, "screenshots-take-screenshot");
 | |
|       this.initialized = false;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   handleEvent(event) {
 | |
|     // We need to add back Escape to hide behavior as we have set noautohide="true"
 | |
|     if (event.type === "keydown" && event.key === "Escape") {
 | |
|       this.closePanel(event.view.gBrowser.selectedBrowser, true);
 | |
|       this.recordTelemetryEvent("canceled", "escape", {});
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   observe(subj, topic, data) {
 | |
|     let { gBrowser } = subj;
 | |
|     let browser = gBrowser.selectedBrowser;
 | |
| 
 | |
|     switch (topic) {
 | |
|       case "menuitem-screenshot":
 | |
|         let success = this.closeDialogBox(browser);
 | |
|         if (!success || data === "retry") {
 | |
|           // only toggle the buttons if no dialog box is found because
 | |
|           // if dialog box is found then the buttons are hidden and we return early
 | |
|           // else no dialog box is found and we need to toggle the buttons
 | |
|           // or if retry because the dialog box was closed and we need to show the panel
 | |
|           this.togglePanelAndOverlay(browser, data);
 | |
|         }
 | |
|         break;
 | |
|       case "screenshots-take-screenshot":
 | |
|         // need to close the preview because screenshot was taken
 | |
|         this.closePanel(browser, true);
 | |
| 
 | |
|         // init UI as a tab dialog box
 | |
|         let dialogBox = gBrowser.getTabDialogBox(browser);
 | |
| 
 | |
|         let { dialog } = dialogBox.open(
 | |
|           `chrome://browser/content/screenshots/screenshots.html?browsingContextId=${browser.browsingContext.id}`,
 | |
|           {
 | |
|             features: "resizable=no",
 | |
|             sizeTo: "available",
 | |
|             allowDuplicateDialogs: false,
 | |
|           }
 | |
|         );
 | |
|         this.doScreenshot(browser, dialog, data);
 | |
|     }
 | |
|     return null;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Notify screenshots when screenshot command is used.
 | |
|    * @param window The current window the screenshot command was used.
 | |
|    * @param type The type of screenshot taken. Used for telemetry.
 | |
|    */
 | |
|   notify(window, type) {
 | |
|     if (Services.prefs.getBoolPref("screenshots.browser.component.enabled")) {
 | |
|       Services.obs.notifyObservers(
 | |
|         window.event.currentTarget.ownerGlobal,
 | |
|         "menuitem-screenshot",
 | |
|         type
 | |
|       );
 | |
|     } else {
 | |
|       Services.obs.notifyObservers(null, "menuitem-screenshot-extension", type);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Creates/gets and returns a Screenshots actor.
 | |
|    * @param browser The current browser.
 | |
|    * @returns JSWindowActor The screenshot actor.
 | |
|    */
 | |
|   getActor(browser) {
 | |
|     let actor = browser.browsingContext.currentWindowGlobal.getActor(
 | |
|       "ScreenshotsComponent"
 | |
|     );
 | |
|     return actor;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Returns the buttons panel. If the panel doesn't exist, create the panel.
 | |
|    * @param browser The current browser
 | |
|    * @returns The buttons panel
 | |
|    */
 | |
|   panelForBrowser(browser) {
 | |
|     let doc = browser.ownerDocument;
 | |
|     let buttonsPanel = doc.getElementById("screenshotsPagePanel");
 | |
|     if (!buttonsPanel) {
 | |
|       let template = doc.getElementById("screenshotsPagePanelTemplate");
 | |
|       let clone = template.content.cloneNode(true);
 | |
|       template.replaceWith(clone);
 | |
|       buttonsPanel = this.panelForBrowser(browser);
 | |
|     }
 | |
|     return buttonsPanel;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Open the buttons panel.
 | |
|    * @param browser The current browser
 | |
|    */
 | |
|   async openPanel(browser) {
 | |
|     let buttonsPanel = this.panelForBrowser(browser);
 | |
|     if (buttonsPanel.state !== "closed") {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     buttonsPanel.ownerDocument.addEventListener("keydown", this);
 | |
|     let anchor = browser.ownerDocument.getElementById("navigator-toolbox");
 | |
|     buttonsPanel.openPopup(anchor, PanelPosition, PanelOffsetX, PanelOffsetY);
 | |
| 
 | |
|     if (buttonsPanel.state !== "open") {
 | |
|       await new Promise(resolve => {
 | |
|         buttonsPanel.addEventListener("popupshown", resolve, { once: true });
 | |
|       });
 | |
|     }
 | |
|     buttonsPanel
 | |
|       .querySelector("screenshots-buttons")
 | |
|       .focusFirst({ focusVisible: true });
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Close the panel and call child actor to close the overlay
 | |
|    * @param browser The current browser
 | |
|    * @param {bool} closeOverlay Whether or not to
 | |
|    * send a message to the child to close the overly.
 | |
|    * Defaults to false. Will be false when called from didDestroy.
 | |
|    */
 | |
|   async closePanel(browser, closeOverlay = false) {
 | |
|     let buttonsPanel = this.panelForBrowser(browser);
 | |
|     if (buttonsPanel && buttonsPanel.state !== "closed") {
 | |
|       buttonsPanel.hidePopup();
 | |
|     }
 | |
|     buttonsPanel.ownerDocument.removeEventListener("keydown", this);
 | |
| 
 | |
|     if (closeOverlay) {
 | |
|       let actor = this.getActor(browser);
 | |
|       await actor.sendQuery("Screenshots:HideOverlay");
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * If the buttons panel exists and is open we will hide both the panel
 | |
|    * and the overlay. If the overlay is showing, we will hide the overlay.
 | |
|    * Otherwise create or display the buttons.
 | |
|    * @param browser The current browser.
 | |
|    */
 | |
|   async togglePanelAndOverlay(browser, data) {
 | |
|     let buttonsPanel = this.panelForBrowser(browser);
 | |
|     let isOverlayShowing = await this.getActor(browser).sendQuery(
 | |
|       "Screenshots:isOverlayShowing"
 | |
|     );
 | |
| 
 | |
|     data = data === "retry" ? "preview_retry" : data;
 | |
|     if (buttonsPanel && (isOverlayShowing || buttonsPanel.state !== "closed")) {
 | |
|       this.recordTelemetryEvent("canceled", data, {});
 | |
|       return this.closePanel(browser, true);
 | |
|     }
 | |
|     let actor = this.getActor(browser);
 | |
|     actor.sendAsyncMessage("Screenshots:ShowOverlay");
 | |
|     this.recordTelemetryEvent("started", data, {});
 | |
|     return this.openPanel(browser);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Gets the screenshots dialog box
 | |
|    * @param browser The selected browser
 | |
|    * @returns Screenshots dialog box if it exists otherwise null
 | |
|    */
 | |
|   getDialog(browser) {
 | |
|     let currTabDialogBox = browser.tabDialogBox;
 | |
|     let browserContextId = browser.browsingContext.id;
 | |
|     if (currTabDialogBox) {
 | |
|       currTabDialogBox.getTabDialogManager();
 | |
|       let manager = currTabDialogBox.getTabDialogManager();
 | |
|       let dialogs = manager.hasDialogs && manager.dialogs;
 | |
|       if (dialogs.length) {
 | |
|         for (let dialog of dialogs) {
 | |
|           if (
 | |
|             dialog._openedURL.endsWith(
 | |
|               `browsingContextId=${browserContextId}`
 | |
|             ) &&
 | |
|             dialog._openedURL.includes("screenshots.html")
 | |
|           ) {
 | |
|             return dialog;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     return null;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Closes the dialog box it it exists
 | |
|    * @param browser The selected browser
 | |
|    */
 | |
|   closeDialogBox(browser) {
 | |
|     let dialog = this.getDialog(browser);
 | |
|     if (dialog) {
 | |
|       dialog.close();
 | |
|       return true;
 | |
|     }
 | |
|     return false;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Gets the screenshots button if it is visible, otherwise it will get the
 | |
|    * element that the screenshots button is nested under. If the screenshots
 | |
|    * button doesn't exist then we will default to the navigator toolbox.
 | |
|    * @param browser The selected browser
 | |
|    * @returns The anchor element for the ConfirmationHint
 | |
|    */
 | |
|   getWidgetAnchor(browser) {
 | |
|     let window = browser.ownerGlobal;
 | |
|     let widgetGroup = window.CustomizableUI.getWidget("screenshot-button");
 | |
|     let widget = widgetGroup?.forWindow(window);
 | |
|     let anchor = widget?.anchor;
 | |
| 
 | |
|     // Check if the anchor exists and is visible
 | |
|     if (!anchor || !window.isElementVisible(anchor.parentNode)) {
 | |
|       anchor = browser.ownerDocument.getElementById("navigator-toolbox");
 | |
|     }
 | |
|     return anchor;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Indicate that the screenshot has been copied via ConfirmationHint.
 | |
|    * @param browser The selected browser
 | |
|    */
 | |
|   showCopiedConfirmationHint(browser) {
 | |
|     let anchor = this.getWidgetAnchor(browser);
 | |
| 
 | |
|     browser.ownerGlobal.ConfirmationHint.show(
 | |
|       anchor,
 | |
|       "confirmation-hint-screenshot-copied"
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Gets the full page bounds from the screenshots child actor.
 | |
|    * @param browser The current browser.
 | |
|    * @returns { object }
 | |
|    *    Contains the full page bounds from the screenshots child actor.
 | |
|    */
 | |
|   fetchFullPageBounds(browser) {
 | |
|     let actor = this.getActor(browser);
 | |
|     return actor.sendQuery("Screenshots:getFullPageBounds");
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Gets the visible bounds from the screenshots child actor.
 | |
|    * @param browser The current browser.
 | |
|    * @returns { object }
 | |
|    *    Contains the visible bounds from the screenshots child actor.
 | |
|    */
 | |
|   fetchVisibleBounds(browser) {
 | |
|     let actor = this.getActor(browser);
 | |
|     return actor.sendQuery("Screenshots:getVisibleBounds");
 | |
|   },
 | |
| 
 | |
|   showAlertMessage(title, message) {
 | |
|     lazy.AlertsService.showAlertNotification(null, title, message);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * The max one dimesion for a canvas is 32767 and the max canvas area is
 | |
|    * 124925329. If the width or height is greater than 32767 we will crop the
 | |
|    * screenshot to the max width. If the area is still too large for the canvas
 | |
|    * we will adjust the height so we can successfully capture the screenshot.
 | |
|    * @param {Object} rect The dimensions of the screenshot. The rect will be
 | |
|    * modified in place
 | |
|    */
 | |
|   cropScreenshotRectIfNeeded(rect) {
 | |
|     let cropped = false;
 | |
|     let width = rect.width * rect.devicePixelRatio;
 | |
|     let height = rect.height * rect.devicePixelRatio;
 | |
| 
 | |
|     if (width > MAX_CAPTURE_DIMENSION) {
 | |
|       width = MAX_CAPTURE_DIMENSION;
 | |
|       cropped = true;
 | |
|     }
 | |
|     if (height > MAX_CAPTURE_DIMENSION) {
 | |
|       height = MAX_CAPTURE_DIMENSION;
 | |
|       cropped = true;
 | |
|     }
 | |
|     if (width * height > MAX_CAPTURE_AREA) {
 | |
|       height = Math.floor(MAX_CAPTURE_AREA / width);
 | |
|       cropped = true;
 | |
|     }
 | |
| 
 | |
|     rect.width = Math.floor(width / rect.devicePixelRatio);
 | |
|     rect.height = Math.floor(height / rect.devicePixelRatio);
 | |
| 
 | |
|     if (cropped) {
 | |
|       let [errorTitle, errorMessage] =
 | |
|         lazy.screenshotsLocalization.formatMessagesSync([
 | |
|           { id: "screenshots-too-large-error-title" },
 | |
|           { id: "screenshots-too-large-error-details" },
 | |
|         ]);
 | |
|       this.showAlertMessage(errorTitle.value, errorMessage.value);
 | |
|       this.recordTelemetryEvent("failed", "screenshot_too_large", null);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Add screenshot-ui to the dialog box and then take the screenshot
 | |
|    * @param browser The current browser.
 | |
|    * @param dialog The dialog box to show the screenshot preview.
 | |
|    * @param type The type of screenshot taken.
 | |
|    */
 | |
|   async doScreenshot(browser, dialog, type) {
 | |
|     await dialog._dialogReady;
 | |
|     let screenshotsUI =
 | |
|       dialog._frame.contentDocument.createElement("screenshots-ui");
 | |
|     dialog._frame.contentDocument.body.appendChild(screenshotsUI);
 | |
| 
 | |
|     let rect;
 | |
|     if (type === "full-page") {
 | |
|       rect = await this.fetchFullPageBounds(browser);
 | |
|       type = "full_page";
 | |
|     } else {
 | |
|       rect = await this.fetchVisibleBounds(browser);
 | |
|     }
 | |
|     this.recordTelemetryEvent("selected", type, {});
 | |
|     return this.takeScreenshot(browser, dialog, rect);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Take the screenshot and add the image to the dialog box
 | |
|    * @param browser The current browser.
 | |
|    * @param dialog The dialog box to show the screenshot preview.
 | |
|    * @param rect DOMRect containing bounds of the screenshot.
 | |
|    */
 | |
|   async takeScreenshot(browser, dialog, rect) {
 | |
|     let { canvas, snapshot } = await this.createCanvas(rect, browser);
 | |
| 
 | |
|     let newImg = dialog._frame.contentDocument.createElement("img");
 | |
|     let url = canvas.toDataURL();
 | |
| 
 | |
|     newImg.id = "placeholder-image";
 | |
| 
 | |
|     newImg.src = url;
 | |
|     dialog._frame.contentDocument
 | |
|       .getElementById("preview-image-div")
 | |
|       .appendChild(newImg);
 | |
| 
 | |
|     if (Cu.isInAutomation) {
 | |
|       Services.obs.notifyObservers(null, "screenshots-preview-ready");
 | |
|     }
 | |
| 
 | |
|     snapshot.close();
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Creates a canvas and draws a snapshot of the screenshot on the canvas
 | |
|    * @param region The bounds of screenshots
 | |
|    * @param browser The current browser
 | |
|    * @returns The canvas and snapshot in an object
 | |
|    */
 | |
|   async createCanvas(region, browser) {
 | |
|     this.cropScreenshotRectIfNeeded(region);
 | |
| 
 | |
|     let rect = new DOMRect(
 | |
|       region.left,
 | |
|       region.top,
 | |
|       region.width,
 | |
|       region.height
 | |
|     );
 | |
|     let { devicePixelRatio } = region;
 | |
| 
 | |
|     let browsingContext = BrowsingContext.get(browser.browsingContext.id);
 | |
| 
 | |
|     let snapshot = await browsingContext.currentWindowGlobal.drawSnapshot(
 | |
|       rect,
 | |
|       devicePixelRatio,
 | |
|       "rgb(255,255,255)"
 | |
|     );
 | |
| 
 | |
|     let canvas = browser.ownerDocument.createElementNS(
 | |
|       "http://www.w3.org/1999/xhtml",
 | |
|       "html:canvas"
 | |
|     );
 | |
|     let context = canvas.getContext("2d");
 | |
| 
 | |
|     canvas.width = snapshot.width;
 | |
|     canvas.height = snapshot.height;
 | |
| 
 | |
|     context.drawImage(snapshot, 0, 0);
 | |
| 
 | |
|     return { canvas, snapshot };
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Copy the screenshot
 | |
|    * @param region The bounds of the screenshots
 | |
|    * @param browser The current browser
 | |
|    */
 | |
|   async copyScreenshotFromRegion(region, browser) {
 | |
|     let { canvas, snapshot } = await this.createCanvas(region, browser);
 | |
| 
 | |
|     let url = canvas.toDataURL();
 | |
| 
 | |
|     this.copyScreenshot(url, browser);
 | |
| 
 | |
|     snapshot.close();
 | |
| 
 | |
|     this.recordTelemetryEvent("copy", "overlay_copy", {});
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Copy the image to the clipboard
 | |
|    * @param dataUrl The image data
 | |
|    * @param browser The current browser
 | |
|    */
 | |
|   copyScreenshot(dataUrl, browser) {
 | |
|     // Guard against missing image data.
 | |
|     if (!dataUrl) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     const imageTools = Cc["@mozilla.org/image/tools;1"].getService(
 | |
|       Ci.imgITools
 | |
|     );
 | |
| 
 | |
|     const base64Data = dataUrl.replace("data:image/png;base64,", "");
 | |
| 
 | |
|     const image = atob(base64Data);
 | |
|     const imgDecoded = imageTools.decodeImageFromBuffer(
 | |
|       image,
 | |
|       image.length,
 | |
|       "image/png"
 | |
|     );
 | |
| 
 | |
|     const transferable = Cc[
 | |
|       "@mozilla.org/widget/transferable;1"
 | |
|     ].createInstance(Ci.nsITransferable);
 | |
|     transferable.init(null);
 | |
|     transferable.addDataFlavor("image/png");
 | |
|     transferable.setTransferData("image/png", imgDecoded);
 | |
| 
 | |
|     Services.clipboard.setData(
 | |
|       transferable,
 | |
|       null,
 | |
|       Services.clipboard.kGlobalClipboard
 | |
|     );
 | |
| 
 | |
|     this.showCopiedConfirmationHint(browser);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Download the screenshot
 | |
|    * @param title The title of the current page
 | |
|    * @param region The bounds of the screenshot
 | |
|    * @param browser The current browser
 | |
|    */
 | |
|   async downloadScreenshotFromRegion(title, region, browser) {
 | |
|     let { canvas, snapshot } = await this.createCanvas(region, browser);
 | |
| 
 | |
|     let dataUrl = canvas.toDataURL();
 | |
| 
 | |
|     await this.downloadScreenshot(title, dataUrl, browser);
 | |
| 
 | |
|     snapshot.close();
 | |
| 
 | |
|     this.recordTelemetryEvent("download", "overlay_download", {});
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Download the screenshot
 | |
|    * @param title The title of the current page or null and getFilename will get the title
 | |
|    * @param dataUrl The image data
 | |
|    * @param browser The current browser
 | |
|    */
 | |
|   async downloadScreenshot(title, dataUrl, browser) {
 | |
|     // Guard against missing image data.
 | |
|     if (!dataUrl) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     let filename = await getFilename(title, browser);
 | |
| 
 | |
|     const targetFile = new lazy.FileUtils.File(filename);
 | |
| 
 | |
|     // Create download and track its progress.
 | |
|     try {
 | |
|       const download = await lazy.Downloads.createDownload({
 | |
|         source: dataUrl,
 | |
|         target: targetFile,
 | |
|       });
 | |
| 
 | |
|       let isPrivate = lazy.PrivateBrowsingUtils.isWindowPrivate(
 | |
|         browser.ownerGlobal
 | |
|       );
 | |
|       const list = await lazy.Downloads.getList(
 | |
|         isPrivate ? lazy.Downloads.PRIVATE : lazy.Downloads.PUBLIC
 | |
|       );
 | |
|       // add the download to the download list in the Downloads list in the Browser UI
 | |
|       list.add(download);
 | |
| 
 | |
|       // Await successful completion of the save via the download manager
 | |
|       await download.start();
 | |
|     } catch (ex) {}
 | |
|   },
 | |
| 
 | |
|   recordTelemetryEvent(type, object, args) {
 | |
|     Services.telemetry.recordEvent("screenshots", type, object, null, args);
 | |
|   },
 | |
| };
 | 
