From bed32d0a2e7f7b41a96fd52ba2c274d4b2a24f2e Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Mon, 11 Dec 2023 12:27:10 -0800 Subject: [PATCH] Use addEventListener for MediaKeySession events #6034 --- api-extractor/report/hls.js.api.md | 4 ++ src/controller/eme-controller.ts | 35 ++++++++-- tests/unit/controller/eme-controller.ts | 89 ++++++++++--------------- 3 files changed, 67 insertions(+), 61 deletions(-) diff --git a/api-extractor/report/hls.js.api.md b/api-extractor/report/hls.js.api.md index 963a275c762..a2bee4a6591 100644 --- a/api-extractor/report/hls.js.api.md +++ b/api-extractor/report/hls.js.api.md @@ -2804,6 +2804,10 @@ export interface MediaKeySessionContext { mediaKeys: MediaKeys; // (undocumented) mediaKeysSession: MediaKeySession; + // (undocumented) + _onkeystatuseschange?: (this: MediaKeySession, ev: Event) => any; + // (undocumented) + _onmessage?: (this: MediaKeySession, ev: MediaKeyMessageEvent) => any; } // Warning: (ae-missing-release-tag) "MediaPlaylist" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/controller/eme-controller.ts b/src/controller/eme-controller.ts index 8a2ed35080f..ae2c40f249e 100644 --- a/src/controller/eme-controller.ts +++ b/src/controller/eme-controller.ts @@ -57,6 +57,8 @@ export interface MediaKeySessionContext { mediaKeysSession: MediaKeySession; keyStatus: MediaKeyStatus; licenseXhr?: XMLHttpRequest; + _onmessage?: (this: MediaKeySession, ev: MediaKeyMessageEvent) => any; + _onkeystatuseschange?: (this: MediaKeySession, ev: Event) => any; } /** @@ -717,7 +719,7 @@ class EMEController implements ComponentAPI { const licenseStatus = new EventEmitter(); - context.mediaKeysSession.onmessage = (event: MediaKeyMessageEvent) => { + const onmessage = (context._onmessage = (event: MediaKeyMessageEvent) => { const keySession = context.mediaKeysSession; if (!keySession) { licenseStatus.emit('error', new Error('invalid state')); @@ -743,10 +745,10 @@ class EMEController implements ComponentAPI { } else { this.warn(`unhandled media key message type "${messageType}"`); } - }; + }); - context.mediaKeysSession.onkeystatuseschange = ( - event: MediaKeyMessageEvent, + const onkeystatuseschange = (context._onkeystatuseschange = ( + event: Event, ) => { const keySession = context.mediaKeysSession; if (!keySession) { @@ -760,7 +762,13 @@ class EMEController implements ComponentAPI { this.warn(`${context.keySystem} expired for key ${keyId}`); this.renewKeySession(context); } - }; + }); + + context.mediaKeysSession.addEventListener('message', onmessage); + context.mediaKeysSession.addEventListener( + 'keystatuseschange', + onkeystatuseschange, + ); const keyUsablePromise = new Promise( (resolve: (value?: void) => void, reject) => { @@ -1269,8 +1277,21 @@ class EMEController implements ComponentAPI { this.log( `Remove licenses and keys and close session ${mediaKeysSession.sessionId}`, ); - mediaKeysSession.onmessage = null; - mediaKeysSession.onkeystatuseschange = null; + if (mediaKeySessionContext._onmessage) { + mediaKeysSession.removeEventListener( + 'message', + mediaKeySessionContext._onmessage, + ); + mediaKeySessionContext._onmessage = undefined; + } + if (mediaKeySessionContext._onkeystatuseschange) { + mediaKeysSession.removeEventListener( + 'keystatuseschange', + mediaKeySessionContext._onkeystatuseschange, + ); + mediaKeySessionContext._onkeystatuseschange = undefined; + } + if (licenseXhr && licenseXhr.readyState !== XMLHttpRequest.DONE) { licenseXhr.abort(); } diff --git a/tests/unit/controller/eme-controller.ts b/tests/unit/controller/eme-controller.ts index eb78ebf9a67..b67b734cf8f 100644 --- a/tests/unit/controller/eme-controller.ts +++ b/tests/unit/controller/eme-controller.ts @@ -44,6 +44,34 @@ class MediaMock extends EventEmitter { } } +class MediaKeySessionMock extends EventEmitter { + addEventListener: any; + removeEventListener: any; + keyStatuses: Map; + constructor() { + super(); + this.keyStatuses = new Map(); + this.addEventListener = this.addListener.bind(this); + this.removeEventListener = this.removeListener.bind(this); + } + generateRequest() { + return Promise.resolve().then(() => { + this.emit('message', { + messageType: 'license-request', + message: new Uint8Array(0), + }); + this.keyStatuses.set(new Uint8Array(0), 'usable'); + this.emit('keystatuseschange', {}); + }); + } + remove() { + return Promise.resolve(); + } + update() { + return Promise.resolve(); + } +} + let emeController: EMEControllerTestable; let media: MediaMock; let sinonFakeXMLHttpRequestStatic: sinon.SinonFakeXMLHttpRequestStatic; @@ -82,24 +110,7 @@ describe('EMEController', function () { createMediaKeys: sinon.spy(() => Promise.resolve({ setServerCertificate: () => Promise.resolve(), - createSession: (): Partial => ({ - addEventListener: () => {}, - onmessage: null, - onkeystatuseschange: null, - generateRequest() { - return Promise.resolve().then(() => { - this.onmessage({ - messageType: 'license-request', - message: new Uint8Array(0), - }); - this.keyStatuses.set(new Uint8Array(0), 'usable'); - this.onkeystatuseschange({}); - }); - }, - remove: () => Promise.resolve(), - update: () => Promise.resolve(), - keyStatuses: new Map(), - }), + createSession: () => new MediaKeySessionMock(), }), ), }); @@ -163,24 +174,7 @@ describe('EMEController', function () { createMediaKeys: sinon.spy(() => Promise.resolve({ setServerCertificate: () => Promise.resolve(), - createSession: (): Partial => ({ - addEventListener: () => {}, - onmessage: null, - onkeystatuseschange: null, - generateRequest() { - return Promise.resolve().then(() => { - this.onmessage({ - messageType: 'license-request', - message: new Uint8Array(0), - }); - this.keyStatuses.set(new Uint8Array(0), 'usable'); - this.onkeystatuseschange({}); - }); - }, - remove: () => Promise.resolve(), - update: () => Promise.resolve(), - keyStatuses: new Map(), - }), + createSession: () => new MediaKeySessionMock(), }), ), }); @@ -260,6 +254,7 @@ describe('EMEController', function () { setServerCertificate: () => Promise.resolve(), createSession: () => ({ addEventListener: () => {}, + removeEventListener: () => {}, generateRequest: () => Promise.reject(new Error('bad data')), remove: () => Promise.resolve(), update: () => Promise.resolve(), @@ -324,24 +319,7 @@ describe('EMEController', function () { createMediaKeys: sinon.spy(() => Promise.resolve({ setServerCertificate: mediaKeysSetServerCertificateSpy, - createSession: () => ({ - addEventListener: () => {}, - onmessage: null, - onkeystatuseschange: null, - generateRequest() { - return Promise.resolve().then(() => { - this.onmessage({ - messageType: 'license-request', - message: new Uint8Array(0), - }); - this.keyStatuses.set(new Uint8Array(0), 'usable'); - this.onkeystatuseschange({}); - }); - }, - remove: () => Promise.resolve(), - update: () => Promise.resolve(), - keyStatuses: new Map(), - }), + createSession: () => new MediaKeySessionMock(), }), ), }); @@ -421,6 +399,7 @@ describe('EMEController', function () { setServerCertificate: mediaKeysSetServerCertificateSpy, createSession: () => ({ addEventListener: () => {}, + removeEventListener: () => {}, generateRequest: () => Promise.resolve(), remove: () => Promise.resolve(), update: () => Promise.resolve(), @@ -505,6 +484,7 @@ describe('EMEController', function () { Promise.resolve({ createSession: () => ({ addEventListener: () => {}, + removeEventListener: () => {}, generateRequest: () => Promise.resolve(), remove: () => Promise.resolve(), update: () => Promise.resolve(), @@ -583,6 +563,7 @@ describe('EMEController', function () { setServerCertificate: () => Promise.resolve(), createSession: () => ({ addEventListener: () => {}, + removeEventListener: () => {}, generateRequest: () => Promise.resolve(), remove: () => Promise.resolve(), update: () => Promise.resolve(),