edge-keystatuses.js (6539B)
1 (function() { 2 3 // This polyfill fixes the following problems with Edge browser 4 // (1) Various maplike methods for keystatuses are not supported or suported incorrectly 5 // (2) Key Ids exposed in keystatuses are incorrect (byte swaps) 6 if ( navigator.userAgent.toLowerCase().indexOf('edge') > -1 ) { 7 /////////////////////////////////////////////////////////////////////////////////////////////// 8 // The following function is the core of this JS patch. The rest of this file is infrastructure 9 // required to enable this function 10 /////////////////////////////////////////////////////////////////////////////////////////////// 11 function _proxyKeyStatusesChange( event ) { 12 this._keyStatuses.clear(); 13 var keyStatuses = []; 14 this._session.keyStatuses.forEach( function( keyId, status ) { 15 var newKeyId = new Uint8Array( keyId ); 16 17 function swap( arr, a, b ) { var t = arr[a]; arr[a] = arr[b]; arr[b] = t; } 18 swap( newKeyId, 0, 3 ); 19 swap( newKeyId, 1, 2 ); 20 swap( newKeyId, 4, 5 ); 21 swap( newKeyId, 6, 7 ); 22 23 keyStatuses.push( { key: newKeyId, status: status, ord: arrayBufferAsString( newKeyId ) } ); 24 }); 25 26 function lexicographical( a, b ) { return a < b ? -1 : a === b ? 0 : +1; } 27 function lexicographicalkey( a, b ) { return lexicographical( a.ord, b.ord ); } 28 29 keyStatuses.sort( lexicographicalkey ).forEach( function( obj ) { 30 this._keyStatuses._set( obj.key, obj.status ); 31 }.bind( this ) ); 32 33 this.dispatchEvent( event ); 34 }; 35 /////////////////////////////////////////////////////////////////////////////////////////////// 36 37 // Override MediaKeys.createSession 38 var _mediaKeysCreateSession = MediaKeys.prototype.createSession; 39 MediaKeys.prototype.createSession = function ( sessionType ) { 40 return new MediaKeySession( _mediaKeysCreateSession.call( this, sessionType ) ); 41 }; 42 43 // MediaKeySession proxy 44 function MediaKeySession( session ) { 45 EventTarget.call( this ); 46 this._session = session; 47 this._keyStatuses = new MediaKeyStatusMap(); 48 this._session.addEventListener("keystatuseschange",this._onKeyStatusesChange.bind(this)); 49 this._session.addEventListener("message",this.dispatchEvent.bind(this)); 50 } 51 52 MediaKeySession.prototype = Object.create( EventTarget.prototype ); 53 54 Object.defineProperties( MediaKeySession.prototype, { 55 sessionId: { get: function() { return this._session.sessionId; } }, 56 expiration: { get: function() { return this._session.expiration; } }, 57 closed: { get: function() { return this._session.closed; } }, 58 keyStatuses:{ get: function() { return this._keyStatuses; } } 59 }); 60 61 [ "generateRequest", "load", "update", "remove", "close" ].forEach( function( fnname ) { 62 MediaKeySession.prototype[ fnname ] = function() { 63 return window.MediaKeySession.prototype[ fnname ].apply( this._session, arguments ); 64 } 65 } ); 66 67 MediaKeySession.prototype._onKeyStatusesChange = _proxyKeyStatusesChange; 68 69 // MediaKeyStatusMap proxy 70 // 71 // We need a proxy class to replace the broken MediaKeyStatusMap one. We cannot use a 72 // regular Map directly because we need get and has methods to compare by value not 73 // as references. 74 function MediaKeyStatusMap() { this._map = new Map(); } 75 76 Object.defineProperties( MediaKeyStatusMap.prototype, { 77 size: { get: function() { return this._map.size; } }, 78 forEach: { get: function() { return function( f ) { return this._map.forEach( f ); } } }, 79 entries: { get: function() { return function() { return this._map.entries(); } } }, 80 values: { get: function() { return function() { return this._map.values(); } } }, 81 keys: { get: function() { return function() { return this._map.keys(); } } }, 82 clear: { get: function() { return function() { return this._map.clear(); } } } } ); 83 84 MediaKeyStatusMap.prototype[ Symbol.iterator ] = function() { return this._map[ Symbol.iterator ]() }; 85 86 MediaKeyStatusMap.prototype.has = function has( keyId ) { 87 for ( var k of this._map.keys() ) { if ( arrayBufferEqual( k, keyId ) ) return true; } 88 return false; 89 }; 90 91 MediaKeyStatusMap.prototype.get = function get( keyId ) { 92 for ( var k of this._map.entries() ) { if ( arrayBufferEqual( k[ 0 ], keyId ) ) return k[ 1 ]; } 93 }; 94 95 MediaKeyStatusMap.prototype._set = function _set( keyId, status ) { 96 this._map.set( new Uint8Array( keyId ), status ); 97 }; 98 99 function arrayBufferEqual(buf1, buf2) 100 { 101 if (buf1.byteLength !== buf2.byteLength) return false; 102 var a1 = Array.from( new Int8Array(buf1) ), a2 = Array.from( new Int8Array(buf2) ); 103 return a1.every( function( x, i ) { return x === a2[i]; } ); 104 } 105 106 // EventTarget 107 function EventTarget(){ 108 this.listeners = {}; 109 }; 110 111 EventTarget.prototype.listeners = null; 112 113 EventTarget.prototype.addEventListener = function(type, callback){ 114 if(!(type in this.listeners)) { 115 this.listeners[type] = []; 116 } 117 this.listeners[type].push(callback); 118 }; 119 120 EventTarget.prototype.removeEventListener = function(type, callback){ 121 if(!(type in this.listeners)) { 122 return; 123 } 124 var stack = this.listeners[type]; 125 for(var i = 0, l = stack.length; i < l; i++){ 126 if(stack[i] === callback){ 127 stack.splice(i, 1); 128 return this.removeEventListener(type, callback); 129 } 130 } 131 }; 132 133 EventTarget.prototype.dispatchEvent = function(event){ 134 if(!(event.type in this.listeners)) { 135 return; 136 } 137 var stack = this.listeners[event.type]; 138 event.target = this; 139 for(var i = 0, l = stack.length; i < l; i++) { 140 stack[i].call(this, event); 141 } 142 }; 143 } 144 })();