michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: "use strict"; michael@0: michael@0: require("sdk/clipboard"); michael@0: michael@0: const { Cc, Ci } = require("chrome"); michael@0: michael@0: const imageTools = Cc["@mozilla.org/image/tools;1"]. michael@0: getService(Ci.imgITools); michael@0: michael@0: const io = Cc["@mozilla.org/network/io-service;1"]. michael@0: getService(Ci.nsIIOService); michael@0: michael@0: const base64png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA" + michael@0: "AABzenr0AAAASUlEQVRYhe3O0QkAIAwD0eyqe3Q993AQ3cBSUKpygfsNTy" + michael@0: "N5ugbQpK0BAADgP0BRDWXWlwEAAAAAgPsA3rzDaAAAAHgPcGrpgAnzQ2FG" + michael@0: "bWRR9AAAAABJRU5ErkJggg%3D%3D"; michael@0: michael@0: const base64jpeg = "data:image/jpeg;base64,%2F9j%2F4AAQSkZJRgABAQAAAQABAAD%2F" + michael@0: "2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCg" + michael@0: "sOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD%2F2wBDAQMDAwQDBAgEBAgQCw" + michael@0: "kLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ" + michael@0: "EBAQEBAQEBD%2FwAARCAAgACADAREAAhEBAxEB%2F8QAHwAAAQUBAQEBAQ" + michael@0: "EAAAAAAAAAAAECAwQFBgcICQoL%2F8QAtRAAAgEDAwIEAwUFBAQAAAF9AQ" + michael@0: "IDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRol" + michael@0: "JicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eX" + michael@0: "qDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJ" + michael@0: "ytLT1NXW19jZ2uHi4%2BTl5ufo6erx8vP09fb3%2BPn6%2F8QAHwEAAwEB" + michael@0: "AQEBAQEBAQAAAAAAAAECAwQFBgcICQoL%2F8QAtREAAgECBAQDBAcFBAQA" + michael@0: "AQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNO" + michael@0: "El8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0" + michael@0: "dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6ws" + michael@0: "PExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3%2BPn6%2F9oADAMB" + michael@0: "AAIRAxEAPwD5Kr8kP9CwoA5f4m%2F8iRqX%2FbH%2FANHJXr5F%2FwAjCn" + michael@0: "8%2F%2FSWfnnir%2FwAkji%2F%2B4f8A6dgeD1%2BiH8bn1BX5If6FmFqW" + michael@0: "pXtveyQwzbUXGBtB7D2r9l4U4UyjMsoo4rFUeacua75pLaUktFJLZH5NxN" + michael@0: "xNmmX5pVw2Gq8sI8tlyxe8U3q03uzD8S3dxqOi3NneSeZDJs3LgDOHBHI5" + michael@0: "6gV%2BkcG%2BH%2FDmJzuhSq4e8XzfbqfyS%2FvH5rx1xTm2MyDEUa1W8X" + michael@0: "yXXLFbTi%2BkThv7B0r%2FAJ9f%2FH2%2Fxr90%2FwCIVcI%2F9An%2FAJ" + michael@0: "Uq%2FwDyZ%2FO%2F16v%2FADfgv8j0r%2FhZvgj%2FAKDf%2FktN%2FwDE" + michael@0: "V%2Fnr%2FYWYf8%2B%2Fxj%2Fmf3R%2FxFXhH%2FoL%2FwDKdX%2F5Azrv" + michael@0: "xLouo3D3lne%2BZDJja3luM4GDwRnqDX9LeH%2FBud4nhzD1aVC8Xz%2Fa" + michael@0: "h%2Fz8l%2FePx%2FinjrIMZm1WtRxF4vls%2BSa2jFdYlDUdRsp7OSKKbc" + michael@0: "7YwNpHce1fqfCvCub5bm9HFYqjywjzXfNF7xklopN7s%2BC4l4lyvMMrq4" + michael@0: "fD1bzfLZcsltJPqktkYlfsZ%2BUnBV%2FnufVnXaD%2FAMgqD%2FgX%2Fo" + michael@0: "Rr%2BxvCr%2FkkcJ%2F3E%2F8ATsz5%2FHfx5fL8kX6%2FQjkCgD%2F%2F" + michael@0: "2Q%3D%3D"; michael@0: michael@0: const canvasHTML = "data:text/html," + encodeURIComponent( michael@0: "\ michael@0: \ michael@0: \ michael@0: \ michael@0: " michael@0: ); michael@0: michael@0: function comparePixelImages(imageA, imageB, callback) { michael@0: let tabs = require("sdk/tabs"); michael@0: michael@0: tabs.open({ michael@0: url: canvasHTML, michael@0: michael@0: onReady: function onReady(tab) { michael@0: let worker = tab.attach({ michael@0: contentScript: "new " + function() { michael@0: let canvas = document.querySelector("canvas"); michael@0: let context = canvas.getContext("2d"); michael@0: michael@0: self.port.on("draw-image", function(imageURI) { michael@0: let img = new Image(); michael@0: michael@0: img.onload = function() { michael@0: context.drawImage(this, 0, 0); michael@0: michael@0: let pixels = Array.join(context.getImageData(0, 0, 32, 32).data); michael@0: self.port.emit("image-pixels", pixels); michael@0: } michael@0: michael@0: img.src = imageURI; michael@0: }); michael@0: } michael@0: }); michael@0: michael@0: let compared = ""; michael@0: michael@0: worker.port.on("image-pixels", function (pixels) { michael@0: if (!compared) { michael@0: compared = pixels; michael@0: this.emit("draw-image", imageB); michael@0: } else { michael@0: callback(compared === pixels); michael@0: tab.close() michael@0: } michael@0: }); michael@0: michael@0: worker.port.emit("draw-image", imageA); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: michael@0: // Test the typical use case, setting & getting with no flavors specified michael@0: exports["test With No Flavor"] = function(assert) { michael@0: var contents = "hello there"; michael@0: var flavor = "text"; michael@0: var fullFlavor = "text/unicode"; michael@0: var clip = require("sdk/clipboard"); michael@0: michael@0: // Confirm we set the clipboard michael@0: assert.ok(clip.set(contents)); michael@0: michael@0: // Confirm flavor is set michael@0: assert.equal(clip.currentFlavors[0], flavor); michael@0: michael@0: // Confirm we set the clipboard michael@0: assert.equal(clip.get(), contents); michael@0: michael@0: // Confirm we can get the clipboard using the flavor michael@0: assert.equal(clip.get(flavor), contents); michael@0: michael@0: // Confirm we can still get the clipboard using the full flavor michael@0: assert.equal(clip.get(fullFlavor), contents); michael@0: }; michael@0: michael@0: // Test the slightly less common case where we specify the flavor michael@0: exports["test With Flavor"] = function(assert) { michael@0: var contents = "hello there"; michael@0: var contentsText = "hello there"; michael@0: var flavor = "html"; michael@0: var fullFlavor = "text/html"; michael@0: var unicodeFlavor = "text"; michael@0: var unicodeFullFlavor = "text/unicode"; michael@0: var clip = require("sdk/clipboard"); michael@0: michael@0: assert.ok(clip.set(contents, flavor)); michael@0: michael@0: assert.equal(clip.currentFlavors[0], unicodeFlavor); michael@0: assert.equal(clip.currentFlavors[1], flavor); michael@0: assert.equal(clip.get(), contentsText); michael@0: assert.equal(clip.get(flavor), contents); michael@0: assert.equal(clip.get(fullFlavor), contents); michael@0: assert.equal(clip.get(unicodeFlavor), contentsText); michael@0: assert.equal(clip.get(unicodeFullFlavor), contentsText); michael@0: }; michael@0: michael@0: // Test that the typical case still works when we specify the flavor to set michael@0: exports["test With Redundant Flavor"] = function(assert) { michael@0: var contents = "hello there"; michael@0: var flavor = "text"; michael@0: var fullFlavor = "text/unicode"; michael@0: var clip = require("sdk/clipboard"); michael@0: michael@0: assert.ok(clip.set(contents, flavor)); michael@0: assert.equal(clip.currentFlavors[0], flavor); michael@0: assert.equal(clip.get(), contents); michael@0: assert.equal(clip.get(flavor), contents); michael@0: assert.equal(clip.get(fullFlavor), contents); michael@0: }; michael@0: michael@0: exports["test Not In Flavor"] = function(assert) { michael@0: var contents = "hello there"; michael@0: var flavor = "html"; michael@0: var clip = require("sdk/clipboard"); michael@0: michael@0: assert.ok(clip.set(contents)); michael@0: // If there's nothing on the clipboard with this flavor, should return null michael@0: assert.equal(clip.get(flavor), null); michael@0: }; michael@0: michael@0: exports["test Set Image"] = function(assert) { michael@0: var clip = require("sdk/clipboard"); michael@0: var flavor = "image"; michael@0: var fullFlavor = "image/png"; michael@0: michael@0: assert.ok(clip.set(base64png, flavor), "clipboard set"); michael@0: assert.equal(clip.currentFlavors[0], flavor, "flavor is set"); michael@0: }; michael@0: michael@0: exports["test Get Image"] = function(assert, done) { michael@0: var clip = require("sdk/clipboard"); michael@0: michael@0: clip.set(base64png, "image"); michael@0: michael@0: var contents = clip.get(); michael@0: michael@0: comparePixelImages(base64png, contents, function (areEquals) { michael@0: assert.ok(areEquals, michael@0: "Image gets from clipboard equals to image sets to the clipboard"); michael@0: michael@0: done(); michael@0: }); michael@0: } michael@0: michael@0: exports["test Set Image Type Not Supported"] = function(assert) { michael@0: var clip = require("sdk/clipboard"); michael@0: var flavor = "image"; michael@0: michael@0: assert.throws(function () { michael@0: clip.set(base64jpeg, flavor); michael@0: }, "Invalid flavor for image/jpeg"); michael@0: michael@0: }; michael@0: michael@0: // Notice that `imageTools.decodeImageData`, used by `clipboard.set` method for michael@0: // images, write directly to the javascript console the error in case the image michael@0: // is corrupt, even if the error is catched. michael@0: // michael@0: // See: http://mxr.mozilla.org/mozilla-central/source/image/src/Decoder.cpp#136 michael@0: exports["test Set Image Type Wrong Data"] = function(assert) { michael@0: var clip = require("sdk/clipboard"); michael@0: var flavor = "image"; michael@0: michael@0: var wrongPNG = "data:image/png" + base64jpeg.substr(15); michael@0: michael@0: assert.throws(function () { michael@0: clip.set(wrongPNG, flavor); michael@0: }, "Unable to decode data given in a valid image."); michael@0: }; michael@0: michael@0: require("test").run(exports)