diff options
Diffstat (limited to 'devtools/client/commandline/test/browser_cmd_screenshot.js')
-rw-r--r-- | devtools/client/commandline/test/browser_cmd_screenshot.js | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/devtools/client/commandline/test/browser_cmd_screenshot.js b/devtools/client/commandline/test/browser_cmd_screenshot.js new file mode 100644 index 000000000..e7f3d0587 --- /dev/null +++ b/devtools/client/commandline/test/browser_cmd_screenshot.js @@ -0,0 +1,374 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* global helpers, btoa, whenDelayedStartupFinished, OpenBrowserWindow */ + +// Test that screenshot command works properly + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/commandline/" + + "test/browser_cmd_screenshot.html"; + +var FileUtils = (Cu.import("resource://gre/modules/FileUtils.jsm", {})).FileUtils; + +function test() { + // This test gets bombarded by a cascade of GCs and often takes 50s so lets be + // safe and give the test 90s to run. + requestLongerTimeout(3); + + return Task.spawn(spawnTest).then(finish, helpers.handleError); +} + +function* spawnTest() { + waitForExplicitFinish(); + + info("RUN TEST: non-private window"); + let normWin = yield addWindow({ private: false }); + yield addTabWithToolbarRunTests(normWin); + normWin.close(); + + info("RUN TEST: private window"); + let pbWin = yield addWindow({ private: true }); + yield addTabWithToolbarRunTests(pbWin); + pbWin.close(); +} + +function* addTabWithToolbarRunTests(win) { + let options = yield helpers.openTab(TEST_URI, { chromeWindow: win }); + let browser = options.browser; + yield helpers.openToolbar(options); + + // Test input status + yield helpers.audit(options, [ + { + setup: "screenshot", + check: { + input: "screenshot", + markup: "VVVVVVVVVV", + status: "VALID", + args: { + } + }, + }, + { + setup: "screenshot abc.png", + check: { + input: "screenshot abc.png", + markup: "VVVVVVVVVVVVVVVVVV", + status: "VALID", + args: { + filename: { value: "abc.png"}, + } + }, + }, + { + setup: "screenshot --fullpage", + check: { + input: "screenshot --fullpage", + markup: "VVVVVVVVVVVVVVVVVVVVV", + status: "VALID", + args: { + fullpage: { value: true}, + } + }, + }, + { + setup: "screenshot abc --delay 5", + check: { + input: "screenshot abc --delay 5", + markup: "VVVVVVVVVVVVVVVVVVVVVVVV", + status: "VALID", + args: { + filename: { value: "abc"}, + delay: { value: 5 }, + } + }, + }, + { + setup: "screenshot --selector img#testImage", + check: { + input: "screenshot --selector img#testImage", + markup: "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV", + status: "VALID", + }, + }, + ]); + + // Test capture to file + let file = FileUtils.getFile("TmpD", [ "TestScreenshotFile.png" ]); + + yield helpers.audit(options, [ + { + setup: "screenshot " + file.path, + check: { + args: { + filename: { value: "" + file.path }, + fullpage: { value: false }, + clipboard: { value: false }, + }, + }, + exec: { + output: new RegExp("^Saved to "), + }, + post: function () { + // Bug 849168: screenshot command tests fail in try but not locally + // ok(file.exists(), "Screenshot file exists"); + + if (file.exists()) { + file.remove(false); + } + } + }, + ]); + + // Test capture to clipboard + yield helpers.audit(options, [ + { + setup: "screenshot --clipboard", + check: { + args: { + clipboard: { value: true }, + }, + }, + exec: { + output: new RegExp("^Copied to clipboard.$"), + }, + post: Task.async(function* () { + let imgSize1 = yield getImageSizeFromClipboard(); + yield ContentTask.spawn(browser, imgSize1, function* (imgSize) { + Assert.equal(imgSize.width, content.innerWidth, + "Image width matches window size"); + Assert.equal(imgSize.height, content.innerHeight, + "Image height matches window size"); + }); + }) + }, + { + setup: "screenshot --fullpage --clipboard", + check: { + args: { + fullpage: { value: true }, + clipboard: { value: true }, + }, + }, + exec: { + output: new RegExp("^Copied to clipboard.$"), + }, + post: Task.async(function* () { + let imgSize1 = yield getImageSizeFromClipboard(); + yield ContentTask.spawn(browser, imgSize1, function* (imgSize) { + Assert.equal(imgSize.width, + content.innerWidth + content.scrollMaxX - content.scrollMinX, + "Image width matches page size"); + Assert.equal(imgSize.height, + content.innerHeight + content.scrollMaxY - content.scrollMinY, + "Image height matches page size"); + }); + }) + }, + { + setup: "screenshot --selector img#testImage --clipboard", + check: { + args: { + clipboard: { value: true }, + }, + }, + exec: { + output: new RegExp("^Copied to clipboard.$"), + }, + post: Task.async(function* () { + let imgSize1 = yield getImageSizeFromClipboard(); + yield ContentTask.spawn(browser, imgSize1, function* (imgSize) { + let img = content.document.querySelector("img#testImage"); + Assert.equal(imgSize.width, img.clientWidth, + "Image width matches element size"); + Assert.equal(imgSize.height, img.clientHeight, + "Image height matches element size"); + }); + }) + }, + ]); + + // Trigger scrollbars by forcing document to overflow + // This only affects results on OSes with scrollbars that reduce document size + // (non-floating scrollbars). With default OS settings, this means Windows + // and Linux are affected, but Mac is not. For Mac to exhibit this behavior, + // change System Preferences -> General -> Show scroll bars to Always. + yield ContentTask.spawn(browser, {}, function* () { + content.document.body.classList.add("overflow"); + }); + + let scrollbarSize = yield ContentTask.spawn(browser, {}, function* () { + const winUtils = content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + let scrollbarHeight = {}; + let scrollbarWidth = {}; + winUtils.getScrollbarSize(true, scrollbarWidth, scrollbarHeight); + return { + width: scrollbarWidth.value, + height: scrollbarHeight.value, + }; + }); + + info(`Scrollbar size: ${scrollbarSize.width}x${scrollbarSize.height}`); + + // Test capture to clipboard in presence of scrollbars + yield helpers.audit(options, [ + { + setup: "screenshot --clipboard", + check: { + args: { + clipboard: { value: true }, + }, + }, + exec: { + output: new RegExp("^Copied to clipboard.$"), + }, + post: Task.async(function* () { + let imgSize1 = yield getImageSizeFromClipboard(); + imgSize1.scrollbarWidth = scrollbarSize.width; + imgSize1.scrollbarHeight = scrollbarSize.height; + yield ContentTask.spawn(browser, imgSize1, function* (imgSize) { + Assert.equal(imgSize.width, content.innerWidth - imgSize.scrollbarWidth, + "Image width matches window size minus scrollbar size"); + Assert.equal(imgSize.height, content.innerHeight - imgSize.scrollbarHeight, + "Image height matches window size minus scrollbar size"); + }); + }) + }, + { + setup: "screenshot --fullpage --clipboard", + check: { + args: { + fullpage: { value: true }, + clipboard: { value: true }, + }, + }, + exec: { + output: new RegExp("^Copied to clipboard.$"), + }, + post: Task.async(function* () { + let imgSize1 = yield getImageSizeFromClipboard(); + imgSize1.scrollbarWidth = scrollbarSize.width; + imgSize1.scrollbarHeight = scrollbarSize.height; + yield ContentTask.spawn(browser, imgSize1, function* (imgSize) { + Assert.equal(imgSize.width, + (content.innerWidth + content.scrollMaxX - + content.scrollMinX) - imgSize.scrollbarWidth, + "Image width matches page size minus scrollbar size"); + Assert.equal(imgSize.height, + (content.innerHeight + content.scrollMaxY - + content.scrollMinY) - imgSize.scrollbarHeight, + "Image height matches page size minus scrollbar size"); + }); + }) + }, + { + setup: "screenshot --selector img#testImage --clipboard", + check: { + args: { + clipboard: { value: true }, + }, + }, + exec: { + output: new RegExp("^Copied to clipboard.$"), + }, + post: Task.async(function* () { + let imgSize1 = yield getImageSizeFromClipboard(); + yield ContentTask.spawn(browser, imgSize1, function* (imgSize) { + let img = content.document.querySelector("img#testImage"); + Assert.equal(imgSize.width, img.clientWidth, + "Image width matches element size"); + Assert.equal(imgSize.height, img.clientHeight, + "Image height matches element size"); + }); + }) + }, + ]); + + yield helpers.closeToolbar(options); + yield helpers.closeTab(options); +} + +function addWindow(windowOptions) { + return new Promise(resolve => { + let win = OpenBrowserWindow(windowOptions); + + // This feels hacky, we should refactor it + whenDelayedStartupFinished(win, () => { + // Would like to get rid of this executeSoon, but without it the url + // (TEST_URI) provided in addTabWithToolbarRunTests hasn't loaded + executeSoon(() => { + resolve(win); + }); + }); + }); +} + +let getImageSizeFromClipboard = Task.async(function* () { + let clipid = Ci.nsIClipboard; + let clip = Cc["@mozilla.org/widget/clipboard;1"].getService(clipid); + let trans = Cc["@mozilla.org/widget/transferable;1"] + .createInstance(Ci.nsITransferable); + let flavor = "image/png"; + trans.init(null); + trans.addDataFlavor(flavor); + + clip.getData(trans, clipid.kGlobalClipboard); + let data = new Object(); + let dataLength = new Object(); + trans.getTransferData(flavor, data, dataLength); + + ok(data.value, "screenshot exists"); + ok(dataLength.value > 0, "screenshot has length"); + + let image = data.value; + let dataURI = `data:${flavor};base64,`; + + // Due to the differences in how images could be stored in the clipboard the + // checks below are needed. The clipboard could already provide the image as + // byte streams, but also as pointer, or as image container. If it's not + // possible obtain a byte stream, the function returns `null`. + if (image instanceof Ci.nsISupportsInterfacePointer) { + image = image.data; + } + + if (image instanceof Ci.imgIContainer) { + image = Cc["@mozilla.org/image/tools;1"] + .getService(Ci.imgITools) + .encodeImage(image, flavor); + } + + if (image instanceof Ci.nsIInputStream) { + let binaryStream = Cc["@mozilla.org/binaryinputstream;1"] + .createInstance(Ci.nsIBinaryInputStream); + binaryStream.setInputStream(image); + let rawData = binaryStream.readBytes(binaryStream.available()); + let charCodes = Array.from(rawData, c => c.charCodeAt(0) & 0xff); + let encodedData = String.fromCharCode(...charCodes); + encodedData = btoa(encodedData); + dataURI = dataURI + encodedData; + } else { + throw new Error("Unable to read image data"); + } + + let img = document.createElementNS("http://www.w3.org/1999/xhtml", "img"); + + let loaded = new Promise(resolve => { + img.addEventListener("load", function onLoad() { + img.removeEventListener("load", onLoad); + resolve(); + }); + }); + + img.src = dataURI; + document.documentElement.appendChild(img); + yield loaded; + img.remove(); + + return { + width: img.width, + height: img.height, + }; +}); |