UNPKG

happy-dom

Version:

Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. It includes many web standards from WHATWG DOM and HTML.

163 lines (148 loc) 5.13 kB
import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum.js'; import Blob from '../file/Blob.js'; import WindowBrowserContext from '../window/WindowBrowserContext.js'; import type ICanvasAdapter from './ICanvasAdapter.js'; import type ICanvasRenderingContext2D from './ICanvasRenderingContext2D.js'; import type ImageBitmap from './ImageBitmap.js'; import * as PropertySymbol from '../PropertySymbol.js'; import type BrowserWindow from '../window/BrowserWindow.js'; import type ICanvasShape from './ICanvasShape.js'; /** * The OffscreenCanvas interface provides a canvas that can be rendered off screen, decoupling the DOM and the Canvas API so that the <canvas> element is no longer entirely dependent on the DOM. * * @see https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas/OffscreenCanvas */ export default class OffscreenCanvas implements ICanvasShape { // Injected by WindowContextClassExtender protected declare [PropertySymbol.window]: BrowserWindow; protected [PropertySymbol.context]: ICanvasRenderingContext2D | null = null; public readonly width: number; public readonly height: number; /** * Constructor. * * @param width Width. * @param height Height. */ constructor(width: number, height: number) { this.width = width; this.height = height; } /** * Returns context. * * @param contextType Context type. * @param [contextAttributes] Context attributes. * @returns Context. */ public getContext( contextType: '2d' | 'webgl' | 'webgl2' | 'webgpu' | 'bitmaprenderer', contextAttributes?: { [key: string]: any } ): ICanvasRenderingContext2D | null { const browserFrame = new WindowBrowserContext(this[PropertySymbol.window]).getBrowserFrame(); if (!browserFrame) { throw new this[PropertySymbol.window].Error( `Failed to execute 'getContext' on 'OffscreenCanvas': Browser frame is not available. This happens when the browser is closing.` ); } const settings = new WindowBrowserContext(this[PropertySymbol.window]).getSettings(); const adapter = settings?.canvasAdapter; if (!adapter) { return null; } this[PropertySymbol.context] = (<ICanvasAdapter>adapter).getContext( { window: this[PropertySymbol.window], browserFrame, canvas: this }, contextType, contextAttributes ); return this[PropertySymbol.context]; } /** * Converts the canvas to a Blob. * * @param options Options. * @param options.type Type. * @param options.quality Quality. * @returns Blob. */ public convertToBlob(options?: { type?: string; quality?: any }): Promise<Blob> { const browserFrame = new WindowBrowserContext(this[PropertySymbol.window]).getBrowserFrame(); if (!browserFrame) { throw new this[PropertySymbol.window].Error( `Failed to execute 'getContext' on 'OffscreenCanvas': Browser frame is not available. This happens when the browser is closing.` ); } const settings = new WindowBrowserContext(this[PropertySymbol.window]).getSettings(); const adapter = settings?.canvasAdapter; if (!adapter) { return new Promise((resolve) => { this[PropertySymbol.window].requestAnimationFrame(() => resolve(new Blob([], options))); }); } return new Promise((resolve, reject) => { (<ICanvasAdapter>adapter).toBlob( { window: this[PropertySymbol.window], browserFrame, canvas: this }, (blob) => { if (blob) { resolve(blob); } else { reject( new this[PropertySymbol.window].DOMException( `Failed to execute 'convertToBlob' on 'OffscreenCanvas': The canvas could not be converted to a Blob.`, DOMExceptionNameEnum.encodingError ) ); } }, options?.type, options?.quality ); }); } /** * Creates an ImageBitmap object from the most recently rendered image of the OffscreenCanvas. * * The image in the OffscreenCanvas is replaced with a new blank image for subsequent rendering. * * @returns ImageBitmap. */ public transferToImageBitmap(): ImageBitmap { const browserFrame = new WindowBrowserContext(this[PropertySymbol.window]).getBrowserFrame(); if (!browserFrame) { throw new this[PropertySymbol.window].Error( `Failed to execute 'getContext' on 'OffscreenCanvas': Browser frame is not available. This happens when the browser is closing.` ); } const window = this[PropertySymbol.window]; const settings = new WindowBrowserContext(window).getSettings(); const adapter = settings?.canvasAdapter; if (!adapter) { return new window.ImageBitmap( PropertySymbol.illegalConstructor, this[PropertySymbol.window], this ); } const context = this[PropertySymbol.context]; if (!context) { throw new this[PropertySymbol.window].TypeError( `Failed to execute 'transferToImageBitmap' on 'OffscreenCanvas': Cannot transfer an ImageBitmap from an OffscreenCanvas with no context` ); } const imageBitmap = new window.ImageBitmap( PropertySymbol.illegalConstructor, this[PropertySymbol.window], this ); context.clearRect(0, 0, this.width, this.height); return imageBitmap; } }