Blame view

lib/jsdom/living/nodes/HTMLCanvasElement-impl.js 3.67 KB
858f2bdf5   Boyan Georgiev   fixes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  "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) {
        arguments[0] = impl._image || impl._canvas;
      }
      return prev.apply(ctx, arguments);
    };
  }
  
  module.exports = {
    implementation: HTMLCanvasElementImpl
  };