Bug 1356543 - Change test_ext_clipboard_image from xpcshell to mochitest
MozReview-Commit-ID: 9AaCsOKNkza
--- a/toolkit/components/extensions/test/mochitest/mochitest-common.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest-common.ini
@@ -50,16 +50,17 @@ support-files =
file_permission_xhr.html
file_teardown_test.js
return_headers.sjs
webrequest_worker.js
!/toolkit/components/passwordmgr/test/authenticate.sjs
!/dom/tests/mochitest/geolocation/network_geolocation.sjs
[test_ext_clipboard.html]
+[test_ext_clipboard_image.html]
# skip-if = # disabled test case with_permission_allow_copy, see inline comment.
[test_ext_inIncognitoContext_window.html]
skip-if = os == 'android' # Android does not support multiple windows.
[test_ext_geturl.html]
[test_ext_background_canvas.html]
[test_ext_content_security_policy.html]
[test_ext_contentscript_api_injection.html]
[test_ext_contentscript_async_loading.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_clipboard_image.html
@@ -0,0 +1,263 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Clipboard permissions tests</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+ <script src="head.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+
+<script>
+"use strict";
+/**
+ * This cannot be a xpcshell test, because:
+ * - On Android, copyString of nsIClipboardHelper segfaults because
+ * widget/android/nsClipboard.cpp calls java::Clipboard::SetText, which is
+ * unavailable in xpcshell.
+ * - On Windows, the clipboard is unavailable to xpcshell.
+ */
+
+function resetClipboard() {
+ SpecialPowers.clipboardCopyString(
+ "This is the default value of the clipboard in the test.");
+}
+
+async function checkClipboardHasTestImage(imageType) {
+ async function backgroundScript(imageType) {
+ async function verifyImage(img) {
+ // Checks whether the image is a 1x1 red image.
+ browser.test.assertEq(1, img.naturalWidth, "image width should match");
+ browser.test.assertEq(1, img.naturalHeight, "image height should match");
+
+ let canvas = document.createElement("canvas");
+ canvas.width = 1;
+ canvas.height = 1;
+ let ctx = canvas.getContext("2d");
+ ctx.drawImage(img, 0, 0); // Draw without scaling.
+ let [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
+ let expectedColor;
+ if (imageType === "png") {
+ expectedColor = [255, 0, 0];
+ } else if (imageType === "jpeg") {
+ expectedColor = [254, 0, 0];
+ }
+ let {os} = await browser.runtime.getPlatformInfo();
+ if (os === "mac") {
+ // Due to https://bugzil.la/1396587, the pasted image differs from the
+ // original/expected image.
+ // Once that bug is fixed, this whole macOS-only branch can be removed.
+ if (imageType === "png") {
+ expectedColor = [255, 38, 0];
+ } else if (imageType === "jpeg") {
+ expectedColor = [255, 38, 0];
+ }
+ }
+ browser.test.assertEq(expectedColor[0], r, "pixel should be red");
+ browser.test.assertEq(expectedColor[1], g, "pixel should not contain green");
+ browser.test.assertEq(expectedColor[2], b, "pixel should not contain blue");
+ browser.test.assertEq(255, a, "pixel should be opaque");
+ }
+
+ let editable = document.body;
+ editable.contentEditable = true;
+ let file;
+ await new Promise(resolve => {
+ document.addEventListener("paste", function(event) {
+ browser.test.assertEq(1, event.clipboardData.types.length, "expected one type");
+ browser.test.assertEq("Files", event.clipboardData.types[0], "expected type");
+ browser.test.assertEq(1, event.clipboardData.files.length, "expected one file");
+
+ // After returning from the paste event, event.clipboardData is cleaned, so we
+ // have to store the file in a separate variable.
+ file = event.clipboardData.files[0];
+ resolve();
+ }, {once: true});
+
+ document.execCommand("paste"); // requires clipboardWrite permission.
+ });
+
+ // When image data is copied, its first frame is decoded and exported to the
+ // clipboard. The pasted result is always an unanimated PNG file, regardless
+ // of the input.
+ browser.test.assertEq("image/png", file.type, "expected file.type");
+
+ // event.files[0] should be an accurate representation of the input image.
+ {
+ let img = new Image();
+ await new Promise((resolve, reject) => {
+ img.onload = resolve;
+ img.onerror = () => reject(new Error(`Failed to load image ${img.src} of size ${file.size}`));
+ img.src = URL.createObjectURL(file);
+ });
+
+ await verifyImage(img);
+ }
+
+ // This confirms that an image was put on the clipboard.
+ // In contrast, when document.execCommand('copy') + clipboardData.setData
+ // is used, then the 'paste' event will also have the image data (as tested
+ // above), but the contentEditable area will be empty.
+ {
+ let imgs = editable.querySelectorAll("img");
+ browser.test.assertEq(1, imgs.length, "should have pasted one image");
+ await verifyImage(imgs[0]);
+ }
+ browser.test.sendMessage("tested image on clipboard");
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ background: `(${backgroundScript})("${imageType}");`,
+ manifest: {
+ permissions: ["clipboardRead"],
+ },
+ });
+ await extension.startup();
+ await extension.awaitMessage("tested image on clipboard");
+ await extension.unload();
+}
+
+add_task(async function test_without_clipboard_permission() {
+ let extension = ExtensionTestUtils.loadExtension({
+ background() {
+ browser.test.assertEq(undefined, browser.clipboard,
+ "clipboard API requires the clipboardWrite permission.");
+ browser.test.notifyPass();
+ },
+ manifest: {
+ permissions: ["clipboardRead"],
+ },
+ });
+ await extension.startup();
+ await extension.awaitFinish();
+ await extension.unload();
+});
+
+add_task(async function test_copy_png() {
+ if (AppConstants.platform === "android") {
+ return; // Android does not support images on the clipboard.
+ }
+ let extension = ExtensionTestUtils.loadExtension({
+ async background() {
+ // A 1x1 red PNG image.
+ let b64data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABlBMVEX/AAD///9BHTQRAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAACklEQVQImWNgAAAAAgAB9HFkpgAAAABJRU5ErkJggg==";
+ let imageData = Uint8Array.from(atob(b64data), c => c.charCodeAt(0)).buffer;
+ await browser.clipboard.setImageData(imageData, "png");
+ browser.test.sendMessage("Called setImageData with PNG");
+ },
+ manifest: {
+ permissions: ["clipboardWrite"],
+ },
+ });
+
+ resetClipboard();
+
+ await extension.startup();
+ await extension.awaitMessage("Called setImageData with PNG");
+ await extension.unload();
+
+ await checkClipboardHasTestImage("png");
+});
+
+add_task(async function test_copy_jpeg() {
+ if (AppConstants.platform === "android") {
+ return; // Android does not support images on the clipboard.
+ }
+ let extension = ExtensionTestUtils.loadExtension({
+ async background() {
+ // A 1x1 red JPEG image, created using: convert xc:red red.jpg.
+ // JPEG is lossy, and the red pixel value is actually #FE0000 instead of
+ // #FF0000 (also seen using: convert red.jpg text:-).
+ let b64data = "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAABAAEDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACP/EABQQAQAAAAAAAAAAAAAAAAAAAAD/xAAVAQEBAAAAAAAAAAAAAAAAAAAHCf/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/ADoDFU3/2Q==";
+ let imageData = Uint8Array.from(atob(b64data), c => c.charCodeAt(0)).buffer;
+ await browser.clipboard.setImageData(imageData, "jpeg");
+ browser.test.sendMessage("Called setImageData with JPEG");
+ },
+ manifest: {
+ permissions: ["clipboardWrite"],
+ },
+ });
+
+ resetClipboard();
+
+ await extension.startup();
+ await extension.awaitMessage("Called setImageData with JPEG");
+ await extension.unload();
+
+ await checkClipboardHasTestImage("jpeg");
+});
+
+add_task(async function test_copy_invalid_image() {
+ if (AppConstants.platform === "android") {
+ // Android does not support images on the clipboard.
+ return;
+ }
+ let extension = ExtensionTestUtils.loadExtension({
+ async background() {
+ // This is a PNG image.
+ let b64data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABlBMVEX/AAD///9BHTQRAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAACklEQVQImWNgAAAAAgAB9HFkpgAAAABJRU5ErkJggg==";
+ let pngImageData = Uint8Array.from(atob(b64data), c => c.charCodeAt(0)).buffer;
+ await browser.test.assertRejects(
+ browser.clipboard.setImageData(pngImageData, "jpeg"),
+ "Data is not a valid jpeg image",
+ "Image data that is not valid for the given type should be rejected.");
+ browser.test.sendMessage("finished invalid image");
+ },
+ manifest: {
+ permissions: ["clipboardWrite"],
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("finished invalid image");
+ await extension.unload();
+});
+
+add_task(async function test_copy_invalid_image_type() {
+ let extension = ExtensionTestUtils.loadExtension({
+ async background() {
+ // setImageData expects "png" or "jpeg", but we pass "image/png" here.
+ browser.test.assertThrows(
+ () => { browser.clipboard.setImageData(new ArrayBuffer(0), "image/png"); },
+ "Type error for parameter imageType (Invalid enumeration value \"image/png\") for clipboard.setImageData.",
+ "An invalid type for setImageData should be rejected.");
+ browser.test.sendMessage("finished invalid type");
+ },
+ manifest: {
+ permissions: ["clipboardWrite"],
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("finished invalid type");
+ await extension.unload();
+});
+
+if (AppConstants.platform === "android") {
+ add_task(async function test_setImageData_unsupported_on_android() {
+ let extension = ExtensionTestUtils.loadExtension({
+ async background() {
+ // Android does not support images on the clipboard,
+ // so it should not try to decode an image but fail immediately.
+ await browser.test.assertRejects(
+ browser.clipboard.setImageData(new ArrayBuffer(0), "png"),
+ "Writing images to the clipboard is not supported on Android",
+ "Should get an error when setImageData is called on Android.");
+ browser.test.sendMessage("finished unsupported setImageData");
+ },
+ manifest: {
+ permissions: ["clipboardWrite"],
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("finished unsupported setImageData");
+ await extension.unload();
+ });
+}
+
+</script>
+</body>
+</html>
deleted file mode 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_clipboard_image.js
+++ /dev/null
@@ -1,215 +0,0 @@
-/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set sts=2 sw=2 et tw=80: */
-"use strict";
-
-function resetClipboard() {
- // Note: When run as a xpcshell test on Android, the copyString call will
- // cause a segfault due to an invocation of java::Clipboard::SetText
- // in widget/android/nsClipboard.cpp.
- //
- // Since copying images to the clipboard is not supported on Android,
- // we will not run the test on Android and this shouldn't be a problem.
- Components.classes["@mozilla.org/widget/clipboardhelper;1"]
- .getService(Components.interfaces.nsIClipboardHelper)
- .copyString("This is the default value of the clipboard in the test.");
-}
-
-async function checkClipboardHasTestImage(imageType) {
- async function backgroundScript(imageType) {
- async function verifyImage(img) {
- // Checks whether the image is a 1x1 red image.
- browser.test.assertEq(1, img.naturalWidth, "image width should match");
- browser.test.assertEq(1, img.naturalHeight, "image height should match");
-
- let canvas = document.createElement("canvas");
- canvas.width = 1;
- canvas.height = 1;
- let ctx = canvas.getContext("2d");
- ctx.drawImage(img, 0, 0); // Draw without scaling.
- let [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
- let expectedColor;
- if (imageType === "png") {
- expectedColor = [255, 0, 0];
- } else if (imageType === "jpeg") {
- expectedColor = [254, 0, 0];
- }
- let {os} = await browser.runtime.getPlatformInfo();
- if (os === "mac") {
- // Due to https://bugzil.la/1396587, the pasted image differs from the
- // original/expected image.
- // Once that bug is fixed, this whole macOS-only branch can be removed.
- if (imageType === "png") {
- expectedColor = [255, 42, 26];
- } else if (imageType === "jpeg") {
- expectedColor = [254, 42, 26];
- }
- }
- browser.test.assertEq(expectedColor[0], r, "pixel should be red");
- browser.test.assertEq(expectedColor[1], g, "pixel should not contain green");
- browser.test.assertEq(expectedColor[2], b, "pixel should not contain blue");
- browser.test.assertEq(255, a, "pixel should be opaque");
- }
-
- let editable = document.body;
- editable.contentEditable = true;
- let file;
- await new Promise(resolve => {
- document.addEventListener("paste", function(event) {
- browser.test.assertEq(1, event.clipboardData.types.length, "expected one type");
- browser.test.assertEq("Files", event.clipboardData.types[0], "expected type");
- browser.test.assertEq(1, event.clipboardData.files.length, "expected one file");
-
- // After returning from the paste event, event.clipboardData is cleaned, so we
- // have to store the file in a separate variable.
- file = event.clipboardData.files[0];
- resolve();
- }, {once: true});
-
- document.execCommand("paste"); // requires clipboardWrite permission.
- });
-
- // When image data is copied, its first frame is decoded and exported to the
- // clipboard. The pasted result is always an unanimated PNG file, regardless
- // of the input.
- browser.test.assertEq("image/png", file.type, "expected file.type");
-
- // event.files[0] should be an accurate representation of the input image.
- {
- let img = new Image();
- await new Promise((resolve, reject) => {
- img.onload = resolve;
- img.onerror = () => reject(new Error(`Failed to load image ${img.src} of size ${file.size}`));
- img.src = URL.createObjectURL(file);
- });
-
- await verifyImage(img);
- }
-
- // This confirms that an image was put on the clipboard.
- // In contrast, when document.execCommand('copy') + clipboardData.setData
- // is used, then the 'paste' event will also have the image data (as tested
- // above), but the contentEditable area will be empty.
- {
- let imgs = editable.querySelectorAll("img");
- browser.test.assertEq(1, imgs.length, "should have pasted one image");
- await verifyImage(imgs[0]);
- }
- browser.test.sendMessage("tested image on clipboard");
- }
-
- let extension = ExtensionTestUtils.loadExtension({
- background: `(${backgroundScript})("${imageType}");`,
- manifest: {
- permissions: ["clipboardRead"],
- },
- });
- await extension.startup();
- await extension.awaitMessage("tested image on clipboard");
- await extension.unload();
-}
-
-add_task(async function test_without_clipboard_permission() {
- let extension = ExtensionTestUtils.loadExtension({
- background() {
- browser.test.assertEq(undefined, browser.clipboard,
- "clipboard API requires the clipboardWrite permission.");
- browser.test.notifyPass();
- },
- manifest: {
- permissions: ["clipboardRead"],
- },
- });
- await extension.startup();
- await extension.awaitFinish();
- await extension.unload();
-});
-
-add_task(async function test_copy_png() {
- let extension = ExtensionTestUtils.loadExtension({
- async background() {
- // A 1x1 red PNG image.
- let b64data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABlBMVEX/AAD///9BHTQRAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAACklEQVQImWNgAAAAAgAB9HFkpgAAAABJRU5ErkJggg==";
- let imageData = Uint8Array.from(atob(b64data), c => c.charCodeAt(0)).buffer;
- await browser.clipboard.setImageData(imageData, "png");
- browser.test.sendMessage("Called setImageData with PNG");
- },
- manifest: {
- permissions: ["clipboardWrite"],
- },
- });
-
- resetClipboard();
-
- await extension.startup();
- await extension.awaitMessage("Called setImageData with PNG");
- await extension.unload();
-
- await checkClipboardHasTestImage("png");
-});
-
-add_task(async function test_copy_jpeg() {
- let extension = ExtensionTestUtils.loadExtension({
- async background() {
- // A 1x1 red JPEG image, created using: convert xc:red red.jpg.
- // JPEG is lossy, and the red pixel value is actually #FE0000 instead of
- // #FF0000 (also seen using: convert red.jpg text:-).
- let b64data = "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAABAAEDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACP/EABQQAQAAAAAAAAAAAAAAAAAAAAD/xAAVAQEBAAAAAAAAAAAAAAAAAAAHCf/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/ADoDFU3/2Q==";
- let imageData = Uint8Array.from(atob(b64data), c => c.charCodeAt(0)).buffer;
- await browser.clipboard.setImageData(imageData, "jpeg");
- browser.test.sendMessage("Called setImageData with JPEG");
- },
- manifest: {
- permissions: ["clipboardWrite"],
- },
- });
-
- resetClipboard();
-
- await extension.startup();
- await extension.awaitMessage("Called setImageData with JPEG");
- await extension.unload();
-
- await checkClipboardHasTestImage("jpeg");
-});
-
-add_task(async function test_copy_invalid_image() {
- let extension = ExtensionTestUtils.loadExtension({
- async background() {
- // This is a PNG image.
- let b64data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABlBMVEX/AAD///9BHTQRAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAACklEQVQImWNgAAAAAgAB9HFkpgAAAABJRU5ErkJggg==";
- let pngImageData = Uint8Array.from(atob(b64data), c => c.charCodeAt(0)).buffer;
- await browser.test.assertRejects(
- browser.clipboard.setImageData(pngImageData, "jpeg"),
- "Data is not a valid jpeg image",
- "Image data that is not valid for the given type should be rejected.");
- browser.test.sendMessage("finished invalid image");
- },
- manifest: {
- permissions: ["clipboardWrite"],
- },
- });
-
- await extension.startup();
- await extension.awaitMessage("finished invalid image");
- await extension.unload();
-});
-
-add_task(async function test_copy_invalid_image_type() {
- let extension = ExtensionTestUtils.loadExtension({
- async background() {
- // setImageData expects "png" or "jpeg", but we pass "image/png" here.
- browser.test.assertThrows(
- () => { browser.clipboard.setImageData(new ArrayBuffer(0), "image/png"); },
- "Type error for parameter imageType (Invalid enumeration value \"image/png\") for clipboard.setImageData.",
- "An invalid type for setImageData should be rejected.");
- browser.test.sendMessage("finished invalid type");
- },
- manifest: {
- permissions: ["clipboardWrite"],
- },
- });
-
- await extension.startup();
- await extension.awaitMessage("finished invalid type");
- await extension.unload();
-});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
@@ -9,18 +9,16 @@
skip-if = os == "android" # Android does not use Places for history.
[test_ext_background_private_browsing.js]
[test_ext_background_runtime_connect_params.js]
[test_ext_background_sub_windows.js]
[test_ext_background_telemetry.js]
[test_ext_background_window_properties.js]
skip-if = os == "android"
[test_ext_browserSettings.js]
-[test_ext_clipboard_image.js]
-skip-if = os == "android" # Android does not support images on the clipboard.
[test_ext_contextual_identities.js]
skip-if = os == "android" # Containers are not exposed to android.
[test_ext_debugging_utils.js]
[test_ext_downloads.js]
[test_ext_downloads_download.js]
skip-if = os == "android"
[test_ext_downloads_misc.js]
skip-if = os == "android" || (os=='linux' && bits==32) # linux32: bug 1324870