From 59022195f7efc02205c7da50224c392e057b597e Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Tue, 18 Apr 2023 02:54:52 +0800 Subject: [PATCH] add string tags for browser polyglot classes (#2214) * add string tags * set toStringTag property to configurable: true --- CHANGELOG.md | 1 + lib/bindings.js | 30 ++++++++++++++++++++++++ test/canvas.test.js | 22 ++++++++++++++++- test/image.test.js | 5 ++++ test/imageData.test.js | 5 ++++ test/wpt/generated/the-canvas-element.js | 6 +++++ 6 files changed, 68 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be424e687..b3ba9a13b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ project adheres to [Semantic Versioning](http://semver.org/). * Avoid calling virtual methods in constructors/destructors to avoid bypassing virtual dispatch. (#2229) * Remove unused private field `backend` in the `Backend` class. (#2229) ### Added +* Added string tags to support class detection ### Fixed * Fix a case of use-after-free. (#2229) * Fix usage of garbage value by filling the allocated memory entirely with zeros if it's not modified. (#2229) diff --git a/lib/bindings.js b/lib/bindings.js index c638a5878..40cef3c69 100644 --- a/lib/bindings.js +++ b/lib/bindings.js @@ -4,10 +4,40 @@ const bindings = require('../build/Release/canvas.node') module.exports = bindings +Object.defineProperty(bindings.Canvas.prototype, Symbol.toStringTag, { + value: 'HTMLCanvasElement', + configurable: true +}) + +Object.defineProperty(bindings.Image.prototype, Symbol.toStringTag, { + value: 'HTMLImageElement', + configurable: true +}) + bindings.ImageData.prototype.toString = function () { return '[object ImageData]' } +Object.defineProperty(bindings.ImageData.prototype, Symbol.toStringTag, { + value: 'ImageData', + configurable: true +}) + bindings.CanvasGradient.prototype.toString = function () { return '[object CanvasGradient]' } + +Object.defineProperty(bindings.CanvasGradient.prototype, Symbol.toStringTag, { + value: 'CanvasGradient', + configurable: true +}) + +Object.defineProperty(bindings.CanvasPattern.prototype, Symbol.toStringTag, { + value: 'CanvasPattern', + configurable: true +}) + +Object.defineProperty(bindings.CanvasRenderingContext2d.prototype, Symbol.toStringTag, { + value: 'CanvasRenderingContext2d', + configurable: true +}) diff --git a/test/canvas.test.js b/test/canvas.test.js index 083459ab6..9573688f5 100644 --- a/test/canvas.test.js +++ b/test/canvas.test.js @@ -1410,6 +1410,14 @@ describe('Canvas', function () { assert.strictEqual(pattern.toString(), '[object CanvasPattern]') }) + it('CanvasPattern has class string of `CanvasPattern`', async function () { + const img = await loadImage(path.join(__dirname, '/fixtures/checkers.png')); + const canvas = createCanvas(20, 20) + const ctx = canvas.getContext('2d') + const pattern = ctx.createPattern(img) + assert.strictEqual(Object.prototype.toString.call(pattern), '[object CanvasPattern]') + }) + it('Context2d#createLinearGradient()', function () { const canvas = createCanvas(20, 1) const ctx = canvas.getContext('2d') @@ -1439,6 +1447,11 @@ describe('Canvas', function () { assert.equal(0, imageData.data[i + 2]) assert.equal(255, imageData.data[i + 3]) }) + it('Canvas has class string of `HTMLCanvasElement`', function () { + const canvas = createCanvas(20, 1) + + assert.strictEqual(Object.prototype.toString.call(canvas), '[object HTMLCanvasElement]') + }) it('CanvasGradient stringifies as [object CanvasGradient]', function () { const canvas = createCanvas(20, 1) @@ -1447,6 +1460,13 @@ describe('Canvas', function () { assert.strictEqual(gradient.toString(), '[object CanvasGradient]') }) + it('CanvasGradient has class string of `CanvasGradient`', function () { + const canvas = createCanvas(20, 1) + const ctx = canvas.getContext('2d') + const gradient = ctx.createLinearGradient(1, 1, 19, 1) + assert.strictEqual(Object.prototype.toString.call(gradient), '[object CanvasGradient]') + }) + describe('Context2d#putImageData()', function () { it('throws for invalid arguments', function () { const canvas = createCanvas(2, 1) @@ -1943,7 +1963,7 @@ describe('Canvas', function () { ctx[k] = v ctx.restore() assert.strictEqual(ctx[k], old) - + // save() doesn't modify the value: ctx[k] = v old = ctx[k] diff --git a/test/image.test.js b/test/image.test.js index 8d54dd90f..ec1631a10 100644 --- a/test/image.test.js +++ b/test/image.test.js @@ -30,6 +30,11 @@ describe('Image', function () { assert(Image.prototype.hasOwnProperty('width')) }) + it('Image has class string of `HTMLImageElement`', async function () { + const img = new Image() + assert.strictEqual(Object.prototype.toString.call(img), '[object HTMLImageElement]') + }) + it('loads JPEG image', function () { return loadImage(jpgFace).then((img) => { assert.strictEqual(img.onerror, null) diff --git a/test/imageData.test.js b/test/imageData.test.js index d3c84c29a..04b117b45 100644 --- a/test/imageData.test.js +++ b/test/imageData.test.js @@ -17,6 +17,11 @@ describe('ImageData', function () { assert.strictEqual(imageData.toString(), '[object ImageData]') }) + it('gives class string as `ImageData`', function () { + const imageData = createImageData(2, 3) + assert.strictEqual(Object.prototype.toString.call(imageData), '[object ImageData]') + }) + it('should throw with invalid numeric arguments', function () { assert.throws(() => { createImageData(0, 0) }, /width is zero/) assert.throws(() => { createImageData(1, 0) }, /height is zero/) diff --git a/test/wpt/generated/the-canvas-element.js b/test/wpt/generated/the-canvas-element.js index 8b1a6817e..cea4fd9b4 100644 --- a/test/wpt/generated/the-canvas-element.js +++ b/test/wpt/generated/the-canvas-element.js @@ -171,6 +171,12 @@ describe("WPT: the-canvas-element", function () { assert.strictEqual(window.CanvasRenderingContext2D.prototype.fill, undefined, "window.CanvasRenderingContext2D.prototype.fill", "undefined") }); + it("2d.type class string", function () { + const canvas = createCanvas(100, 50); + const ctx = canvas.getContext("2d"); + assert.strictEqual(Object.prototype.toString.call(ctx), '[object CanvasRenderingContext2D]') + }) + it("2d.type.replace", function () { // Interface methods can be overridden const canvas = createCanvas(100, 50);