tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 })();