blob: 7c7bf974bb67051aa200f360ab248dd71dffbcd0 [file] [log] [blame]
"use strict";
const HTMLElementImpl = require("./HTMLElement-impl").implementation;
const notImplemented = require("../../browser/not-implemented");
const idlUtils = require("../generated/utils");
const { Canvas } = require("../../utils");
class HTMLCanvasElementImpl extends HTMLElementImpl {
_attrModified(name, value) {
if (this._canvas && (name === "width" || name === "height")) {
this._canvas[name] = parseInt(value);
}
super._attrModified.apply(this, arguments);
}
_getCanvas() {
if (Canvas && !this._canvas) {
this._canvas = Canvas.createCanvas(this.width, this.height);
}
return this._canvas;
}
getContext(contextId) {
const canvas = this._getCanvas();
if (canvas) {
if (!this._context) {
this._context = canvas.getContext(contextId) || null;
if (this._context) {
// Override the native canvas reference with our wrapper. This is the
// reason why we need to locally cache _context, since each call to
// canvas.getContext(contextId) would replace this reference again.
// Perhaps in the longer term, a better solution would be to create a
// full wrapper for the Context object as well.
this._context.canvas = idlUtils.wrapperForImpl(this);
wrapNodeCanvasMethod(this._context, "createPattern");
wrapNodeCanvasMethod(this._context, "drawImage");
}
}
return this._context;
}
notImplemented(
"HTMLCanvasElement.prototype.getContext (without installing the canvas npm package)",
this._ownerDocument._defaultView
);
return null;
}
toDataURL() {
const canvas = this._getCanvas();
if (canvas) {
return canvas.toDataURL.apply(this._canvas, arguments);
}
notImplemented(
"HTMLCanvasElement.prototype.toDataURL (without installing the canvas npm package)",
this._ownerDocument._defaultView
);
return null;
}
toBlob(callback, type, qualityArgument) {
const window = this._ownerDocument._defaultView;
const canvas = this._getCanvas();
if (canvas) {
const options = {};
switch (type) {
case "image/jpg":
case "image/jpeg":
type = "image/jpeg";
options.quality = qualityArgument;
break;
default:
type = "image/png";
}
canvas.toBuffer((err, buff) => {
if (err) {
throw err;
}
callback(new window.Blob([buff], { type }));
}, type, options);
} else {
notImplemented(
"HTMLCanvasElement.prototype.toBlob (without installing the canvas npm package)",
window
);
}
}
get width() {
const parsed = parseInt(this.getAttributeNS(null, "width"));
return isNaN(parsed) || parsed < 0 || parsed > 2147483647 ? 300 : parsed;
}
set width(v) {
v = v > 2147483647 ? 300 : v;
this.setAttributeNS(null, "width", String(v));
}
get height() {
const parsed = parseInt(this.getAttributeNS(null, "height"));
return isNaN(parsed) || parsed < 0 || parsed > 2147483647 ? 150 : parsed;
}
set height(v) {
v = v > 2147483647 ? 150 : v;
this.setAttributeNS(null, "height", String(v));
}
}
// We need to wrap the methods that receive an image or canvas object
// (luckily, always as the first argument), so that these objects can be
// unwrapped an the expected types passed.
function wrapNodeCanvasMethod(ctx, name) {
const prev = ctx[name];
ctx[name] = function (image) {
const impl = idlUtils.implForWrapper(image);
if (impl) {
if (impl instanceof HTMLCanvasElementImpl && !impl._canvas) {
impl._getCanvas();
}
arguments[0] = impl._image || impl._canvas;
}
return prev.apply(ctx, arguments);
};
}
module.exports = {
implementation: HTMLCanvasElementImpl
};