mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 04:09:50 +00:00
Bug 1636508 - Make tabs.captureTab compatible with Fission r=mattwoodrow,robwu,geckoview-reviewers,agi
Also fix WindowGlobalParent.drawSnapshot() to render the currently visible viewport when called with a null rect, and clarify the webidl comment. Differential Revision: https://phabricator.services.mozilla.com/D87971
This commit is contained in:
parent
aff64e4ea9
commit
ccc7783ad3
@ -36,6 +36,7 @@ support-files =
|
||||
file_title.html
|
||||
file_indexedDB.html
|
||||
file_serviceWorker.html
|
||||
file_green.html
|
||||
webNav_createdTarget.html
|
||||
webNav_createdTargetSource.html
|
||||
webNav_createdTargetSource_subframe.html
|
||||
|
@ -2,35 +2,13 @@
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
async function runTest(options) {
|
||||
options.neutral = [0xaa, 0xaa, 0xaa];
|
||||
|
||||
let html = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head><meta charset="UTF-8"></head>
|
||||
<body style="background-color: rgb(${options.color})">
|
||||
<!-- Fill most of the image with a neutral color to test edge-to-edge scaling. -->
|
||||
<div style="position: absolute;
|
||||
left: 2px;
|
||||
right: 2px;
|
||||
top: 2px;
|
||||
bottom: 2px;
|
||||
background: rgb(${options.neutral});"></div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
let url = `data:text/html,${encodeURIComponent(html)}`;
|
||||
async function runTest({ html, fullZoom = 1, coords }) {
|
||||
let url = `data:text/html,${encodeURIComponent(html)}#scroll`;
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, true);
|
||||
|
||||
tab.linkedBrowser.fullZoom = options.fullZoom;
|
||||
|
||||
async function background(options) {
|
||||
browser.test.log(
|
||||
`Test color ${options.color} at fullZoom=${options.fullZoom}`
|
||||
);
|
||||
tab.linkedBrowser.fullZoom = fullZoom;
|
||||
|
||||
async function background(coords) {
|
||||
try {
|
||||
let [tab] = await browser.tabs.query({
|
||||
currentWindow: true,
|
||||
@ -68,17 +46,18 @@ async function runTest(options) {
|
||||
);
|
||||
|
||||
[jpeg, png] = await Promise.all(promises);
|
||||
let tabDims = `${tab.width}\u00d7${tab.height}`;
|
||||
|
||||
let images = { jpeg, png };
|
||||
for (let format of Object.keys(images)) {
|
||||
let img = images[format];
|
||||
|
||||
let dims = `${img.width}\u00d7${img.height}`;
|
||||
browser.test.assertEq(
|
||||
tabDims,
|
||||
dims,
|
||||
`${format} dimensions are correct`
|
||||
// WGP.drawSnapshot() deals in int coordinates, and rounds down.
|
||||
browser.test.assertTrue(
|
||||
Math.abs(tab.width - img.width) <= 1,
|
||||
`${format} ok image width: ${img.width}, from a tab: ${tab.width}`
|
||||
);
|
||||
browser.test.assertTrue(
|
||||
Math.abs(tab.height - img.height) <= 1,
|
||||
`${format} ok image height ${img.height}, from a tab: ${tab.height}`
|
||||
);
|
||||
|
||||
let canvas = document.createElement("canvas");
|
||||
@ -89,19 +68,9 @@ async function runTest(options) {
|
||||
let ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
// Check the colors of the first and last pixels of the image, to make
|
||||
// sure we capture the entire frame, and scale it correctly.
|
||||
let coords = [
|
||||
{ x: 0, y: 0, color: options.color },
|
||||
{ x: img.width - 1, y: img.height - 1, color: options.color },
|
||||
{
|
||||
x: (img.width / 2) | 0,
|
||||
y: (img.height / 2) | 0,
|
||||
color: options.neutral,
|
||||
},
|
||||
];
|
||||
|
||||
for (let { x, y, color } of coords) {
|
||||
x = (x + img.width) % img.width;
|
||||
y = (y + img.height) % img.height;
|
||||
let imageData = ctx.getImageData(x, y, 1, 1).data;
|
||||
|
||||
if (format == "png") {
|
||||
@ -153,7 +122,7 @@ async function runTest(options) {
|
||||
permissions: ["<all_urls>"],
|
||||
},
|
||||
|
||||
background: `(${background})(${JSON.stringify(options)})`,
|
||||
background: `(${background})(${JSON.stringify(coords)})`,
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
@ -165,14 +134,77 @@ async function runTest(options) {
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
add_task(async function testCaptureTab() {
|
||||
await runTest({ color: [0, 0, 0], fullZoom: 1 });
|
||||
function testEdgeToEdge({ color, fullZoom }) {
|
||||
let neutral = [0xaa, 0xaa, 0xaa];
|
||||
|
||||
await runTest({ color: [0, 0, 0], fullZoom: 2 });
|
||||
let html = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head><meta charset="UTF-8"></head>
|
||||
<body style="background-color: rgb(${color})">
|
||||
<!-- Fill most of the image with a neutral color to test edge-to-edge scaling. -->
|
||||
<div style="position: absolute;
|
||||
left: 2px;
|
||||
right: 2px;
|
||||
top: 2px;
|
||||
bottom: 2px;
|
||||
background: rgb(${neutral});"></div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
await runTest({ color: [0, 0, 0], fullZoom: 0.5 });
|
||||
// Check the colors of the first and last pixels of the image, to make
|
||||
// sure we capture the entire frame, and scale it correctly.
|
||||
let coords = [
|
||||
{ x: 0, y: 0, color },
|
||||
{ x: -1, y: -1, color },
|
||||
{ x: 300, y: 200, color: neutral },
|
||||
];
|
||||
|
||||
await runTest({ color: [255, 255, 255], fullZoom: 1 });
|
||||
info(`Test edge to edge color ${color} at fullZoom=${fullZoom}`);
|
||||
return runTest({ html, fullZoom, coords });
|
||||
}
|
||||
|
||||
add_task(async function testCaptureEdgeToEdge() {
|
||||
await testEdgeToEdge({ color: [0, 0, 0], fullZoom: 1 });
|
||||
|
||||
await testEdgeToEdge({ color: [0, 0, 0], fullZoom: 2 });
|
||||
|
||||
await testEdgeToEdge({ color: [0, 0, 0], fullZoom: 0.5 });
|
||||
|
||||
await testEdgeToEdge({ color: [255, 255, 255], fullZoom: 1 });
|
||||
});
|
||||
|
||||
// Test currently visible viewport is captured if scrolling is involved.
|
||||
add_task(async function testScrolledViewport() {
|
||||
await runTest({
|
||||
html: `<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<div style="background: yellow; width: 50%; height: 500px;"></div>
|
||||
<div id=scroll style="background: red; width: 25%; height: 5000px;"></div>
|
||||
Opened with the #scroll fragment, scrolls the div ^ into view.
|
||||
`,
|
||||
coords: [
|
||||
{ x: 50, y: 50, color: [255, 0, 0] },
|
||||
{ x: 50, y: -50, color: [255, 0, 0] },
|
||||
{ x: -50, y: -50, color: [255, 255, 255] },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
// Test OOP iframes are captured, for Fission compatibility.
|
||||
add_task(async function testOOPiframe() {
|
||||
await runTest({
|
||||
html: `<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<iframe src="http://example.com/browser/browser/components/extensions/test/browser/file_green.html"></iframe>
|
||||
`,
|
||||
coords: [
|
||||
{ x: 50, y: 50, color: [0, 255, 0] },
|
||||
{ x: 50, y: -50, color: [255, 255, 255] },
|
||||
{ x: -50, y: 50, color: [255, 255, 255] },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function testCaptureTabPermissions() {
|
||||
|
@ -74,17 +74,18 @@ async function runTest(options) {
|
||||
);
|
||||
|
||||
[jpeg, png] = await Promise.all(promises);
|
||||
let tabDims = `${tab.width}\u00d7${tab.height}`;
|
||||
|
||||
let images = { jpeg, png };
|
||||
for (let format of Object.keys(images)) {
|
||||
let img = images[format];
|
||||
|
||||
let dims = `${img.width}\u00d7${img.height}`;
|
||||
browser.test.assertEq(
|
||||
tabDims,
|
||||
dims,
|
||||
`${format} dimensions are correct`
|
||||
// WGP.drawSnapshot() deals in int coordinates, and rounds down.
|
||||
browser.test.assertTrue(
|
||||
Math.abs(tab.width - img.width) <= 1,
|
||||
`${format} ok image width: ${img.width}, from a tab: ${tab.width}`
|
||||
);
|
||||
browser.test.assertTrue(
|
||||
Math.abs(tab.height - img.height) <= 1,
|
||||
`${format} ok image height ${img.height}, from a tab: ${tab.height}`
|
||||
);
|
||||
|
||||
let canvas = document.createElement("canvas");
|
||||
|
@ -0,0 +1,3 @@
|
||||
<meta charset=utf-8>
|
||||
<title>Super green test page</title>
|
||||
<body style="background: #0f0">
|
@ -81,9 +81,8 @@ interface WindowGlobalParent : WindowContext {
|
||||
/**
|
||||
* Renders a region of the frame into an image bitmap.
|
||||
*
|
||||
* @param rect Specify the area of the window to render, in CSS pixels. This
|
||||
* is relative to the current scroll position. If null, the entire viewport
|
||||
* is rendered.
|
||||
* @param rect Specify the area of the document to render, in CSS pixels,
|
||||
* relative to the page. If null, the currently visible viewport is rendered.
|
||||
* @param scale The scale to render the window at. Use devicePixelRatio
|
||||
* to have comparable rendering to the OS.
|
||||
* @param backgroundColor The background color to use.
|
||||
|
@ -623,8 +623,13 @@ already_AddRefed<mozilla::dom::Promise> WindowGlobalParent::DrawSnapshot(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!gfx::CrossProcessPaint::Start(this, aRect, (float)aScale, color,
|
||||
gfx::CrossProcessPaintFlags::None,
|
||||
gfx::CrossProcessPaintFlags flags = gfx::CrossProcessPaintFlags::None;
|
||||
if (!aRect) {
|
||||
// If no explicit Rect was passed, we want the currently visible viewport.
|
||||
flags = gfx::CrossProcessPaintFlags::DrawView;
|
||||
}
|
||||
|
||||
if (!gfx::CrossProcessPaint::Start(this, aRect, (float)aScale, color, flags,
|
||||
promise)) {
|
||||
aRv = NS_ERROR_FAILURE;
|
||||
return nullptr;
|
||||
|
@ -63,14 +63,20 @@ function* runTest(options) {
|
||||
}));
|
||||
|
||||
[jpeg, png] = await Promise.all(promises);
|
||||
const tabDims = `${tab.width}\u00d7${tab.height}`;
|
||||
|
||||
const images = {jpeg, png};
|
||||
for (const format of Object.keys(images)) {
|
||||
const img = images[format];
|
||||
|
||||
const dims = `${img.width}\u00d7${img.height}`;
|
||||
browser.test.assertEq(tabDims, dims, `${format} dimensions are correct`);
|
||||
// WGP.drawSnapshot() deals in int coordinates, and rounds down.
|
||||
browser.test.assertTrue(
|
||||
Math.abs(tab.width - img.width) <= 1,
|
||||
`${format} ok image width: ${img.width}, from a tab: ${tab.width}`
|
||||
);
|
||||
browser.test.assertTrue(
|
||||
Math.abs(tab.height - img.height) <= 1,
|
||||
`${format} ok image height ${img.height}, from a tab: ${tab.height}`
|
||||
);
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = img.width;
|
||||
|
@ -1101,34 +1101,6 @@ var ExtensionContent = {
|
||||
return context;
|
||||
},
|
||||
|
||||
handleExtensionCapture(global, width, height, options) {
|
||||
let win = global.content;
|
||||
|
||||
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
let canvas = win.document.createElementNS(XHTML_NS, "canvas");
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
canvas.mozOpaque = true;
|
||||
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
// We need to scale the image to the visible size of the browser,
|
||||
// in order for the result to appear as the user sees it when
|
||||
// settings like full zoom come into play.
|
||||
ctx.scale(canvas.width / win.innerWidth, canvas.height / win.innerHeight);
|
||||
|
||||
ctx.drawWindow(
|
||||
win,
|
||||
win.scrollX,
|
||||
win.scrollY,
|
||||
win.innerWidth,
|
||||
win.innerHeight,
|
||||
"#fff"
|
||||
);
|
||||
|
||||
return canvas.toDataURL(`image/${options.format}`, options.quality / 100);
|
||||
},
|
||||
|
||||
handleDetectLanguage(global, target) {
|
||||
let doc = target.content.document;
|
||||
|
||||
@ -1217,13 +1189,6 @@ var ExtensionContent = {
|
||||
|
||||
async receiveMessage(global, name, target, data, recipient) {
|
||||
switch (name) {
|
||||
case "Extension:Capture":
|
||||
return this.handleExtensionCapture(
|
||||
global,
|
||||
data.width,
|
||||
data.height,
|
||||
data.options
|
||||
);
|
||||
case "Extension:DetectLanguage":
|
||||
return this.handleDetectLanguage(global, target);
|
||||
case "WebNavigation:GetFrame":
|
||||
|
@ -69,7 +69,6 @@ class ExtensionGlobal {
|
||||
|
||||
this.frameData = null;
|
||||
|
||||
MessageChannel.addListener(global, "Extension:Capture", this);
|
||||
MessageChannel.addListener(global, "Extension:DetectLanguage", this);
|
||||
MessageChannel.addListener(global, "WebNavigation:GetFrame", this);
|
||||
MessageChannel.addListener(global, "WebNavigation:GetAllFrames", this);
|
||||
|
@ -7,16 +7,11 @@
|
||||
|
||||
/* globals EventEmitter */
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"Services",
|
||||
"resource://gre/modules/Services.jsm"
|
||||
);
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
this,
|
||||
"containersEnabled",
|
||||
@ -112,7 +107,7 @@ class TabBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture the visible area of this tab, and return the result as a data: URL.
|
||||
* Capture the visible area of this tab, and return the result as a data: URI.
|
||||
*
|
||||
* @param {BaseContext} context
|
||||
* The extension context for which to perform the capture.
|
||||
@ -127,24 +122,23 @@ class TabBase {
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
capture(context, options = null) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
if (options.format == null) {
|
||||
options.format = "png";
|
||||
}
|
||||
if (options.quality == null) {
|
||||
options.quality = 92;
|
||||
}
|
||||
async capture(context, options = null) {
|
||||
let { ZoomManager, devicePixelRatio } = this.nativeTab.ownerGlobal;
|
||||
let scale = ZoomManager.getZoomForBrowser(this.browser) * devicePixelRatio;
|
||||
|
||||
let message = {
|
||||
options,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
};
|
||||
let wgp = this.browsingContext.currentWindowGlobal;
|
||||
let image = await wgp.drawSnapshot(null, scale, "white");
|
||||
|
||||
return this.sendMessage(context, "Extension:Capture", message);
|
||||
let win = Services.appShell.hiddenDOMWindow;
|
||||
let canvas = win.document.createElement("canvas");
|
||||
canvas.width = image.width;
|
||||
canvas.height = image.height;
|
||||
|
||||
let ctx = canvas.getContext("2d", { alpha: false });
|
||||
ctx.drawImage(image, 0, 0);
|
||||
image.close();
|
||||
|
||||
return canvas.toDataURL(`image/${options?.format}`, options?.quality / 100);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user