Source: lib/polyfill/patchedmediakeys_ms.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.polyfill.PatchedMediaKeysMs');
  7. goog.require('goog.asserts');
  8. goog.require('shaka.log');
  9. goog.require('shaka.media.DrmEngine');
  10. goog.require('shaka.polyfill');
  11. goog.require('shaka.util.BufferUtils');
  12. goog.require('shaka.util.EventManager');
  13. goog.require('shaka.util.FakeEvent');
  14. goog.require('shaka.util.FakeEventTarget');
  15. goog.require('shaka.util.MediaReadyState');
  16. goog.require('shaka.util.Pssh');
  17. goog.require('shaka.util.PublicPromise');
  18. /**
  19. * @summary A polyfill to implement
  20. * {@link https://bit.ly/EmeMar15 EME draft 12 March 2015}
  21. * on top of ms-prefixed
  22. * {@link https://www.w3.org/TR/2014/WD-encrypted-media-20140218/ EME v20140218}
  23. * @export
  24. */
  25. shaka.polyfill.PatchedMediaKeysMs = class {
  26. /**
  27. * Installs the polyfill if needed.
  28. * @export
  29. */
  30. static install() {
  31. if (!window.HTMLVideoElement || !window.MSMediaKeys ||
  32. (navigator.requestMediaKeySystemAccess &&
  33. // eslint-disable-next-line no-restricted-syntax
  34. MediaKeySystemAccess.prototype.getConfiguration)) {
  35. return;
  36. }
  37. shaka.log.info('Using ms-prefixed EME v20140218');
  38. // Alias
  39. const PatchedMediaKeysMs = shaka.polyfill.PatchedMediaKeysMs;
  40. // Delete mediaKeys to work around strict mode compatibility issues.
  41. // eslint-disable-next-line no-restricted-syntax
  42. delete HTMLMediaElement.prototype['mediaKeys'];
  43. // Work around read-only declaration for mediaKeys by using a string.
  44. // eslint-disable-next-line no-restricted-syntax
  45. HTMLMediaElement.prototype['mediaKeys'] = null;
  46. // Install patches
  47. window.MediaKeys = PatchedMediaKeysMs.MediaKeys;
  48. window.MediaKeySystemAccess = PatchedMediaKeysMs.MediaKeySystemAccess;
  49. navigator.requestMediaKeySystemAccess =
  50. PatchedMediaKeysMs.requestMediaKeySystemAccess;
  51. // eslint-disable-next-line no-restricted-syntax
  52. HTMLMediaElement.prototype.setMediaKeys =
  53. PatchedMediaKeysMs.MediaKeySystemAccess.setMediaKeys;
  54. }
  55. /**
  56. * An implementation of navigator.requestMediaKeySystemAccess.
  57. * Retrieves a MediaKeySystemAccess object.
  58. *
  59. * @this {!Navigator}
  60. * @param {string} keySystem
  61. * @param {!Array.<!MediaKeySystemConfiguration>} supportedConfigurations
  62. * @return {!Promise.<!MediaKeySystemAccess>}
  63. */
  64. static requestMediaKeySystemAccess(keySystem, supportedConfigurations) {
  65. shaka.log.debug('PatchedMediaKeysMs.requestMediaKeySystemAccess');
  66. goog.asserts.assert(this == navigator,
  67. 'bad "this" for requestMediaKeySystemAccess');
  68. // Alias.
  69. const PatchedMediaKeysMs = shaka.polyfill.PatchedMediaKeysMs;
  70. try {
  71. const access = new PatchedMediaKeysMs.MediaKeySystemAccess(
  72. keySystem, supportedConfigurations);
  73. return Promise.resolve(/** @type {!MediaKeySystemAccess} */ (access));
  74. } catch (exception) {
  75. return Promise.reject(exception);
  76. }
  77. }
  78. /**
  79. * Handler for the native media elements msNeedKey event.
  80. *
  81. * @this {!HTMLMediaElement}
  82. * @param {!MediaKeyEvent} event
  83. * @suppress {constantProperty} We reassign what would be const on a real
  84. * MediaEncryptedEvent, but in our look-alike event.
  85. * @private
  86. */
  87. static onMsNeedKey_(event) {
  88. shaka.log.debug('PatchedMediaKeysMs.onMsNeedKey_', event);
  89. if (!event.initData) {
  90. return;
  91. }
  92. const event2 = new CustomEvent('encrypted');
  93. const encryptedEvent =
  94. /** @type {!MediaEncryptedEvent} */(/** @type {?} */(event2));
  95. encryptedEvent.initDataType = 'cenc';
  96. encryptedEvent.initData = shaka.util.BufferUtils.toArrayBuffer(
  97. shaka.util.Pssh.normaliseInitData(event.initData));
  98. this.dispatchEvent(event2);
  99. }
  100. };
  101. /**
  102. * An implementation of MediaKeySystemAccess.
  103. *
  104. * @implements {MediaKeySystemAccess}
  105. */
  106. shaka.polyfill.PatchedMediaKeysMs.MediaKeySystemAccess = class {
  107. /**
  108. * @param {string} keySystem
  109. * @param {!Array.<!MediaKeySystemConfiguration>} supportedConfigurations
  110. */
  111. constructor(keySystem, supportedConfigurations) {
  112. shaka.log.debug('PatchedMediaKeysMs.MediaKeySystemAccess');
  113. /** @type {string} */
  114. this.keySystem = keySystem;
  115. /** @private {!MediaKeySystemConfiguration} */
  116. this.configuration_;
  117. const allowPersistentState = false;
  118. let success = false;
  119. for (const cfg of supportedConfigurations) {
  120. // Create a new config object and start adding in the pieces which we
  121. // find support for. We will return this from getConfiguration() if
  122. // asked.
  123. /** @type {!MediaKeySystemConfiguration} */
  124. const newCfg = {
  125. 'audioCapabilities': [],
  126. 'videoCapabilities': [],
  127. // It is technically against spec to return these as optional, but we
  128. // don't truly know their values from the prefixed API:
  129. 'persistentState': 'optional',
  130. 'distinctiveIdentifier': 'optional',
  131. // Pretend the requested init data types are supported, since we don't
  132. // really know that either:
  133. 'initDataTypes': cfg.initDataTypes,
  134. 'sessionTypes': ['temporary'],
  135. 'label': cfg.label,
  136. };
  137. // PatchedMediaKeysMs tests for key system availability through
  138. // MSMediaKeys.isTypeSupported
  139. let ranAnyTests = false;
  140. if (cfg.audioCapabilities) {
  141. for (const cap of cfg.audioCapabilities) {
  142. if (cap.contentType) {
  143. ranAnyTests = true;
  144. const contentType = cap.contentType.split(';')[0];
  145. if (MSMediaKeys.isTypeSupported(this.keySystem, contentType)) {
  146. newCfg.audioCapabilities.push(cap);
  147. success = true;
  148. }
  149. }
  150. }
  151. }
  152. if (cfg.videoCapabilities) {
  153. for (const cap of cfg.videoCapabilities) {
  154. if (cap.contentType) {
  155. ranAnyTests = true;
  156. const contentType = cap.contentType.split(';')[0];
  157. if (MSMediaKeys.isTypeSupported(this.keySystem, contentType)) {
  158. newCfg.videoCapabilities.push(cap);
  159. success = true;
  160. }
  161. }
  162. }
  163. }
  164. if (!ranAnyTests) {
  165. // If no specific types were requested, we check all common types to
  166. // find out if the key system is present at all.
  167. success = MSMediaKeys.isTypeSupported(this.keySystem, 'video/mp4');
  168. }
  169. if (cfg.persistentState == 'required') {
  170. if (allowPersistentState) {
  171. newCfg.persistentState = 'required';
  172. newCfg.sessionTypes = ['persistent-license'];
  173. } else {
  174. success = false;
  175. }
  176. }
  177. if (success) {
  178. this.configuration_ = newCfg;
  179. return;
  180. }
  181. } // for each cfg in supportedConfigurations
  182. // According to the spec, this should be a DOMException, but there is not a
  183. // public constructor for that. So we make this look-alike instead.
  184. const unsupportedKeySystemError = new Error('Unsupported keySystem');
  185. unsupportedKeySystemError.name = 'NotSupportedError';
  186. unsupportedKeySystemError['code'] = DOMException.NOT_SUPPORTED_ERR;
  187. throw unsupportedKeySystemError;
  188. }
  189. /** @override */
  190. createMediaKeys() {
  191. shaka.log.debug(
  192. 'PatchedMediaKeysMs.MediaKeySystemAccess.createMediaKeys');
  193. // Alias
  194. const PatchedMediaKeysMs = shaka.polyfill.PatchedMediaKeysMs;
  195. const mediaKeys = new PatchedMediaKeysMs.MediaKeys(this.keySystem);
  196. return Promise.resolve(/** @type {!MediaKeys} */ (mediaKeys));
  197. }
  198. /** @override */
  199. getConfiguration() {
  200. shaka.log.debug(
  201. 'PatchedMediaKeysMs.MediaKeySystemAccess.getConfiguration');
  202. return this.configuration_;
  203. }
  204. /**
  205. * An implementation of HTMLMediaElement.prototype.setMediaKeys.
  206. * Attaches a MediaKeys object to the media element.
  207. *
  208. * @this {!HTMLMediaElement}
  209. * @param {MediaKeys} mediaKeys
  210. * @return {!Promise}
  211. */
  212. static setMediaKeys(mediaKeys) {
  213. shaka.log.debug('PatchedMediaKeysMs.setMediaKeys');
  214. goog.asserts.assert(this instanceof HTMLMediaElement,
  215. 'bad "this" for setMediaKeys');
  216. // Alias
  217. const PatchedMediaKeysMs = shaka.polyfill.PatchedMediaKeysMs;
  218. const newMediaKeys =
  219. /** @type {shaka.polyfill.PatchedMediaKeysMs.MediaKeys} */ (
  220. mediaKeys);
  221. const oldMediaKeys =
  222. /** @type {shaka.polyfill.PatchedMediaKeysMs.MediaKeys} */ (
  223. this.mediaKeys);
  224. if (oldMediaKeys && oldMediaKeys != newMediaKeys) {
  225. goog.asserts.assert(oldMediaKeys instanceof PatchedMediaKeysMs.MediaKeys,
  226. 'non-polyfill instance of oldMediaKeys');
  227. // Have the old MediaKeys stop listening to events on the video tag.
  228. oldMediaKeys.setMedia(null);
  229. }
  230. delete this['mediaKeys']; // in case there is an existing getter
  231. this['mediaKeys'] = mediaKeys; // work around read-only declaration
  232. if (newMediaKeys) {
  233. goog.asserts.assert(newMediaKeys instanceof PatchedMediaKeysMs.MediaKeys,
  234. 'non-polyfill instance of newMediaKeys');
  235. return newMediaKeys.setMedia(this);
  236. }
  237. return Promise.resolve();
  238. }
  239. };
  240. /**
  241. * An implementation of MediaKeys.
  242. *
  243. * @implements {MediaKeys}
  244. */
  245. shaka.polyfill.PatchedMediaKeysMs.MediaKeys = class {
  246. /** @param {string} keySystem */
  247. constructor(keySystem) {
  248. shaka.log.debug('PatchedMediaKeysMs.MediaKeys');
  249. /** @private {!MSMediaKeys} */
  250. this.nativeMediaKeys_ = new MSMediaKeys(keySystem);
  251. /** @private {!shaka.util.EventManager} */
  252. this.eventManager_ = new shaka.util.EventManager();
  253. }
  254. /** @override */
  255. createSession(sessionType) {
  256. shaka.log.debug('PatchedMediaKeysMs.MediaKeys.createSession');
  257. sessionType = sessionType || 'temporary';
  258. // For now, only the 'temporary' type is supported.
  259. if (sessionType != 'temporary') {
  260. throw new TypeError('Session type ' + sessionType +
  261. ' is unsupported on this platform.');
  262. }
  263. // Alias
  264. const PatchedMediaKeysMs = shaka.polyfill.PatchedMediaKeysMs;
  265. return new PatchedMediaKeysMs.MediaKeySession(
  266. this.nativeMediaKeys_, sessionType);
  267. }
  268. /** @override */
  269. setServerCertificate(serverCertificate) {
  270. shaka.log.debug('PatchedMediaKeysMs.MediaKeys.setServerCertificate');
  271. // There is no equivalent in PatchedMediaKeysMs, so return failure.
  272. return Promise.resolve(false);
  273. }
  274. /**
  275. * @param {HTMLMediaElement} media
  276. * @protected
  277. * @return {!Promise}
  278. */
  279. setMedia(media) {
  280. // Alias
  281. const PatchedMediaKeysMs = shaka.polyfill.PatchedMediaKeysMs;
  282. // Remove any old listeners.
  283. this.eventManager_.removeAll();
  284. // It is valid for media to be null; null is used to flag that event
  285. // handlers need to be cleaned up.
  286. if (!media) {
  287. return Promise.resolve();
  288. }
  289. // Intercept and translate these prefixed EME events.
  290. this.eventManager_.listen(media, 'msneedkey',
  291. /** @type {shaka.util.EventManager.ListenerType} */
  292. (PatchedMediaKeysMs.onMsNeedKey_));
  293. // Wrap native HTMLMediaElement.msSetMediaKeys with a Promise.
  294. try {
  295. // Edge requires that readyState >=1 before mediaKeys can be set,
  296. // so check this and wait for loadedmetadata if we are not in the
  297. // correct state
  298. shaka.util.MediaReadyState.waitForReadyState(media,
  299. HTMLMediaElement.HAVE_METADATA,
  300. this.eventManager_, () => {
  301. media.msSetMediaKeys(this.nativeMediaKeys_);
  302. });
  303. return Promise.resolve();
  304. } catch (exception) {
  305. return Promise.reject(exception);
  306. }
  307. }
  308. };
  309. /**
  310. * An implementation of MediaKeySession.
  311. *
  312. * @implements {MediaKeySession}
  313. */
  314. shaka.polyfill.PatchedMediaKeysMs.MediaKeySession =
  315. class extends shaka.util.FakeEventTarget {
  316. /**
  317. * @param {MSMediaKeys} nativeMediaKeys
  318. * @param {string} sessionType
  319. */
  320. constructor(nativeMediaKeys, sessionType) {
  321. shaka.log.debug('PatchedMediaKeysMs.MediaKeySession');
  322. super();
  323. /**
  324. * The native MediaKeySession, which will be created in generateRequest.
  325. * @private {MSMediaKeySession}
  326. */
  327. this.nativeMediaKeySession_ = null;
  328. /** @private {MSMediaKeys} */
  329. this.nativeMediaKeys_ = nativeMediaKeys;
  330. // Promises that are resolved later
  331. /** @private {shaka.util.PublicPromise} */
  332. this.generateRequestPromise_ = null;
  333. /** @private {shaka.util.PublicPromise} */
  334. this.updatePromise_ = null;
  335. /** @private {!shaka.util.EventManager} */
  336. this.eventManager_ = new shaka.util.EventManager();
  337. /** @type {string} */
  338. this.sessionId = '';
  339. /** @type {number} */
  340. this.expiration = NaN;
  341. /** @type {!shaka.util.PublicPromise} */
  342. this.closed = new shaka.util.PublicPromise();
  343. /** @type {!shaka.polyfill.PatchedMediaKeysMs.MediaKeyStatusMap} */
  344. this.keyStatuses =
  345. new shaka.polyfill.PatchedMediaKeysMs.MediaKeyStatusMap();
  346. }
  347. /** @override */
  348. generateRequest(initDataType, initData) {
  349. shaka.log.debug('PatchedMediaKeysMs.MediaKeySession.generateRequest');
  350. this.generateRequestPromise_ = new shaka.util.PublicPromise();
  351. try {
  352. // This EME spec version requires a MIME content type as the 1st param to
  353. // createSession, but doesn't seem to matter what the value is.
  354. // NOTE: Edge 12 only accepts Uint8Array.
  355. this.nativeMediaKeySession_ = this.nativeMediaKeys_.createSession(
  356. 'video/mp4', shaka.util.BufferUtils.toUint8(initData), null);
  357. // Attach session event handlers here.
  358. this.eventManager_.listen(this.nativeMediaKeySession_, 'mskeymessage',
  359. /** @type {shaka.util.EventManager.ListenerType} */
  360. ((event) => this.onMsKeyMessage_(event)));
  361. this.eventManager_.listen(this.nativeMediaKeySession_, 'mskeyadded',
  362. /** @type {shaka.util.EventManager.ListenerType} */
  363. ((event) => this.onMsKeyAdded_(event)));
  364. this.eventManager_.listen(this.nativeMediaKeySession_, 'mskeyerror',
  365. /** @type {shaka.util.EventManager.ListenerType} */
  366. ((event) => this.onMsKeyError_(event)));
  367. this.updateKeyStatus_('status-pending');
  368. } catch (exception) {
  369. this.generateRequestPromise_.reject(exception);
  370. }
  371. return this.generateRequestPromise_;
  372. }
  373. /** @override */
  374. load() {
  375. shaka.log.debug('PatchedMediaKeysMs.MediaKeySession.load');
  376. return Promise.reject(new Error('MediaKeySession.load not yet supported'));
  377. }
  378. /** @override */
  379. update(response) {
  380. shaka.log.debug('PatchedMediaKeysMs.MediaKeySession.update');
  381. this.updatePromise_ = new shaka.util.PublicPromise();
  382. try {
  383. // Pass through to the native session.
  384. // NOTE: Edge 12 only accepts Uint8Array.
  385. this.nativeMediaKeySession_.update(
  386. shaka.util.BufferUtils.toUint8(response));
  387. } catch (exception) {
  388. this.updatePromise_.reject(exception);
  389. }
  390. return this.updatePromise_;
  391. }
  392. /** @override */
  393. close() {
  394. shaka.log.debug('PatchedMediaKeysMs.MediaKeySession.close');
  395. try {
  396. // Pass through to the native session.
  397. // NOTE: IE seems to have a spec discrepancy here - v2010218 should have
  398. // MediaKeySession.release, but actually uses "close". The next version of
  399. // the spec is the initial Promise based one, so it's not the target spec
  400. // either.
  401. this.nativeMediaKeySession_.close();
  402. this.closed.resolve();
  403. this.eventManager_.removeAll();
  404. } catch (exception) {
  405. this.closed.reject(exception);
  406. }
  407. return this.closed;
  408. }
  409. /** @override */
  410. remove() {
  411. shaka.log.debug('PatchedMediaKeysMs.MediaKeySession.remove');
  412. return Promise.reject(new Error(
  413. 'MediaKeySession.remove is only applicable for persistent licenses, ' +
  414. 'which are not supported on this platform'));
  415. }
  416. /**
  417. * Handler for the native keymessage event on MSMediaKeySession.
  418. *
  419. * @param {!MediaKeyEvent} event
  420. * @private
  421. */
  422. onMsKeyMessage_(event) {
  423. shaka.log.debug('PatchedMediaKeysMs.onMsKeyMessage_', event);
  424. // We can now resolve this.generateRequestPromise, which should be non-null.
  425. goog.asserts.assert(this.generateRequestPromise_,
  426. 'generateRequestPromise_ not set in onMsKeyMessage_');
  427. if (this.generateRequestPromise_) {
  428. this.generateRequestPromise_.resolve();
  429. this.generateRequestPromise_ = null;
  430. }
  431. const isNew = this.keyStatuses.getStatus() == undefined;
  432. const data = new Map()
  433. .set('messageType', isNew ? 'license-request' : 'license-renewal')
  434. .set('message', shaka.util.BufferUtils.toArrayBuffer(event.message));
  435. const event2 = new shaka.util.FakeEvent('message', data);
  436. this.dispatchEvent(event2);
  437. }
  438. /**
  439. * Handler for the native keyadded event on MSMediaKeySession.
  440. *
  441. * @param {!MediaKeyEvent} event
  442. * @private
  443. */
  444. onMsKeyAdded_(event) {
  445. shaka.log.debug('PatchedMediaKeysMs.onMsKeyAdded_', event);
  446. // PlayReady's concept of persistent licenses makes emulation difficult
  447. // here. A license policy can say that the license persists, which causes
  448. // the CDM to store it for use in a later session. The result is that in
  449. // IE11, the CDM fires 'mskeyadded' without ever firing 'mskeymessage'.
  450. if (this.generateRequestPromise_) {
  451. shaka.log.debug('Simulating completion for a PR persistent license.');
  452. goog.asserts.assert(!this.updatePromise_,
  453. 'updatePromise_ and generateRequestPromise_ set in onMsKeyAdded_');
  454. this.updateKeyStatus_('usable');
  455. this.generateRequestPromise_.resolve();
  456. this.generateRequestPromise_ = null;
  457. return;
  458. }
  459. // We can now resolve this.updatePromise, which should be non-null.
  460. goog.asserts.assert(this.updatePromise_,
  461. 'updatePromise_ not set in onMsKeyAdded_');
  462. if (this.updatePromise_) {
  463. this.updateKeyStatus_('usable');
  464. this.updatePromise_.resolve();
  465. this.updatePromise_ = null;
  466. }
  467. }
  468. /**
  469. * Handler for the native keyerror event on MSMediaKeySession.
  470. *
  471. * @param {!MediaKeyEvent} event
  472. * @private
  473. */
  474. onMsKeyError_(event) {
  475. shaka.log.debug('PatchedMediaKeysMs.onMsKeyError_', event);
  476. const error = new Error('EME PatchedMediaKeysMs key error');
  477. error['errorCode'] = this.nativeMediaKeySession_.error;
  478. if (this.generateRequestPromise_ != null) {
  479. this.generateRequestPromise_.reject(error);
  480. this.generateRequestPromise_ = null;
  481. } else if (this.updatePromise_ != null) {
  482. this.updatePromise_.reject(error);
  483. this.updatePromise_ = null;
  484. } else {
  485. // Unexpected error - map native codes to standardised key statuses.
  486. // Possible values of this.nativeMediaKeySession_.error.code:
  487. // MS_MEDIA_KEYERR_UNKNOWN = 1
  488. // MS_MEDIA_KEYERR_CLIENT = 2
  489. // MS_MEDIA_KEYERR_SERVICE = 3
  490. // MS_MEDIA_KEYERR_OUTPUT = 4
  491. // MS_MEDIA_KEYERR_HARDWARECHANGE = 5
  492. // MS_MEDIA_KEYERR_DOMAIN = 6
  493. switch (this.nativeMediaKeySession_.error.code) {
  494. case MSMediaKeyError.MS_MEDIA_KEYERR_OUTPUT:
  495. case MSMediaKeyError.MS_MEDIA_KEYERR_HARDWARECHANGE:
  496. this.updateKeyStatus_('output-not-allowed');
  497. break;
  498. default:
  499. this.updateKeyStatus_('internal-error');
  500. break;
  501. }
  502. }
  503. }
  504. /**
  505. * Updates key status and dispatch a 'keystatuseschange' event.
  506. *
  507. * @param {string} status
  508. * @private
  509. */
  510. updateKeyStatus_(status) {
  511. this.keyStatuses.setStatus(status);
  512. const event = new shaka.util.FakeEvent('keystatuseschange');
  513. this.dispatchEvent(event);
  514. }
  515. };
  516. /**
  517. * @summary An implementation of MediaKeyStatusMap.
  518. * This fakes a map with a single key ID.
  519. *
  520. * @todo Consolidate the MediaKeyStatusMap types in these polyfills.
  521. * @implements {MediaKeyStatusMap}
  522. */
  523. shaka.polyfill.PatchedMediaKeysMs.MediaKeyStatusMap = class {
  524. /** */
  525. constructor() {
  526. /**
  527. * @type {number}
  528. */
  529. this.size = 0;
  530. /**
  531. * @private {string|undefined}
  532. */
  533. this.status_ = undefined;
  534. }
  535. /**
  536. * An internal method used by the session to set key status.
  537. * @param {string|undefined} status
  538. */
  539. setStatus(status) {
  540. this.size = status == undefined ? 0 : 1;
  541. this.status_ = status;
  542. }
  543. /**
  544. * An internal method used by the session to get key status.
  545. * @return {string|undefined}
  546. */
  547. getStatus() {
  548. return this.status_;
  549. }
  550. /** @override */
  551. forEach(fn) {
  552. if (this.status_) {
  553. fn(this.status_, shaka.media.DrmEngine.DUMMY_KEY_ID.value());
  554. }
  555. }
  556. /** @override */
  557. get(keyId) {
  558. if (this.has(keyId)) {
  559. return this.status_;
  560. }
  561. return undefined;
  562. }
  563. /** @override */
  564. has(keyId) {
  565. const fakeKeyId = shaka.media.DrmEngine.DUMMY_KEY_ID.value();
  566. if (this.status_ && shaka.util.BufferUtils.equal(keyId, fakeKeyId)) {
  567. return true;
  568. }
  569. return false;
  570. }
  571. /**
  572. * @suppress {missingReturn}
  573. * @override
  574. */
  575. entries() {
  576. goog.asserts.assert(false, 'Not used! Provided only for the compiler.');
  577. }
  578. /**
  579. * @suppress {missingReturn}
  580. * @override
  581. */
  582. keys() {
  583. goog.asserts.assert(false, 'Not used! Provided only for the compiler.');
  584. }
  585. /**
  586. * @suppress {missingReturn}
  587. * @override
  588. */
  589. values() {
  590. goog.asserts.assert(false, 'Not used! Provided only for the compiler.');
  591. }
  592. };
  593. shaka.polyfill.register(shaka.polyfill.PatchedMediaKeysMs.install);