Bug 1636508 - Add options to select captureTab area and scale r=robwu,geckoview-reviewers,agi
Also fix test for HiDPI monitors, and refactor it to remove duplicate code for captureVisibleTab. Differential Revision: https://phabricator.services.mozilla.com/D89128
This commit is contained in:
@@ -1120,7 +1120,7 @@
|
|||||||
{
|
{
|
||||||
"name": "captureTab",
|
"name": "captureTab",
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"description": "Captures the visible area of a specified tab. You must have $(topic:declare_permissions)[<all_urls>] permission to use this method.",
|
"description": "Captures an area of a specified tab. You must have $(topic:declare_permissions)[<all_urls>] permission to use this method.",
|
||||||
"permissions": ["<all_urls>"],
|
"permissions": ["<all_urls>"],
|
||||||
"async": true,
|
"async": true,
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -1141,7 +1141,7 @@
|
|||||||
{
|
{
|
||||||
"name": "captureVisibleTab",
|
"name": "captureVisibleTab",
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"description": "Captures the visible area of the currently active tab in the specified window. You must have $(topic:declare_permissions)[<all_urls>] permission to use this method.",
|
"description": "Captures an area of the currently active tab in the specified window. You must have $(topic:declare_permissions)[<all_urls>] permission to use this method.",
|
||||||
"permissions": ["<all_urls>"],
|
"permissions": ["<all_urls>"],
|
||||||
"async": "callback",
|
"async": "callback",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -219,7 +219,6 @@ skip-if = !e10s || debug || asan || (os == "win" && processor == "aarch64") # wi
|
|||||||
[browser_ext_tabs_attention.js]
|
[browser_ext_tabs_attention.js]
|
||||||
[browser_ext_tabs_audio.js]
|
[browser_ext_tabs_audio.js]
|
||||||
[browser_ext_tabs_captureTab.js]
|
[browser_ext_tabs_captureTab.js]
|
||||||
[browser_ext_tabs_captureVisibleTab.js]
|
|
||||||
[browser_ext_tabs_create.js]
|
[browser_ext_tabs_create.js]
|
||||||
skip-if = os == "linux" && debug && bits == 32 # Bug 1350189
|
skip-if = os == "linux" && debug && bits == 32 # Bug 1350189
|
||||||
[browser_ext_tabs_create_url.js]
|
[browser_ext_tabs_create_url.js]
|
||||||
|
|||||||
@@ -2,24 +2,26 @@
|
|||||||
/* vim: set sts=2 sw=2 et tw=80: */
|
/* vim: set sts=2 sw=2 et tw=80: */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
async function runTest({ html, fullZoom = 1, coords }) {
|
async function runTest({ html, fullZoom, coords, rect, scale }) {
|
||||||
let url = `data:text/html,${encodeURIComponent(html)}#scroll`;
|
let url = `data:text/html,${encodeURIComponent(html)}#scroll`;
|
||||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, true);
|
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, true);
|
||||||
|
|
||||||
tab.linkedBrowser.fullZoom = fullZoom;
|
tab.linkedBrowser.fullZoom = fullZoom ?? 1;
|
||||||
|
|
||||||
async function background(coords) {
|
async function background({ coords, rect, scale, method }) {
|
||||||
try {
|
try {
|
||||||
let [tab] = await browser.tabs.query({
|
let [tab] = await browser.tabs.query({
|
||||||
currentWindow: true,
|
currentWindow: true,
|
||||||
active: true,
|
active: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let id = method === "captureVisibleTab" ? tab.windowId : tab.id;
|
||||||
|
|
||||||
let [jpeg, png, ...pngs] = await Promise.all([
|
let [jpeg, png, ...pngs] = await Promise.all([
|
||||||
browser.tabs.captureTab(tab.id, { format: "jpeg", quality: 95 }),
|
browser.tabs[method](id, { format: "jpeg", quality: 95, rect, scale }),
|
||||||
browser.tabs.captureTab(tab.id, { format: "png", quality: 95 }),
|
browser.tabs[method](id, { format: "png", quality: 95, rect, scale }),
|
||||||
browser.tabs.captureTab(tab.id, { quality: 95 }),
|
browser.tabs[method](id, { quality: 95, rect, scale }),
|
||||||
browser.tabs.captureTab(tab.id),
|
browser.tabs[method](id, { rect, scale }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
browser.test.assertTrue(
|
browser.test.assertTrue(
|
||||||
@@ -45,6 +47,9 @@ async function runTest({ html, fullZoom = 1, coords }) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let width = (rect?.width ?? tab.width) * (scale ?? devicePixelRatio);
|
||||||
|
let height = (rect?.height ?? tab.height) * (scale ?? devicePixelRatio);
|
||||||
|
|
||||||
[jpeg, png] = await Promise.all(promises);
|
[jpeg, png] = await Promise.all(promises);
|
||||||
let images = { jpeg, png };
|
let images = { jpeg, png };
|
||||||
for (let format of Object.keys(images)) {
|
for (let format of Object.keys(images)) {
|
||||||
@@ -52,12 +57,12 @@ async function runTest({ html, fullZoom = 1, coords }) {
|
|||||||
|
|
||||||
// WGP.drawSnapshot() deals in int coordinates, and rounds down.
|
// WGP.drawSnapshot() deals in int coordinates, and rounds down.
|
||||||
browser.test.assertTrue(
|
browser.test.assertTrue(
|
||||||
Math.abs(tab.width - img.width) <= 1,
|
Math.abs(width - img.width) <= 1,
|
||||||
`${format} ok image width: ${img.width}, from a tab: ${tab.width}`
|
`${format} ok image width: ${img.width}, expected: ${width}`
|
||||||
);
|
);
|
||||||
browser.test.assertTrue(
|
browser.test.assertTrue(
|
||||||
Math.abs(tab.height - img.height) <= 1,
|
Math.abs(height - img.height) <= 1,
|
||||||
`${format} ok image height ${img.height}, from a tab: ${tab.height}`
|
`${format} ok image height ${img.height}, expected: ${height}`
|
||||||
);
|
);
|
||||||
|
|
||||||
let canvas = document.createElement("canvas");
|
let canvas = document.createElement("canvas");
|
||||||
@@ -117,24 +122,27 @@ async function runTest({ html, fullZoom = 1, coords }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let extension = ExtensionTestUtils.loadExtension({
|
for (let method of ["captureTab", "captureVisibleTab"]) {
|
||||||
manifest: {
|
let options = { coords, rect, scale, method };
|
||||||
permissions: ["<all_urls>"],
|
info(`Testing configuration: ${JSON.stringify(options)}`);
|
||||||
},
|
|
||||||
|
|
||||||
background: `(${background})(${JSON.stringify(coords)})`,
|
let extension = ExtensionTestUtils.loadExtension({
|
||||||
});
|
manifest: {
|
||||||
|
permissions: ["<all_urls>"],
|
||||||
|
},
|
||||||
|
|
||||||
await extension.startup();
|
background: `(${background})(${JSON.stringify(options)})`,
|
||||||
|
});
|
||||||
|
|
||||||
await extension.awaitFinish("captureTab");
|
await extension.startup();
|
||||||
|
await extension.awaitFinish("captureTab");
|
||||||
await extension.unload();
|
await extension.unload();
|
||||||
|
}
|
||||||
|
|
||||||
BrowserTestUtils.removeTab(tab);
|
BrowserTestUtils.removeTab(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testEdgeToEdge({ color, fullZoom }) {
|
async function testEdgeToEdge({ color, fullZoom }) {
|
||||||
let neutral = [0xaa, 0xaa, 0xaa];
|
let neutral = [0xaa, 0xaa, 0xaa];
|
||||||
|
|
||||||
let html = `
|
let html = `
|
||||||
@@ -162,28 +170,27 @@ function testEdgeToEdge({ color, fullZoom }) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
info(`Test edge to edge color ${color} at fullZoom=${fullZoom}`);
|
info(`Test edge to edge color ${color} at fullZoom=${fullZoom}`);
|
||||||
return runTest({ html, fullZoom, coords });
|
await runTest({ html, fullZoom, coords });
|
||||||
}
|
}
|
||||||
|
|
||||||
add_task(async function testCaptureEdgeToEdge() {
|
add_task(async function testCaptureEdgeToEdge() {
|
||||||
await testEdgeToEdge({ color: [0, 0, 0], fullZoom: 1 });
|
await testEdgeToEdge({ color: [0, 0, 0], fullZoom: 1 });
|
||||||
|
|
||||||
await testEdgeToEdge({ color: [0, 0, 0], fullZoom: 2 });
|
await testEdgeToEdge({ color: [0, 0, 0], fullZoom: 2 });
|
||||||
|
|
||||||
await testEdgeToEdge({ color: [0, 0, 0], fullZoom: 0.5 });
|
await testEdgeToEdge({ color: [0, 0, 0], fullZoom: 0.5 });
|
||||||
|
|
||||||
await testEdgeToEdge({ color: [255, 255, 255], fullZoom: 1 });
|
await testEdgeToEdge({ color: [255, 255, 255], fullZoom: 1 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const tallDoc = `<!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.
|
||||||
|
`;
|
||||||
|
|
||||||
// Test currently visible viewport is captured if scrolling is involved.
|
// Test currently visible viewport is captured if scrolling is involved.
|
||||||
add_task(async function testScrolledViewport() {
|
add_task(async function testScrolledViewport() {
|
||||||
await runTest({
|
await runTest({
|
||||||
html: `<!DOCTYPE html>
|
html: tallDoc,
|
||||||
<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: [
|
coords: [
|
||||||
{ x: 50, y: 50, color: [255, 0, 0] },
|
{ x: 50, y: 50, color: [255, 0, 0] },
|
||||||
{ x: 50, y: -50, color: [255, 0, 0] },
|
{ x: 50, y: -50, color: [255, 0, 0] },
|
||||||
@@ -192,6 +199,21 @@ add_task(async function testScrolledViewport() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Test rect and scale options.
|
||||||
|
add_task(async function testRectAndScale() {
|
||||||
|
await runTest({
|
||||||
|
html: tallDoc,
|
||||||
|
rect: { x: 50, y: 50, width: 10, height: 1000 },
|
||||||
|
scale: 4,
|
||||||
|
coords: [
|
||||||
|
{ x: 0, y: 0, color: [255, 255, 0] },
|
||||||
|
{ x: -1, y: 0, color: [255, 255, 0] },
|
||||||
|
{ x: 0, y: -1, color: [255, 0, 0] },
|
||||||
|
{ x: -1, y: -1, color: [255, 0, 0] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Test OOP iframes are captured, for Fission compatibility.
|
// Test OOP iframes are captured, for Fission compatibility.
|
||||||
add_task(async function testOOPiframe() {
|
add_task(async function testOOPiframe() {
|
||||||
await runTest({
|
await runTest({
|
||||||
@@ -224,8 +246,27 @@ add_task(async function testCaptureTabPermissions() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await extension.startup();
|
await extension.startup();
|
||||||
|
|
||||||
await extension.awaitFinish("captureTabPermissions");
|
await extension.awaitFinish("captureTabPermissions");
|
||||||
|
await extension.unload();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function testCaptureVisibleTabPermissions() {
|
||||||
|
let extension = ExtensionTestUtils.loadExtension({
|
||||||
|
manifest: {
|
||||||
|
permissions: ["tabs"],
|
||||||
|
},
|
||||||
|
|
||||||
|
background() {
|
||||||
|
browser.test.assertEq(
|
||||||
|
undefined,
|
||||||
|
browser.tabs.captureVisibleTab,
|
||||||
|
'Extension without "<all_urls>" permission should not have access to captureVisibleTab'
|
||||||
|
);
|
||||||
|
browser.test.notifyPass("captureVisibleTabPermissions");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await extension.startup();
|
||||||
|
await extension.awaitFinish("captureVisibleTabPermissions");
|
||||||
await extension.unload();
|
await extension.unload();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,206 +0,0 @@
|
|||||||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
||||||
/* 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)}`;
|
|
||||||
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}`
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
let [tab] = await browser.tabs.query({
|
|
||||||
currentWindow: true,
|
|
||||||
active: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
let [jpeg, png, ...pngs] = await Promise.all([
|
|
||||||
browser.tabs.captureVisibleTab(tab.windowId, {
|
|
||||||
format: "jpeg",
|
|
||||||
quality: 95,
|
|
||||||
}),
|
|
||||||
browser.tabs.captureVisibleTab(tab.windowId, {
|
|
||||||
format: "png",
|
|
||||||
quality: 95,
|
|
||||||
}),
|
|
||||||
browser.tabs.captureVisibleTab(tab.windowId, { quality: 95 }),
|
|
||||||
browser.tabs.captureVisibleTab(tab.windowId),
|
|
||||||
]);
|
|
||||||
|
|
||||||
browser.test.assertTrue(
|
|
||||||
pngs.every(url => url == png),
|
|
||||||
"All PNGs are identical"
|
|
||||||
);
|
|
||||||
|
|
||||||
browser.test.assertTrue(
|
|
||||||
jpeg.startsWith("data:image/jpeg;base64,"),
|
|
||||||
"jpeg is JPEG"
|
|
||||||
);
|
|
||||||
browser.test.assertTrue(
|
|
||||||
png.startsWith("data:image/png;base64,"),
|
|
||||||
"png is PNG"
|
|
||||||
);
|
|
||||||
|
|
||||||
let promises = [jpeg, png].map(
|
|
||||||
url =>
|
|
||||||
new Promise(resolve => {
|
|
||||||
let img = new Image();
|
|
||||||
img.src = url;
|
|
||||||
img.onload = () => resolve(img);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
[jpeg, png] = await Promise.all(promises);
|
|
||||||
let images = { jpeg, png };
|
|
||||||
for (let format of Object.keys(images)) {
|
|
||||||
let img = images[format];
|
|
||||||
|
|
||||||
// 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");
|
|
||||||
canvas.width = img.width;
|
|
||||||
canvas.height = img.height;
|
|
||||||
canvas.mozOpaque = true;
|
|
||||||
|
|
||||||
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) {
|
|
||||||
let imageData = ctx.getImageData(x, y, 1, 1).data;
|
|
||||||
|
|
||||||
if (format == "png") {
|
|
||||||
browser.test.assertEq(
|
|
||||||
`rgba(${color},255)`,
|
|
||||||
`rgba(${[...imageData]})`,
|
|
||||||
`${format} image color is correct at (${x}, ${y})`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Allow for some deviation in JPEG version due to lossy compression.
|
|
||||||
const SLOP = 3;
|
|
||||||
|
|
||||||
browser.test.log(
|
|
||||||
`Testing ${format} image color at (${x}, ${y}), have rgba(${[
|
|
||||||
...imageData,
|
|
||||||
]}), expecting approx. rgba(${color},255)`
|
|
||||||
);
|
|
||||||
|
|
||||||
browser.test.assertTrue(
|
|
||||||
Math.abs(color[0] - imageData[0]) <= SLOP,
|
|
||||||
`${format} image color.red is correct at (${x}, ${y})`
|
|
||||||
);
|
|
||||||
browser.test.assertTrue(
|
|
||||||
Math.abs(color[1] - imageData[1]) <= SLOP,
|
|
||||||
`${format} image color.green is correct at (${x}, ${y})`
|
|
||||||
);
|
|
||||||
browser.test.assertTrue(
|
|
||||||
Math.abs(color[2] - imageData[2]) <= SLOP,
|
|
||||||
`${format} image color.blue is correct at (${x}, ${y})`
|
|
||||||
);
|
|
||||||
browser.test.assertEq(
|
|
||||||
255,
|
|
||||||
imageData[3],
|
|
||||||
`${format} image color.alpha is correct at (${x}, ${y})`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
browser.test.notifyPass("captureVisibleTab");
|
|
||||||
} catch (e) {
|
|
||||||
browser.test.fail(`Error: ${e} :: ${e.stack}`);
|
|
||||||
browser.test.notifyFail("captureVisibleTab");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let extension = ExtensionTestUtils.loadExtension({
|
|
||||||
manifest: {
|
|
||||||
permissions: ["<all_urls>"],
|
|
||||||
},
|
|
||||||
|
|
||||||
background: `(${background})(${JSON.stringify(options)})`,
|
|
||||||
});
|
|
||||||
|
|
||||||
await extension.startup();
|
|
||||||
|
|
||||||
await extension.awaitFinish("captureVisibleTab");
|
|
||||||
|
|
||||||
await extension.unload();
|
|
||||||
|
|
||||||
BrowserTestUtils.removeTab(tab);
|
|
||||||
}
|
|
||||||
|
|
||||||
add_task(async function testCaptureVisibleTab() {
|
|
||||||
await runTest({ color: [0, 0, 0], fullZoom: 1 });
|
|
||||||
|
|
||||||
await runTest({ color: [0, 0, 0], fullZoom: 2 });
|
|
||||||
|
|
||||||
await runTest({ color: [0, 0, 0], fullZoom: 0.5 });
|
|
||||||
|
|
||||||
await runTest({ color: [255, 255, 255], fullZoom: 1 });
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(async function testCaptureVisibleTabPermissions() {
|
|
||||||
let extension = ExtensionTestUtils.loadExtension({
|
|
||||||
manifest: {
|
|
||||||
permissions: ["tabs"],
|
|
||||||
},
|
|
||||||
|
|
||||||
background() {
|
|
||||||
browser.test.assertEq(
|
|
||||||
undefined,
|
|
||||||
browser.tabs.captureVisibleTab,
|
|
||||||
'Extension without "<all_urls>" permission should not have access to captureVisibleTab'
|
|
||||||
);
|
|
||||||
browser.test.notifyPass("captureVisibleTabPermissions");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await extension.startup();
|
|
||||||
|
|
||||||
await extension.awaitFinish("captureVisibleTabPermissions");
|
|
||||||
|
|
||||||
await extension.unload();
|
|
||||||
});
|
|
||||||
@@ -836,7 +836,7 @@
|
|||||||
{
|
{
|
||||||
"name": "captureVisibleTab",
|
"name": "captureVisibleTab",
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"description": "Captures the visible area of the currently active tab in the specified window. You must have $(topic:declare_permissions)[<all_urls>] permission to use this method.",
|
"description": "Captures an area of the currently active tab in the specified window. You must have $(topic:declare_permissions)[<all_urls>] permission to use this method.",
|
||||||
"permissions": ["<all_urls>"],
|
"permissions": ["<all_urls>"],
|
||||||
"async": "callback",
|
"async": "callback",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -70,11 +70,11 @@ function* runTest(options) {
|
|||||||
|
|
||||||
// WGP.drawSnapshot() deals in int coordinates, and rounds down.
|
// WGP.drawSnapshot() deals in int coordinates, and rounds down.
|
||||||
browser.test.assertTrue(
|
browser.test.assertTrue(
|
||||||
Math.abs(tab.width - img.width) <= 1,
|
Math.abs(tab.width * devicePixelRatio - img.width) <= 1,
|
||||||
`${format} ok image width: ${img.width}, from a tab: ${tab.width}`
|
`${format} ok image width: ${img.width}, from a tab: ${tab.width}`
|
||||||
);
|
);
|
||||||
browser.test.assertTrue(
|
browser.test.assertTrue(
|
||||||
Math.abs(tab.height - img.height) <= 1,
|
Math.abs(tab.height * devicePixelRatio - img.height) <= 1,
|
||||||
`${format} ok image height ${img.height}, from a tab: ${tab.height}`
|
`${format} ok image height ${img.height}, from a tab: ${tab.height}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -119,18 +119,24 @@ class TabBase {
|
|||||||
* @param {integer} [options.quality = 92]
|
* @param {integer} [options.quality = 92]
|
||||||
* The quality at which to encode the captured image data, ranging from
|
* The quality at which to encode the captured image data, ranging from
|
||||||
* 0 to 100. Has no effect for the "png" format.
|
* 0 to 100. Has no effect for the "png" format.
|
||||||
*
|
* @param {DOMRectInit} [options.rect]
|
||||||
|
* Area of the document to render, in CSS pixels, relative to the page.
|
||||||
|
* If null, the currently visible viewport is rendered.
|
||||||
|
* @param {number} [options.scale]
|
||||||
|
* The scale to render at, defaults to devicePixelRatio.
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
async capture(context, options = null) {
|
async capture(context, options) {
|
||||||
let { ZoomManager, devicePixelRatio } = this.nativeTab.ownerGlobal;
|
let win = this.nativeTab.ownerGlobal;
|
||||||
let scale = ZoomManager.getZoomForBrowser(this.browser) * devicePixelRatio;
|
let scale = options?.scale || win.devicePixelRatio;
|
||||||
|
let zoom = win.ZoomManager.getZoomForBrowser(this.browser);
|
||||||
|
let rect = options?.rect && win.DOMRect.fromRect(options.rect);
|
||||||
|
|
||||||
let wgp = this.browsingContext.currentWindowGlobal;
|
let wgp = this.browsingContext.currentWindowGlobal;
|
||||||
let image = await wgp.drawSnapshot(null, scale, "white");
|
let image = await wgp.drawSnapshot(rect, scale * zoom, "white");
|
||||||
|
|
||||||
let win = Services.appShell.hiddenDOMWindow;
|
let doc = Services.appShell.hiddenDOMWindow.document;
|
||||||
let canvas = win.document.createElement("canvas");
|
let canvas = doc.createElement("canvas");
|
||||||
canvas.width = image.width;
|
canvas.width = image.width;
|
||||||
canvas.height = image.height;
|
canvas.height = image.height;
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
{
|
{
|
||||||
"id": "ImageDetails",
|
"id": "ImageDetails",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Details about the format and quality of an image.",
|
"description": "Details about the format, quality, area and scale of the capture.",
|
||||||
"properties": {
|
"properties": {
|
||||||
"format": {
|
"format": {
|
||||||
"$ref": "ImageFormat",
|
"$ref": "ImageFormat",
|
||||||
@@ -29,6 +29,22 @@
|
|||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 100,
|
"maximum": 100,
|
||||||
"description": "When format is <code>\"jpeg\"</code>, controls the quality of the resulting image. This value is ignored for PNG images. As quality is decreased, the resulting image will have more visual artifacts, and the number of bytes needed to store it will decrease."
|
"description": "When format is <code>\"jpeg\"</code>, controls the quality of the resulting image. This value is ignored for PNG images. As quality is decreased, the resulting image will have more visual artifacts, and the number of bytes needed to store it will decrease."
|
||||||
|
},
|
||||||
|
"rect": {
|
||||||
|
"type": "object",
|
||||||
|
"optional": true,
|
||||||
|
"description": "The area of the document to capture, in CSS pixels, relative to the page. If omitted, capture the visible viewport.",
|
||||||
|
"properties": {
|
||||||
|
"x": {"type": "number"},
|
||||||
|
"y": {"type": "number"},
|
||||||
|
"width": {"type": "number"},
|
||||||
|
"height": {"type": "number"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scale": {
|
||||||
|
"type": "number",
|
||||||
|
"optional": true,
|
||||||
|
"description": "The scale of the resulting image. Defaults to <code>devicePixelRatio</code>."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user