зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1403686 - Crops screenshots to a region based on a selector. r=jaws
Can now crop screenshots properly, for basic cases. Also changed behavior so that if different window types are given, the application exits. MozReview-Commit-ID: CqmIJFufONw --HG-- extra : rebase_source : 7cdc7c4ce62959930c55491b277923b372929bc6
This commit is contained in:
Родитель
6bda31a889
Коммит
ebcc9cb494
|
@ -4,4 +4,5 @@ support-files =
|
|||
head.js
|
||||
|
||||
[browser_screenshots.js]
|
||||
[browser_screenshots_cropping.js]
|
||||
[browser_boundingbox.js]
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
async function draw(window, src) {
|
||||
const { document, Image } = window;
|
||||
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
|
||||
img.onload = function() {
|
||||
// Create a new offscreen canvas
|
||||
const canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
canvas.width = img.naturalWidth;
|
||||
canvas.height = img.naturalHeight;
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
resolve(canvas);
|
||||
};
|
||||
|
||||
img.onerror = function() {
|
||||
reject(`error loading image ${src}`);
|
||||
}
|
||||
|
||||
// Load the src image for drawing
|
||||
img.src = src;
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
async function compareImages(window, expected, test) {
|
||||
const testCanvas = await draw(window, test);
|
||||
const expectedCanvas = await draw(window, expected);
|
||||
|
||||
is(testCanvas.width, expectedCanvas.width, "The test and expected images must be the same size");
|
||||
is(testCanvas.height, expectedCanvas.height, "The test and expected images must be the same size");
|
||||
|
||||
const nsIDOMWindowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
|
||||
return nsIDOMWindowUtils.compareCanvases(expectedCanvas, testCanvas, {});
|
||||
}
|
||||
|
||||
async function cropAndCompare(window, src, expected, test, region) {
|
||||
await TestRunner._cropImage(window, src, region, test);
|
||||
|
||||
return compareImages(window, expected, OS.Path.toFileURI(test));
|
||||
}
|
||||
|
||||
add_task(async function crop() {
|
||||
const window = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
|
||||
const tmp = OS.Constants.Path.tmpDir;
|
||||
is(await cropAndCompare(
|
||||
window,
|
||||
"chrome://mozscreenshots/content/lib/robot.png",
|
||||
"chrome://mozscreenshots/content/lib/robot_upperleft.png",
|
||||
OS.Path.join(tmp, "test_cropped_upperleft.png"),
|
||||
{x: 0, y: 0, width: 32, height: 32}
|
||||
), 0, "The image should be cropped to the upper left quadrant");
|
||||
|
||||
is(await cropAndCompare(
|
||||
window,
|
||||
"chrome://mozscreenshots/content/lib/robot.png",
|
||||
"chrome://mozscreenshots/content/lib/robot_center.png",
|
||||
OS.Path.join(tmp, "test_cropped_center.png"),
|
||||
{x: 16, y: 16, width: 32, height: 32}
|
||||
), 0, "The image should be cropped to the center of the image");
|
||||
|
||||
await cropAndCompare(
|
||||
window,
|
||||
"chrome://mozscreenshots/content/lib/robot.png",
|
||||
"chrome://mozscreenshots/content/lib/robot.png",
|
||||
OS.Path.join(tmp, "test_cropped_center.png"),
|
||||
{x: 16, y: 16, width: 64, height: 64}
|
||||
).then(() => {
|
||||
ok(false, "Cropping region should have been too large");
|
||||
}, () => {
|
||||
ok(true, "Cropping region is too large as expected")
|
||||
});
|
||||
})
|
|
@ -64,11 +64,11 @@ this.Screenshot = {
|
|||
},
|
||||
|
||||
// Capture the whole screen using an external application.
|
||||
captureExternal(filename) {
|
||||
async captureExternal(filename) {
|
||||
let imagePath = this._buildImagePath(filename);
|
||||
return this._screenshotFunction(imagePath).then(() => {
|
||||
log.debug("saved screenshot: " + filename);
|
||||
});
|
||||
await this._screenshotFunction(imagePath);
|
||||
log.debug("saved screenshot: " + filename);
|
||||
return imagePath;
|
||||
},
|
||||
|
||||
// helpers
|
||||
|
@ -102,12 +102,6 @@ this.Screenshot = {
|
|||
|
||||
// Run the process.
|
||||
let args = ["-x", "-t", "png"];
|
||||
// Darwin version number for OS X 10.6 is 10.x
|
||||
if (windowID && Services.sysinfo.getProperty("version").indexOf("10.") !== 0) {
|
||||
// Capture only that window on 10.7+
|
||||
args.push("-l");
|
||||
args.push(windowID);
|
||||
}
|
||||
args.push(filename);
|
||||
process.runAsync(args, args.length, this._processObserver(resolve, reject));
|
||||
});
|
||||
|
|
|
@ -21,7 +21,7 @@ Cu.import("resource://gre/modules/Geometry.jsm");
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserTestUtils",
|
||||
"resource://testing-common/BrowserTestUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Screenshot", "chrome://mozscreenshots/content/Screenshot.jsm");
|
||||
Cu.import("chrome://mozscreenshots/content/Screenshot.jsm");
|
||||
|
||||
// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
|
||||
// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
|
||||
|
@ -324,21 +324,34 @@ this.TestRunner = {
|
|||
return;
|
||||
}
|
||||
|
||||
await this._onConfigurationReady(combo);
|
||||
// Collect selectors from combo configs for cropping region
|
||||
let windowType;
|
||||
const finalSelectors = [];
|
||||
for (const obj of combo) {
|
||||
if (!windowType) {
|
||||
windowType = obj.windowType;
|
||||
} else if (windowType !== obj.windowType) {
|
||||
log.warn("\tConfigurations with multiple window types are not allowed");
|
||||
return;
|
||||
}
|
||||
for (const selector of obj.selectors) {
|
||||
finalSelectors.push(selector);
|
||||
}
|
||||
}
|
||||
|
||||
const rect = this._findBoundingBox(finalSelectors, windowType);
|
||||
await this._onConfigurationReady(combo, rect);
|
||||
},
|
||||
|
||||
_onConfigurationReady(combo) {
|
||||
let delayedScreenshot = () => {
|
||||
let filename = padLeft(this.currentComboIndex + 1,
|
||||
String(this.combos.length).length) + this._comboName(combo);
|
||||
return Screenshot.captureExternal(filename)
|
||||
.then(() => {
|
||||
this.completedCombos++;
|
||||
});
|
||||
};
|
||||
async _onConfigurationReady(combo, rect) {
|
||||
let filename = padLeft(this.currentComboIndex + 1,
|
||||
String(this.combos.length).length) + this._comboName(combo);
|
||||
const imagePath = await Screenshot.captureExternal(filename);
|
||||
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
await this._cropImage(browserWindow, OS.Path.toFileURI(imagePath), rect, imagePath);
|
||||
this.completedCombos++;
|
||||
log.debug("_onConfigurationReady");
|
||||
return delayedScreenshot();
|
||||
},
|
||||
|
||||
_comboName(combo) {
|
||||
|
@ -347,6 +360,51 @@ this.TestRunner = {
|
|||
}, "");
|
||||
},
|
||||
|
||||
async _cropImage(window, srcPath, rect, targetPath) {
|
||||
const { document, Image } = window;
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = function() {
|
||||
// Make sure that the cropping region is valid and that the cropping
|
||||
// region is inside the screenshot we are cropping
|
||||
if (rect.x < 0 || rect.y < 0 || rect.width < 0 || rect.height < 0 ||
|
||||
img.naturalWidth < rect.x + rect.width ||
|
||||
img.naturalHeight < rect.y + rect.height) {
|
||||
reject("Invalid cropping region");
|
||||
return;
|
||||
}
|
||||
// Create a new offscreen canvas, with the width and height given by the
|
||||
// size of the region we want to crop to
|
||||
const canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
canvas.width = rect.width;
|
||||
canvas.height = rect.height;
|
||||
const ctx = canvas.getContext("2d");
|
||||
// By drawing the image with the negative offset, the unwanted regions
|
||||
// are drawn off canvas, and are not captured when the canvas is saved.
|
||||
ctx.drawImage(img, -rect.x, -rect.y);
|
||||
// Converts the canvas to a binary blob, which can be saved to a png
|
||||
canvas.toBlob((blob) => {
|
||||
// Use a filereader to convert the raw binary blob into a writable buffer
|
||||
const fr = new FileReader();
|
||||
fr.onload = function(e) {
|
||||
const buffer = new Uint8Array(e.target.result);
|
||||
// Save the file and complete the promise
|
||||
OS.File.writeAtomic(targetPath, buffer, {}).then(resolve);
|
||||
};
|
||||
// Do the conversion
|
||||
fr.readAsArrayBuffer(blob);
|
||||
});
|
||||
};
|
||||
|
||||
img.onerror = function() {
|
||||
reject(`error loading image ${srcPath}`);
|
||||
};
|
||||
// Load the src image for drawing
|
||||
img.src = srcPath;
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds the index of the first comma that is not enclosed within square brackets.
|
||||
* @param {String} envVar - the string that needs to be searched
|
||||
|
|
Двоичные данные
browser/tools/mozscreenshots/mozscreenshots/extension/lib/robot_center.png
Normal file
Двоичные данные
browser/tools/mozscreenshots/mozscreenshots/extension/lib/robot_center.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.4 KiB |
Двоичные данные
browser/tools/mozscreenshots/mozscreenshots/extension/lib/robot_upperleft.png
Normal file
Двоичные данные
browser/tools/mozscreenshots/mozscreenshots/extension/lib/robot_upperleft.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 2.1 KiB |
Загрузка…
Ссылка в новой задаче