зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1403686 - Crops screenshots to a region based on a selector. Initial Impl of cropping with Chris Cho. r=jaws
Can now crop screenshots to a given list of XUL elements, which is specified by CSS selectors or custom functions. Also changed behavior so that if different window types are given, the application exits. MozReview-Commit-ID: CqmIJFufONw --HG-- extra : rebase_source : ede33b1c25a8507cdc6abcc6f4bc697e3acd9f0d
This commit is contained in:
Родитель
f6f94ad8e2
Коммит
189e35c016
|
@ -4,4 +4,5 @@ support-files =
|
|||
head.js
|
||||
|
||||
[browser_screenshots.js]
|
||||
[browser_screenshots_cropping.js]
|
||||
[browser_boundingbox.js]
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/* 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";
|
||||
|
||||
Cu.import("resource://gre/modules/Geometry.jsm", this);
|
||||
|
||||
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"),
|
||||
new Rect(0, 0, 32, 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"),
|
||||
new Rect(16, 16, 32, 32)
|
||||
), 0, "The image should be cropped to the center of the image");
|
||||
|
||||
is(await cropAndCompare(
|
||||
window,
|
||||
"chrome://mozscreenshots/content/lib/robot.png",
|
||||
"chrome://mozscreenshots/content/lib/robot_uncropped.png",
|
||||
OS.Path.join(tmp, "test_uncropped.png"),
|
||||
new Rect(-8, -9, 80, 80)
|
||||
), 0, "The image should be not be cropped, and the cropping region should be clipped to the size of the image");
|
||||
});
|
|
@ -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));
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@ Cu.import("resource://gre/modules/Geometry.jsm");
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserTestUtils",
|
||||
"resource://testing-common/BrowserTestUtils.jsm");
|
||||
|
||||
// Screenshot.jsm must be imported this way for xpcshell tests to work
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Screenshot", "chrome://mozscreenshots/content/Screenshot.jsm");
|
||||
|
||||
// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
|
||||
|
@ -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() {
|
||||
// Clip the cropping region to the size of the screenshot
|
||||
// This is necessary mostly to deal with offscreen windows, since we
|
||||
// are capturing an image of the operating system's desktop.
|
||||
rect.left = Math.max(0, rect.left);
|
||||
rect.right = Math.min(img.naturalWidth, rect.right);
|
||||
rect.top = Math.max(0, rect.top);
|
||||
rect.bottom = Math.min(img.naturalHeight, rect.bottom);
|
||||
|
||||
// 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
|
||||
|
|
|
@ -29,7 +29,7 @@ this.TabsInTitlebar = {
|
|||
},
|
||||
|
||||
tabsOutsideTitlebar: {
|
||||
selectors: ["#navigator-toolbox", "#titlebar"],
|
||||
selectors: ["#navigator-toolbox"].concat(Services.appinfo.OS == "Linux" ? [] : ["#titlebar"]),
|
||||
async applyConfig() {
|
||||
Services.prefs.setBoolPref(PREF_TABS_IN_TITLEBAR, false);
|
||||
},
|
||||
|
|
Двоичные данные
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_uncropped.png
Normal file
Двоичные данные
browser/tools/mozscreenshots/mozscreenshots/extension/lib/robot_uncropped.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 9.6 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 |
Загрузка…
Ссылка в новой задаче