diff --git a/src/compat/__tests__/has_eme_apis.test.ts b/src/compat/__tests__/has_eme_apis.test.ts deleted file mode 100644 index a471dbb323..0000000000 --- a/src/compat/__tests__/has_eme_apis.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ - -describe("compat - hasEMEAPIs", () => { - beforeEach(() => { - jest.resetModules(); - }); - - /* eslint-disable max-len */ - it("should return true if we could define a requestMediaKeySystemAccess function", () => { - /* eslint-enable max-len */ - - jest.mock("../eme", () => { - return { - __esModule: true as const, - requestMediaKeySystemAccess: () => null, - }; - }); - const hasEMEAPIs = jest.requireActual("../has_eme_apis"); - expect(hasEMEAPIs.default()).toBe(true); - }); - - /* eslint-disable max-len */ - it("should return false if we could not define a requestMediaKeySystemAccess function", () => { - /* eslint-enable max-len */ - - jest.mock("../eme", () => { - return { - __esModule: true as const, - requestMediaKeySystemAccess: null, - }; - }); - const hasEMEAPIs = jest.requireActual("../has_eme_apis"); - expect(hasEMEAPIs.default()).toBe(false); - }); -}); diff --git a/src/compat/eme/custom_media_keys/ie11_media_keys.ts b/src/compat/eme/custom_media_keys/ie11_media_keys.ts index 372290b557..e20655f18c 100644 --- a/src/compat/eme/custom_media_keys/ie11_media_keys.ts +++ b/src/compat/eme/custom_media_keys/ie11_media_keys.ts @@ -16,6 +16,7 @@ import EventEmitter from "../../../utils/event_emitter"; import TaskCanceller from "../../../utils/task_canceller"; +import wrapInPromise from "../../../utils/wrapInPromise"; import { ICompatHTMLMediaElement } from "../../browser_compatibility_types"; import * as events from "../../event_listeners"; import { @@ -119,11 +120,13 @@ class IE11CustomMediaKeys implements ICustomMediaKeys { this._mediaKeys = new MSMediaKeysConstructor(keyType); } - _setVideo(videoElement: HTMLMediaElement): void { - this._videoElement = videoElement as ICompatHTMLMediaElement; - if (this._videoElement.msSetMediaKeys !== undefined) { - return this._videoElement.msSetMediaKeys(this._mediaKeys); - } + _setVideo(videoElement: HTMLMediaElement): Promise { + return wrapInPromise(() => { + this._videoElement = videoElement as ICompatHTMLMediaElement; + if (this._videoElement.msSetMediaKeys !== undefined) { + this._videoElement.msSetMediaKeys(this._mediaKeys); + } + }); } createSession(/* sessionType */): ICustomMediaKeySession { @@ -144,7 +147,7 @@ export default function getIE11MediaKeysCallbacks() : { setMediaKeys: ( elt: HTMLMediaElement, mediaKeys: MediaKeys|ICustomMediaKeys|null - ) => void; + ) => Promise; } { const isTypeSupported = (keySystem: string, type?: string|null) => { if (MSMediaKeysConstructor === undefined) { @@ -160,12 +163,12 @@ export default function getIE11MediaKeysCallbacks() : { const setMediaKeys = ( elt: HTMLMediaElement, mediaKeys: MediaKeys|ICustomMediaKeys|null - ): void => { + ): Promise => { if (mediaKeys === null) { // msSetMediaKeys only accepts native MSMediaKeys as argument. // Calling it with null or undefined will raise an exception. // There is no way to unset the mediakeys in that case, so return here. - return; + return Promise.resolve(undefined); } if (!(mediaKeys instanceof IE11CustomMediaKeys)) { throw new Error("Custom setMediaKeys is supposed to be called " + diff --git a/src/compat/eme/custom_media_keys/index.ts b/src/compat/eme/custom_media_keys/index.ts index a029facbe7..1f649a5087 100644 --- a/src/compat/eme/custom_media_keys/index.ts +++ b/src/compat/eme/custom_media_keys/index.ts @@ -1,26 +1,3 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { MediaError } from "../../../errors"; -import assert from "../../../utils/assert"; -import { ICompatHTMLMediaElement } from "../../browser_compatibility_types"; -import { isIE11 } from "../../browser_detection"; -import isNode from "../../is_node"; -import shouldFavourCustomSafariEME from "../../should_favour_custom_safari_EME"; -import CustomMediaKeySystemAccess from "./../custom_key_system_access"; import getIE11MediaKeysCallbacks, { MSMediaKeysConstructor, } from "./ie11_media_keys"; @@ -37,168 +14,15 @@ import { import getWebKitMediaKeysCallbacks from "./webkit_media_keys"; import { WebKitMediaKeysConstructor } from "./webkit_media_keys_constructor"; -/** Generic implementation of the navigator.requestMediaKeySystemAccess API. */ -type ICompatRequestMediaKeySystemAccessFn = - (keyType : string, config : MediaKeySystemConfiguration[]) => - Promise; - -let requestMediaKeySystemAccess : ICompatRequestMediaKeySystemAccessFn | null = null; - -/** - * Set the given MediaKeys on the given HTMLMediaElement. - * Emits null when done then complete. - * @param {HTMLMediaElement} elt - * @param {Object} mediaKeys - */ -let setMediaKeys : -((elt: HTMLMediaElement, mediaKeys: MediaKeys | ICustomMediaKeys | null) => unknown) = - function defaultSetMediaKeys( - mediaElement: HTMLMediaElement, - mediaKeys: MediaKeys | ICustomMediaKeys | null - ) { - const elt : ICompatHTMLMediaElement = mediaElement; - /* eslint-disable @typescript-eslint/unbound-method */ - if (typeof elt.setMediaKeys === "function") { - return elt.setMediaKeys(mediaKeys as MediaKeys); - } - /* eslint-enable @typescript-eslint/unbound-method */ - - // If we get in the following code, it means that no compat case has been - // found and no standard setMediaKeys API exists. This case is particulary - // rare. We will try to call each API with native media keys. - if (typeof elt.webkitSetMediaKeys === "function") { - return elt.webkitSetMediaKeys(mediaKeys); - } - - if (typeof elt.mozSetMediaKeys === "function") { - return elt.mozSetMediaKeys(mediaKeys); - } - - if (typeof elt.msSetMediaKeys === "function" && mediaKeys !== null) { - return elt.msSetMediaKeys(mediaKeys); - } - }; - -/** - * Since Safari 12.1, EME APIs are available without webkit prefix. - * However, it seems that since fairplay CDM implementation within the browser is not - * standard with EME w3c current spec, the requestMediaKeySystemAccess API doesn't resolve - * positively, even if the drm (fairplay in most cases) is supported. - * - * Therefore, we prefer not to use requestMediaKeySystemAccess on Safari when webkit API - * is available. - */ -if (isNode || - (navigator.requestMediaKeySystemAccess != null && !shouldFavourCustomSafariEME()) -) { - requestMediaKeySystemAccess = (...args) => - navigator.requestMediaKeySystemAccess(...args); -} else { - let isTypeSupported: (keyType: string) => boolean; - let createCustomMediaKeys: (keyType: string) => ICustomMediaKeys; - - // This is for Chrome with unprefixed EME api - if (isOldWebkitMediaElement(HTMLVideoElement.prototype)) { - const callbacks = getOldKitWebKitMediaKeyCallbacks(); - isTypeSupported = callbacks.isTypeSupported; - createCustomMediaKeys = callbacks.createCustomMediaKeys; - setMediaKeys = callbacks.setMediaKeys; - // This is for WebKit with prefixed EME api - } else if (WebKitMediaKeysConstructor !== undefined) { - const callbacks = getWebKitMediaKeysCallbacks(); - isTypeSupported = callbacks.isTypeSupported; - createCustomMediaKeys = callbacks.createCustomMediaKeys; - setMediaKeys = callbacks.setMediaKeys; - } else if (isIE11 && MSMediaKeysConstructor !== undefined) { - const callbacks = getIE11MediaKeysCallbacks(); - isTypeSupported = callbacks.isTypeSupported; - createCustomMediaKeys = callbacks.createCustomMediaKeys; - setMediaKeys = callbacks.setMediaKeys; - } else if (MozMediaKeysConstructor !== undefined) { - const callbacks = getMozMediaKeysCallbacks(); - isTypeSupported = callbacks.isTypeSupported; - createCustomMediaKeys = callbacks.createCustomMediaKeys; - setMediaKeys = callbacks.setMediaKeys; - } else { - const MK = window.MediaKeys as unknown as typeof MediaKeys & { - isTypeSupported? : (keyType : string) => boolean; - new(keyType? : string) : ICustomMediaKeys; - }; - const checkForStandardMediaKeys = () => { - if (MK === undefined) { - throw new MediaError("MEDIA_KEYS_NOT_SUPPORTED", - "No `MediaKeys` implementation found " + - "in the current browser."); - } - if (typeof MK.isTypeSupported === "undefined") { - const message = "This browser seems to be unable to play encrypted contents " + - "currently. Note: Some browsers do not allow decryption " + - "in some situations, like when not using HTTPS."; - throw new Error(message); - } - }; - isTypeSupported = (keyType: string): boolean => { - checkForStandardMediaKeys(); - assert(typeof MK.isTypeSupported === "function"); - return MK.isTypeSupported(keyType); - }; - createCustomMediaKeys = (keyType: string) => { - checkForStandardMediaKeys(); - return new MK(keyType); - }; - } - - requestMediaKeySystemAccess = function( - keyType : string, - keySystemConfigurations : MediaKeySystemConfiguration[] - ) : Promise { - if (!isTypeSupported(keyType)) { - return Promise.reject(new Error("Unsupported key type")); - } - - for (let i = 0; i < keySystemConfigurations.length; i++) { - const keySystemConfiguration = keySystemConfigurations[i]; - const { videoCapabilities, - audioCapabilities, - initDataTypes, - distinctiveIdentifier } = keySystemConfiguration; - let supported = true; - supported = supported && - (initDataTypes == null || - initDataTypes.some((idt) => idt === "cenc")); - supported = supported && (distinctiveIdentifier !== "required"); - - if (supported) { - const keySystemConfigurationResponse : MediaKeySystemConfiguration = { - initDataTypes: ["cenc"], - distinctiveIdentifier: "not-allowed" as const, - persistentState: "required" as const, - sessionTypes: ["temporary", "persistent-license"], - }; - if (videoCapabilities !== undefined) { - keySystemConfigurationResponse.videoCapabilities = videoCapabilities; - } - if (audioCapabilities !== undefined) { - keySystemConfigurationResponse.audioCapabilities = audioCapabilities; - } - - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const customMediaKeys = createCustomMediaKeys(keyType); - return Promise.resolve( - new CustomMediaKeySystemAccess(keyType, - customMediaKeys, - keySystemConfigurationResponse) - ); - } - } - - return Promise.reject(new Error("Unsupported configuration")); - }; -} - export { - requestMediaKeySystemAccess, - setMediaKeys, + getIE11MediaKeysCallbacks, + MSMediaKeysConstructor, + getMozMediaKeysCallbacks, + MozMediaKeysConstructor, + getOldKitWebKitMediaKeyCallbacks, + isOldWebkitMediaElement, ICustomMediaKeys, ICustomMediaKeySession, + getWebKitMediaKeysCallbacks, + WebKitMediaKeysConstructor, }; diff --git a/src/compat/eme/custom_media_keys/moz_media_keys_constructor.ts b/src/compat/eme/custom_media_keys/moz_media_keys_constructor.ts index 8e1a971bb1..c552adbf34 100644 --- a/src/compat/eme/custom_media_keys/moz_media_keys_constructor.ts +++ b/src/compat/eme/custom_media_keys/moz_media_keys_constructor.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import wrapInPromise from "../../../utils/wrapInPromise"; import { ICompatHTMLMediaElement } from "../../browser_compatibility_types"; import isNode from "../../is_node"; import { ICustomMediaKeys } from "./types"; @@ -47,7 +48,7 @@ export default function getMozMediaKeysCallbacks() : { setMediaKeys: ( elt: HTMLMediaElement, mediaKeys: MediaKeys|ICustomMediaKeys|null - ) => void; + ) => Promise; } { const isTypeSupported = (keySystem: string, type?: string|null) => { if (MozMediaKeysConstructor === undefined) { @@ -67,12 +68,17 @@ export default function getMozMediaKeysCallbacks() : { const setMediaKeys = ( mediaElement: HTMLMediaElement, mediaKeys: MediaKeys|ICustomMediaKeys|null - ): void => { - const elt : ICompatHTMLMediaElement = mediaElement; - if (elt.mozSetMediaKeys === undefined || typeof elt.mozSetMediaKeys !== "function") { - throw new Error("Can't set video on MozMediaKeys."); - } - return elt.mozSetMediaKeys(mediaKeys); + ): Promise => { + return wrapInPromise(() => { + const elt : ICompatHTMLMediaElement = mediaElement; + if ( + elt.mozSetMediaKeys === undefined || + typeof elt.mozSetMediaKeys !== "function" + ) { + throw new Error("Can't set video on MozMediaKeys."); + } + return elt.mozSetMediaKeys(mediaKeys); + }); }; return { isTypeSupported, diff --git a/src/compat/eme/custom_media_keys/old_webkit_media_keys.ts b/src/compat/eme/custom_media_keys/old_webkit_media_keys.ts index f9323a757d..6c7df1dee2 100644 --- a/src/compat/eme/custom_media_keys/old_webkit_media_keys.ts +++ b/src/compat/eme/custom_media_keys/old_webkit_media_keys.ts @@ -18,6 +18,7 @@ import { base64ToBytes } from "../../../utils/base64"; import EventEmitter from "../../../utils/event_emitter"; import noop from "../../../utils/noop"; import { utf8ToStr } from "../../../utils/string_parsing"; +import wrapInPromise from "../../../utils/wrapInPromise"; import { ICustomMediaKeys, ICustomMediaKeySession, @@ -166,11 +167,13 @@ class OldWebKitCustomMediaKeys implements ICustomMediaKeys { this._keySystem = keySystem; } - _setVideo(videoElement: IOldWebkitHTMLMediaElement|HTMLMediaElement): void { - if (!isOldWebkitMediaElement(videoElement)) { - throw new Error("Video not attached to the MediaKeys"); - } - this._videoElement = videoElement; + _setVideo(videoElement: IOldWebkitHTMLMediaElement|HTMLMediaElement): Promise { + return wrapInPromise(() => { + if (!isOldWebkitMediaElement(videoElement)) { + throw new Error("Video not attached to the MediaKeys"); + } + this._videoElement = videoElement; + }); } createSession(/* sessionType */): ICustomMediaKeySession { @@ -191,7 +194,7 @@ export default function getOldWebKitMediaKeysCallbacks() : { setMediaKeys: ( elt: HTMLMediaElement, mediaKeys: MediaKeys|ICustomMediaKeys|null - ) => void; + ) => Promise; } { const isTypeSupported = function (keyType: string): boolean { // get any