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
text/typescript
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;
}
}