diff --git a/package.json b/package.json index 16dbedd..fbfffbc 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "module": "dist/esm/index.js", "types": "dist/types/index.d.ts", "exports": { + "types": "./dist/types/index.d.ts", "import": "./dist/esm/index.js", "require": "./dist/cjs/index.js" }, diff --git a/src/web-capabilities.spec.ts b/src/web-capabilities.spec.ts index 9e5b8b2..a605481 100644 --- a/src/web-capabilities.spec.ts +++ b/src/web-capabilities.spec.ts @@ -41,6 +41,28 @@ describe('WebCapabilities', () => { expect(WebCapabilities.isCapableOfReceiving1080pVideo()).toBe(CapabilityState.CAPABLE); expect(WebCapabilities.isCapableOfSending1080pVideo()).toBe(CapabilityState.CAPABLE); }); + it('should return NOT_CAPABLE for background noise removal when WebAssembly is hard-disabled', () => { + expect.assertions(1); + const originalWebAssembly = globalThis.WebAssembly; + jest.spyOn(CpuInfo, 'getNumLogicalCores').mockReturnValue(8); + delete (globalThis as { WebAssembly?: typeof WebAssembly }).WebAssembly; + + expect(WebCapabilities.isCapableOfBackgroundNoiseRemoval()).toBe(CapabilityState.NOT_CAPABLE); + + (globalThis as { WebAssembly?: typeof WebAssembly }).WebAssembly = originalWebAssembly; + }); + + it('should return NOT_CAPABLE for virtual background when WebAssembly is hard-disabled', () => { + expect.assertions(1); + const originalWebAssembly = globalThis.WebAssembly; + jest.spyOn(CpuInfo, 'getNumLogicalCores').mockReturnValue(8); + delete (globalThis as { WebAssembly?: typeof WebAssembly }).WebAssembly; + + expect(WebCapabilities.isCapableOfVirtualBackground()).toBe(CapabilityState.NOT_CAPABLE); + + (globalThis as { WebAssembly?: typeof WebAssembly }).WebAssembly = originalWebAssembly; + }); + describe('supportsEncodedStreamTransforms', () => { afterEach(() => { // Clean up window modifications @@ -249,6 +271,27 @@ describe('WebCapabilities', () => { }); }); + describe('supportsWasm', () => { + const originalWebAssembly = globalThis.WebAssembly; + + afterEach(() => { + // Restore the WebAssembly global mutated by individual cases. + (globalThis as { WebAssembly?: typeof WebAssembly }).WebAssembly = originalWebAssembly; + }); + + it('should return CAPABLE when the WebAssembly runtime is available', () => { + expect.assertions(1); + expect(WebCapabilities.supportsWasm()).toBe(CapabilityState.CAPABLE); + }); + + it('should return NOT_CAPABLE when the WebAssembly runtime is hard-disabled', () => { + expect.assertions(1); + delete (globalThis as { WebAssembly?: typeof WebAssembly }).WebAssembly; + + expect(WebCapabilities.supportsWasm()).toBe(CapabilityState.NOT_CAPABLE); + }); + }); + describe('supportsEncodingCodec', () => { let isChromeSpy: jest.SpyInstance; let isEdgeSpy: jest.SpyInstance; diff --git a/src/web-capabilities.ts b/src/web-capabilities.ts index 9d4fa54..a486abf 100644 --- a/src/web-capabilities.ts +++ b/src/web-capabilities.ts @@ -23,6 +23,11 @@ export class WebCapabilities { * @returns A {@link CapabilityState}. */ static isCapableOfBackgroundNoiseRemoval(): CapabilityState { + // Background noise removal runs as a WebAssembly module, so it cannot work at all when the + // runtime is hard-disabled (such as Chromium jitless mode). + if (WebCapabilities.supportsWasm() === CapabilityState.NOT_CAPABLE) { + return CapabilityState.NOT_CAPABLE; + } const numCores = CpuInfo.getNumLogicalCores(); if (numCores === undefined) { return CapabilityState.UNKNOWN; @@ -39,6 +44,11 @@ export class WebCapabilities { * @returns A {@link CapabilityState}. */ static isCapableOfVirtualBackground(): CapabilityState { + // Virtual background runs as a WebAssembly module, so it cannot work at all when the runtime + // is hard-disabled (such as Chromium jitless mode). + if (WebCapabilities.supportsWasm() === CapabilityState.NOT_CAPABLE) { + return CapabilityState.NOT_CAPABLE; + } const numCores = CpuInfo.getNumLogicalCores(); if (numCores === undefined) { return CapabilityState.UNKNOWN; @@ -112,6 +122,18 @@ export class WebCapabilities { : CapabilityState.NOT_CAPABLE; } + /** + * Checks whether the browser supports the WebAssembly runtime, which some environments + * hard-disable (such as Chromium jitless mode). + * + * @returns A {@link CapabilityState}. + */ + static supportsWasm(): CapabilityState { + return typeof WebAssembly === 'object' && typeof WebAssembly.validate === 'function' + ? CapabilityState.CAPABLE + : CapabilityState.NOT_CAPABLE; + } + /** * Checks whether the browser supports RTCPeerConnection. This is needed, * because some users install browser extensions that remove RTCPeerConnection.