Bug 1908205 - Switch to OffscreenCanvas and blobs for screenshots. r=sfoster

Differential Revision: https://phabricator.services.mozilla.com/D216723
This commit is contained in:
Niklas Baumgardner
2024-08-22 18:36:48 +00:00
parent b0381f7136
commit ae2879abd5
3 changed files with 54 additions and 28 deletions

View File

@@ -497,6 +497,8 @@ export var ScreenshotsUtils = {
gBrowser.tabContainer.removeEventListener("TabSelect", this);
browser.ownerDocument.removeEventListener("keydown", this);
this.revokeBlobURL(browser);
this.browserToScreenshotsState.delete(browser);
if (Cu.isInAutomation) {
Services.obs.notifyObservers(null, "screenshots-exit");
@@ -948,6 +950,32 @@ export var ScreenshotsUtils = {
lazy.AlertsService.showAlertNotification(null, title, message);
},
/**
* Revoke the object url of the current browsers screenshot.
*
* @param {browser} browser The current browser
*/
revokeBlobURL(browser) {
let browserState = this.browserToScreenshotsState.get(browser);
if (browserState.blobURL) {
URL.revokeObjectURL(browserState.blobURL);
}
},
/**
* Set the blob url on the browser state so we can revoke on exit.
*
* @param {browser} browser The current browser
* @param {string} blobURL The object url for the screenshot
*/
setBlobURL(browser, blobURL) {
// We shouldn't already have a blob URL on the browser
// but let's revoke just in case.
this.revokeBlobURL(browser);
this.setPerBrowserState(browser, { blobURL });
},
/**
* The max dimension of any side of a canvas is 32767 and the max canvas area is
* 124925329. If the width or height is greater or equal to 32766 we will crop the
@@ -1017,7 +1045,7 @@ export var ScreenshotsUtils = {
});
let canvas = await this.createCanvas(rect, browser);
let url = canvas.toDataURL();
let blob = await canvas.convertToBlob();
let dialog = await this.openPreviewDialog(browser);
await dialog._dialogReady;
@@ -1025,7 +1053,9 @@ export var ScreenshotsUtils = {
"screenshots-preview"
);
screenshotsPreviewEl.previewImg.src = url;
let blobURL = URL.createObjectURL(blob);
this.setBlobURL(browser, blobURL);
screenshotsPreviewEl.previewImg.src = blobURL;
screenshotsPreviewEl.focusButton(lazy.SCREENSHOTS_LAST_SAVED_METHOD);
Services.prefs.setStringPref(
@@ -1060,15 +1090,12 @@ export var ScreenshotsUtils = {
let browsingContext = BrowsingContext.get(browser.browsingContext.id);
let canvas = browser.ownerDocument.createElementNS(
"http://www.w3.org/1999/xhtml",
"html:canvas"
let canvas = new OffscreenCanvas(
region.width * devicePixelRatio,
region.height * devicePixelRatio
);
let context = canvas.getContext("2d");
canvas.width = region.width * devicePixelRatio;
canvas.height = region.height * devicePixelRatio;
const snapshotSize = Math.floor(MAX_SNAPSHOT_DIMENSION * devicePixelRatio);
for (
@@ -1127,9 +1154,9 @@ export var ScreenshotsUtils = {
*/
async copyScreenshotFromRegion(region, browser) {
let canvas = await this.createCanvas(region, browser);
let url = canvas.toDataURL();
let blob = await canvas.convertToBlob();
await this.copyScreenshot(url, browser, {
await this.copyScreenshot(blob, browser, {
object: "overlay_copy",
});
},
@@ -1137,13 +1164,13 @@ export var ScreenshotsUtils = {
/**
* Copy the image to the clipboard
* This is called from the preview dialog
* @param dataUrl The image data
* @param blob The image data
* @param browser The current browser
* @param data Telemetry data
*/
async copyScreenshot(dataUrl, browser, data) {
async copyScreenshot(blob, browser, data) {
// Guard against missing image data.
if (!dataUrl) {
if (!blob) {
return;
}
@@ -1151,12 +1178,9 @@ export var ScreenshotsUtils = {
Ci.imgITools
);
const base64Data = dataUrl.replace("data:image/png;base64,", "");
const image = atob(base64Data);
const imgDecoded = imageTools.decodeImageFromBuffer(
image,
image.length,
let buffer = await blob.arrayBuffer();
const imgDecoded = imageTools.decodeImageFromArrayBuffer(
buffer,
"image/png"
);
@@ -1211,9 +1235,11 @@ export var ScreenshotsUtils = {
*/
async downloadScreenshotFromRegion(title, region, browser) {
let canvas = await this.createCanvas(region, browser);
let dataUrl = canvas.toDataURL();
let blob = await canvas.convertToBlob();
let blobURL = URL.createObjectURL(blob);
this.setBlobURL(browser, blobURL);
return this.downloadScreenshot(title, dataUrl, browser, {
return this.downloadScreenshot(title, blobURL, browser, {
object: "overlay_download",
});
},
@@ -1224,14 +1250,14 @@ export var ScreenshotsUtils = {
* will return true if the screenshot is downloaded successfully.
*
* @param title The title of the current page or null and getFilename will get the title
* @param dataUrl The image data
* @param blobURL The image data
* @param browser The current browser
* @param data Telemetry data
* @returns {Promise<boolean>} true if the downnload succeeded, otherwise false
*/
async downloadScreenshot(title, dataUrl, browser, data) {
async downloadScreenshot(title, blobURL, browser, data) {
// Guard against missing image data.
if (!dataUrl) {
if (!blobURL) {
return false;
}
@@ -1246,7 +1272,7 @@ export var ScreenshotsUtils = {
// Create download and track its progress.
try {
const download = await lazy.Downloads.createDownload({
source: dataUrl,
source: blobURL,
target: targetFile,
});

View File

@@ -8,7 +8,7 @@
<title></title>
<meta
http-equiv="Content-Security-Policy"
content="default-src chrome:;img-src data:; object-src 'none'"
content="default-src chrome: blob:;img-src blob:; object-src 'none'"
/>
<link rel="localization" href="browser/screenshots.ftl" />

View File

@@ -74,7 +74,6 @@ class ScreenshotsPreview extends MozLitElement {
close() {
window.removeEventListener("keydown", this, true);
URL.revokeObjectURL(this.previewImg.src);
window.close();
}
@@ -205,7 +204,8 @@ class ScreenshotsPreview extends MozLitElement {
// Wait for the image to be loaded before we copy it
let imageSrc = await this.imageLoadedPromise();
await lazy.ScreenshotsUtils.copyScreenshot(imageSrc, this.openerBrowser, {
let blob = await fetch(imageSrc).then(r => r.blob());
await lazy.ScreenshotsUtils.copyScreenshot(blob, this.openerBrowser, {
object: "preview_copy",
});
this.close();