From a79c4ec8c5077f5d579b4d6fda855c2f31ce8365 Mon Sep 17 00:00:00 2001 From: kkapsner Date: Tue, 19 May 2020 10:48:08 +0200 Subject: [PATCH] Added test for blob, offscreen canvas and offscreen canvas in workers --- test/canvasAPI.js | 4 ++-- test/test.html | 18 +++++++++++++++ test/test.js | 56 ++++++++++++++++++++++++++++++++++++++++++----- test/testAPI.js | 48 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 7 deletions(-) diff --git a/test/canvasAPI.js b/test/canvasAPI.js index 0979518..38d8f21 100644 --- a/test/canvasAPI.js +++ b/test/canvasAPI.js @@ -2,8 +2,8 @@ const canvasAPI = { draw: function draw(canvas){ "use strict"; - canvas.setAttribute("width", 220); - canvas.setAttribute("height", 30); + canvas.width = 220; + canvas.height = 30; const fingerprintText = "BrowserLeaks,com 10"; diff --git a/test/test.html b/test/test.html index c2814fd..4c51cb3 100644 --- a/test/test.html +++ b/test/test.html @@ -71,6 +71,24 @@ Hash: click anywhere to populate (isPointInPath: ) +
+

blob Test

+
+ Hash: + +
+
+

offscreen Test

+
+ Hash: + +
+
+

offscreen Worker Test

+
+ Hash: + +
diff --git a/test/test.js b/test/test.js index 91aba09..a5714b8 100644 --- a/test/test.js +++ b/test/test.js @@ -8,12 +8,14 @@ display.title = url; const hashes = await Promise.all([ testAPI.hash(url), - testAPI.hash(imageData.data) + imageData? testAPI.hash(imageData.data): "" ]); container.querySelector(".hash").textContent = hashes[0] + " / " + hashes[1]; - container.querySelector(".isPointInPath").textContent = isPointInPath; + if (typeof isPointInPath === "boolean"){ + container.querySelector(".isPointInPath").textContent = isPointInPath; + } } function iframeTest(testNode){ @@ -51,15 +53,47 @@ newWindow.close(); return fingerprint; } - } + }, + blob: async function(testNode){ + const canvas = document.createElement("canvas"); + canvasAPI.draw(canvas); + return new Promise(function(resolve, reject){ + canvas.toBlob(async function(blob){ + resolve({url: await testAPI.readBlob(blob)}); + }); + }); + }, + offscreen: async function(testNode){ + if (typeof OffscreenCanvas === "undefined"){ + throw "not supported"; + } + return offscreenTest(); + }, + offscreenWorker: async function(testNode){ + if ( + typeof OffscreenCanvas === "undefined" || + typeof Worker === "undefined" + ){ + throw "not supported"; + } + const canvasAPIUrl = new URL("./canvasAPI.js", location); + const testAPIUrl = new URL("./testAPI.js", location); + return testAPI.runInWorker(offscreenTest, [], [canvasAPIUrl, testAPIUrl]); + }, }; Object.keys(tests).forEach(async function(testName){ const testNode = document.getElementById(testName); const callback = tests[testName]; if (location.search !== "?notInitial"){ - try {show(testNode, await callback(testNode, true));} - catch (error){console.error(testName, error);} + try { + show(testNode, await callback(testNode, true)); + } + catch (error){ + testNode.querySelector(".hash").innerHTML = + "Error while computing: " + (error.message || error) + ""; + console.error(testName, error); + } } testNode.querySelector("button").addEventListener("click", async function(){ show(testNode, await callback(testNode)); @@ -99,4 +133,16 @@ function dynamicIframeTest3(){ const iframeWindow = window[length]; document.body.removeChild(div); return canvasAPI.fingerprint(iframeWindow); +} + +async function offscreenTest(){ + "use strict"; + + const offscreenCanvas = new OffscreenCanvas(220, 30); + canvasAPI.draw(offscreenCanvas); + const blob = offscreenCanvas.convertToBlob? + await offscreenCanvas.convertToBlob(): + await offscreenCanvas.toBlob(); + + return {url: await testAPI.readBlob(blob)}; } \ No newline at end of file diff --git a/test/testAPI.js b/test/testAPI.js index e09b036..fe96f47 100644 --- a/test/testAPI.js +++ b/test/testAPI.js @@ -18,6 +18,54 @@ const testAPI = function(){ return { + runInWorker: async function(func, args = [], scripts = []){ + return new Promise(function(resolve, reject){ + const apis = scripts.map(function(script){ + switch (typeof script){ + case "function": + return script.toString(); + case "string": + case "object": + return "self.importScripts(" + JSON.stringify(script.toString()) + ")"; + default: + return ""; + } + }).join(";\n"); + const code = `${apis}; + ${func.toString()} + async function run(){ + return ${func.name}(${args.map(JSON.stringify).join(", ")}); + } + run().then(function(result){ + self.postMessage({result}); + }).catch(function(error){ + console.log(error); + self.postMessage({error: error.message || error.toString()}); + });`; + const blob = new Blob([code], {type: "text/javascript"}); + + const blobWorker = new Worker(URL.createObjectURL(blob), {name: "BlobWorker"}); + blobWorker.addEventListener("message", function(event){ + if (event.data.error){ + reject(event.data.error); + } + else { + resolve(event.data.result); + } + blobWorker.terminate(); + }); + }); + }, + readBlob: async function(blob){ + const reader = new FileReader(); + return new Promise(function(resolve, reject){ + reader.addEventListener("error", reject); + reader.addEventListener("load", function(){ + resolve(reader.result); + }); + reader.readAsDataURL(blob); + }); + }, hash: async function(input){ const buffer = ((typeof input) === "string")? new TextEncoder("utf-8").encode(input):