Skip to content

Commit

Permalink
chore: [#1332] Continues on implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
capricorn86 committed Apr 7, 2024
1 parent 30b70b6 commit 0af09bd
Show file tree
Hide file tree
Showing 13 changed files with 551 additions and 2 deletions.
4 changes: 4 additions & 0 deletions packages/happy-dom/src/PropertySymbol.ts
Expand Up @@ -164,3 +164,7 @@ export const appendChild = Symbol('appendChild');
export const removeChild = Symbol('removeChild');
export const insertBefore = Symbol('insertBefore');
export const replaceChild = Symbol('replaceChild');
export const tracks = Symbol('tracks');
export const constraints = Symbol('constraints');
export const capabilities = Symbol('capabilities');
export const settings = Symbol('settings');
@@ -0,0 +1,6 @@
import MediaStreamTrack from '../../nodes/html-canvas-element/MediaStreamTrack.js';
import IEventInit from '../IEventInit.js';

export default interface IMediaQueryListEventInit extends IEventInit {
track?: MediaStreamTrack;
}
22 changes: 22 additions & 0 deletions packages/happy-dom/src/event/events/MediaStreamTrackEvent.ts
@@ -0,0 +1,22 @@
import MediaStreamTrack from '../../nodes/html-canvas-element/MediaStreamTrack.js';
import Event from '../Event.js';
import IMediaQueryListEventInit from './IMediaQueryListEventInit.js';

/**
* Media Stream Track Event.
*/
export default class MediaStreamTrackEvent extends Event {
public readonly track: MediaStreamTrack | null;

/**
* Constructor.
*
* @param type Event type.
* @param [eventInit] Event init.
*/
constructor(type: string, eventInit: IMediaQueryListEventInit | null = null) {
super(type, eventInit);

this.track = eventInit?.track ?? null;
}
}
Expand Up @@ -9,6 +9,7 @@ import * as PropertySymbol from '../../PropertySymbol.js';
*/
export default class HTMLBaseElement extends HTMLElement {
public cloneNode: (deep?: boolean) => HTMLBaseElement;

/**
* Returns href.
*
Expand Down
@@ -1,7 +1,28 @@
import Event from '../../event/Event.js';
import HTMLElement from '../html-element/HTMLElement.js';
/**
* HTMLBodyElement
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLBodyElement
*/
export default class HTMLBodyElement extends HTMLElement {}
export default class HTMLBodyElement extends HTMLElement {
// Events
public onafterprint: (event: Event) => void | null = null;
public onbeforeprint: (event: Event) => void | null = null;
public onbeforeunload: (event: Event) => void | null = null;
public ongamepadconnected: (event: Event) => void | null = null;
public ongamepaddisconnected: (event: Event) => void | null = null;
public onhashchange: (event: Event) => void | null = null;
public onlanguagechange: (event: Event) => void | null = null;
public onmessage: (event: Event) => void | null = null;
public onmessageerror: (event: Event) => void | null = null;
public onoffline: (event: Event) => void | null = null;
public ononline: (event: Event) => void | null = null;
public onpagehide: (event: Event) => void | null = null;
public onpageshow: (event: Event) => void | null = null;
public onpopstate: (event: Event) => void | null = null;
public onrejectionhandled: (event: Event) => void | null = null;
public onstorage: (event: Event) => void | null = null;
public onunhandledrejection: (event: Event) => void | null = null;
public onunload: (event: Event) => void | null = null;
}
@@ -0,0 +1,39 @@
import HTMLCanvasElement from './HTMLCanvasElement.js';
import MediaStreamTrack from './MediaStreamTrack.js';

/**
* Canvas Capture Media Stream Track.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasCaptureMediaStreamTrack
*/
export default class CanvasCaptureMediaStreamTrack extends MediaStreamTrack {
public canvas: HTMLCanvasElement;

/**
*
* @param canvas
* @param frameRate
*/
constructor(canvas: HTMLCanvasElement) {
super();
this.canvas = canvas;
}

/**
* Requests a frame.
*/
public requestFrame(): void {
// Do nothing
}

/**
* Clones the track.
*
* @returns Clone.
*/
public clone(): MediaStreamTrack {
const clone = <CanvasCaptureMediaStreamTrack>super.clone();
clone.canvas = this.canvas;
return clone;
}
}
114 changes: 113 additions & 1 deletion packages/happy-dom/src/nodes/html-canvas-element/HTMLCanvasElement.ts
@@ -1,7 +1,119 @@
import HTMLElement from '../html-element/HTMLElement.js';
import CanvasCaptureMediaStreamTrack from './CanvasCaptureMediaStreamTrack.js';
import MediaStream from './MediaStream.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import Blob from '../../file/Blob.js';
import OffscreenCanvas from './OffscreenCanvas.js';
import Event from '../../event/Event.js';

/**
* HTMLCanvasElement
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement
*/
export default class HTMLCanvasElement extends HTMLElement {}
export default class HTMLCanvasElement extends HTMLElement {
// Events
public oncontextlost: (event: Event) => void | null = null;
public oncontextrestored: (event: Event) => void | null = null;
public onwebglcontextcreationerror: (event: Event) => void | null = null;
public onwebglcontextlost: (event: Event) => void | null = null;
public onwebglcontextrestored: (event: Event) => void | null = null;

/**
* Returns width.
*
* @returns Width.
*/
public get width(): number {
const width = this.getAttribute('width');
return width !== null ? Number(width) : 300;
}

/**
* Sets width.
*
* @param width Width.
*/
public set width(width: number) {
this.setAttribute('width', String(width));
}

/**
* Returns height.
*
* @returns Height.
*/
public get height(): number {
const height = this.getAttribute('height');
return height !== null ? Number(height) : 150;
}

/**
* Sets height.
*
* @param height Height.
*/
public set height(height: number) {
this.setAttribute('height', String(height));
}

/**
* Returns capture stream.
*
* @param [_frameRate] Frame rate.
* @returns Capture stream.
*/
public captureStream(_frameRate?: number): MediaStream {
const stream = new MediaStream();
stream.addTrack(new CanvasCaptureMediaStreamTrack(this));
stream[PropertySymbol.capabilities].aspectRatio.max = this.width;
stream[PropertySymbol.capabilities].height.max = this.height;
stream[PropertySymbol.capabilities].width.max = this.width;
return stream;
}

/**
* Returns context.
*
* @param _contextType Context type.
* @param [_contextAttributes] Context attributes.
* @returns Context.
*/
public getContext(
_contextType: '2d' | 'webgl' | 'webgl2' | 'webgpu' | 'bitmaprenderer',
_contextAttributes?: { [key: string]: any }
): null {
return null;
}

/**
* Returns to data URL.
*
* @param [_type] Type.
* @param [_encoderOptions] Quality.
* @returns Data URL.
*/
public toDataURL(_type?: string, _encoderOptions?: any): string {
return '';
}

/**
* Returns to blob.
*
* @param callback Callback.
* @param [_type] Type.
* @param [_quality] Quality.
*/
public toBlob(callback: (blob: Blob) => void, _type?: string, _quality?: any): void {
callback(new Blob([]));
}

/**
* Transfers control to offscreen.
*
* @returns Offscreen canvas.
*/
public transferControlToOffscreen(): OffscreenCanvas {
return new OffscreenCanvas(this.width, this.height);
}
}
@@ -0,0 +1,21 @@
export default interface IMediaTrackCapabilities {
aspectRatio: {
max: number;
min: number;
};
deviceId: string;
facingMode: [];
frameRate: {
max: number;
min: number;
};
height: {
max: number;
min: number;
};
resizeMode: string[];
width: {
max: number;
min: number;
};
}
@@ -0,0 +1,5 @@
export default interface IMediaTrackSettings {
deviceId: string;
frameRate: number;
resizeMode: string;
}
25 changes: 25 additions & 0 deletions packages/happy-dom/src/nodes/html-canvas-element/ImageBitmap.ts
@@ -0,0 +1,25 @@
/**
*
*/
export default class ImageBitmap {
public height: number;
public width: number;

/**
* Constructor.
*
* @param width Width.
* @param height Height.
*/
constructor(width: number, height: number) {
this.width = width;
this.height = height;
}

/**
* Disposes of all graphical resources associated with an ImageBitmap.
*/
public close(): void {
// TODO: Not implemented.
}
}
108 changes: 108 additions & 0 deletions packages/happy-dom/src/nodes/html-canvas-element/MediaStream.ts
@@ -0,0 +1,108 @@
import * as PropertySymbol from '../../PropertySymbol.js';
import Crypto from 'crypto';
import EventTarget from '../../event/EventTarget.js';
import MediaStreamTrackEvent from '../../event/events/MediaStreamTrackEvent.js';
import MediaStreamTrack from './MediaStreamTrack.js';

/**
* MediaStream.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/MediaStream
*/
export default class MediaStream extends EventTarget {
// Public properties
public active = true;
public id: string = Crypto.randomUUID();

// Events
public onaddtrack: (event: MediaStreamTrackEvent) => void | null = null;
public onremovetrack: (event: MediaStreamTrackEvent) => void | null = null;

// Internal properties
public [PropertySymbol.tracks]: MediaStreamTrack[] = [];

/**
* Constructor.
*
* @param [streamOrTracks] Stream or tracks.
*/
constructor(streamOrTracks?: MediaStream | MediaStreamTrack[]) {
super();

if (streamOrTracks !== undefined) {
this[PropertySymbol.tracks] =
streamOrTracks instanceof MediaStream
? streamOrTracks[PropertySymbol.tracks].slice()
: streamOrTracks;
}
}

/**
* Adds a track.
*
* @param track Track.
*/
public addTrack(track: MediaStreamTrack): void {
if (this[PropertySymbol.tracks].includes(track)) {
return;
}
this[PropertySymbol.tracks].push(track);
this.dispatchEvent(new MediaStreamTrackEvent('addtrack', { track }));
}

/**
* Returns a clone.
*
* @returns Clone.
*/
public clone(): MediaStream {
return new (<typeof MediaStream>this.constructor)(this);
}

/**
* Returns audio tracks.
*
* @returns Audio tracks.
*/
public getAudioTracks(): MediaStreamTrack[] {
return this[PropertySymbol.tracks].filter((track) => track.kind === 'audio');
}

/**
* Returns track by id.
*
* @param id Id.
* @returns Track.
*/
public getTrackById(id: string): MediaStreamTrack | null {
for (const track of this[PropertySymbol.tracks]) {
if (track.id === id) {
return track;
}
}
return null;
}

/**
* Returns video tracks.
*
* @returns Video tracks.
*/
public getVideoTracks(): MediaStreamTrack[] {
return this[PropertySymbol.tracks].filter((track) => track.kind === 'video');
}

/**
* Removes a track.
*
* @param track Track.
*/
public removeTrack(track: MediaStreamTrack): void {
const index = this[PropertySymbol.tracks].indexOf(track);
if (index === -1) {
return;
}
this[PropertySymbol.tracks].splice(index, 1);
this.dispatchEvent(new MediaStreamTrackEvent('removetrack', { track }));
}
}

0 comments on commit 0af09bd

Please sign in to comment.