Merge "resourceloader: Remove redundant check in mw.loader.store for Opera 12"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 28 Aug 2018 02:11:08 +0000 (02:11 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 28 Aug 2018 02:11:08 +0000 (02:11 +0000)
1  2 
resources/src/startup/mediawiki.js

                                 * modules and cache each of them separately, using each module's versioning scheme
                                 * to determine when the cache should be invalidated.
                                 *
 +                               * @private
                                 * @singleton
                                 * @class mw.loader.store
                                 */
                                         * @return {string} String of concatenated vary conditions.
                                         */
                                        getVary: function () {
 -                                              return [
 -                                                      mw.config.get( 'skin' ),
 -                                                      mw.config.get( 'wgResourceLoaderStorageVersion' ),
 -                                                      mw.config.get( 'wgUserLanguage' )
 -                                              ].join( ':' );
 +                                              return mw.config.get( 'skin' ) + ':' +
 +                                                      mw.config.get( 'wgResourceLoaderStorageVersion' ) + ':' +
 +                                                      mw.config.get( 'wgUserLanguage' );
                                        },
  
                                        /**
                                                }
  
                                                try {
 +                                                      // This a string we stored, or `null` if the key does not (yet) exist.
                                                        raw = localStorage.getItem( mw.loader.store.getStoreKey() );
                                                        // If we get here, localStorage is available; mark enabled
                                                        mw.loader.store.enabled = true;
 +                                                      // If null, JSON.parse() will cast to string and re-parse, still null.
                                                        data = JSON.parse( raw );
                                                        if ( data && typeof data.items === 'object' && data.vary === mw.loader.store.getVary() ) {
                                                                mw.loader.store.items = data.items;
                                                        } );
                                                }
  
 +                                              // If we get here, one of four things happened:
 +                                              //
 +                                              // 1. localStorage did not contain our store key.
 +                                              //    This means `raw` is `null`, and we're on a fresh page view (cold cache).
 +                                              //    The store was enabled, and `items` starts fresh.
 +                                              //
 +                                              // 2. localStorage contained parseable data under our store key,
 +                                              //    but it's not applicable to our current context (see getVary).
 +                                              //    The store was enabled, and `items` starts fresh.
 +                                              //
 +                                              // 3. JSON.parse threw (localStorage contained corrupt data).
 +                                              //    This means `raw` contains a string.
 +                                              //    The store was enabled, and `items` starts fresh.
 +                                              //
 +                                              // 4. localStorage threw (disabled or otherwise unavailable).
 +                                              //    This means `raw` was never assigned.
 +                                              //    We will disable the store below.
                                                if ( raw === undefined ) {
                                                        // localStorage failed; disable store
                                                        mw.loader.store.enabled = false;
 -                                              } else {
 -                                                      mw.loader.store.update();
                                                }
                                        },
  
                                         *
                                         * @param {string} module Module name
                                         * @param {Object} descriptor The module's descriptor as set in the registry
 -                                       * @return {boolean} Module was set
                                         */
                                        set: function ( module, descriptor ) {
                                                var args, key, src;
  
                                                if ( !mw.loader.store.enabled ) {
 -                                                      return false;
 +                                                      return;
                                                }
  
                                                key = getModuleKey( module );
                                                                descriptor.templates ].indexOf( undefined ) !== -1
                                                ) {
                                                        // Decline to store
 -                                                      return false;
 +                                                      return;
                                                }
  
                                                try {
                                                                JSON.stringify( descriptor.messages ),
                                                                JSON.stringify( descriptor.templates )
                                                        ];
-                                                       // Attempted workaround for a possible Opera bug (bug T59567).
-                                                       // This regex should never match under sane conditions.
-                                                       if ( /^\s*\(/.test( args[ 1 ] ) ) {
-                                                               args[ 1 ] = 'function' + args[ 1 ];
-                                                               mw.trackError( 'resourceloader.assert', { source: 'bug-T59567' } );
-                                                       }
                                                } catch ( e ) {
                                                        mw.trackError( 'resourceloader.exception', {
                                                                exception: e,
                                                                source: 'store-localstorage-json'
                                                        } );
 -                                                      return false;
 +                                                      return;
                                                }
  
                                                src = 'mw.loader.implement(' + args.join( ',' ) + ');';
                                                if ( src.length > mw.loader.store.MODULE_SIZE_MAX ) {
 -                                                      return false;
 +                                                      return;
                                                }
                                                mw.loader.store.items[ key ] = src;
                                                mw.loader.store.update();
 -                                              return true;
                                        },
  
                                        /**
                                         * Iterate through the module store, removing any item that does not correspond
                                         * (in name and version) to an item in the module registry.
 -                                       *
 -                                       * @return {boolean} Store was pruned
                                         */
                                        prune: function () {
                                                var key, module;
  
 -                                              if ( !mw.loader.store.enabled ) {
 -                                                      return false;
 -                                              }
 -
                                                for ( key in mw.loader.store.items ) {
                                                        module = key.slice( 0, key.indexOf( '@' ) );
                                                        if ( getModuleKey( module ) !== key ) {
                                                                delete mw.loader.store.items[ key ];
                                                        }
                                                }
 -                                              return true;
                                        },
  
                                        /**
                                                mw.loader.store.items = {};
                                                try {
                                                        localStorage.removeItem( mw.loader.store.getStoreKey() );
 -                                              } catch ( ignored ) {}
 +                                              } catch ( e ) {}
                                        },
  
                                        /**
                                         * Sync in-memory store back to localStorage.
                                         *
 -                                       * This function debounces updates. When called with a flush already pending,
 -                                       * the call is coalesced into the pending update. The call to
 -                                       * localStorage.setItem will be naturally deferred until the page is quiescent.
 +                                       * This function debounces updates. When called with a flush already pending, the
 +                                       * scheduled flush is postponed. The call to localStorage.setItem will be keep
 +                                       * being deferred until the page is quiescent for 2 seconds.
                                         *
                                         * Because localStorage is shared by all pages from the same origin, if multiple
                                         * pages are loaded with different module sets, the possibility exists that
 -                                       * modules saved by one page will be clobbered by another. But the impact would
 -                                       * be minor and the problem would be corrected by subsequent page views.
 +                                       * modules saved by one page will be clobbered by another. The only impact of this
 +                                       * is minor (merely a less efficient cache use) and the problem would be corrected
 +                                       * by subsequent page views.
                                         *
                                         * @method
                                         */
                                        update: ( function () {
 -                                              var hasPendingWrite = false;
 +                                              var timer, hasPendingWrites = false;
  
                                                function flushWrites() {
                                                        var data, key;
 -                                                      if ( !hasPendingWrite || !mw.loader.store.enabled ) {
 +                                                      if ( !mw.loader.store.enabled ) {
                                                                return;
                                                        }
  
                                                                } );
                                                        }
  
 -                                                      hasPendingWrite = false;
 +                                                      hasPendingWrites = false;
                                                }
  
 -                                              return function () {
 -                                                      if ( !hasPendingWrite ) {
 -                                                              hasPendingWrite = true;
 +                                              function request() {
 +                                                      // If another mw.loader.store.set()/update() call happens in the narrow
 +                                                      // time window between requestIdleCallback() and flushWrites firing, ignore it.
 +                                                      // It'll be saved by the already-scheduled flush.
 +                                                      if ( !hasPendingWrites ) {
 +                                                              hasPendingWrites = true;
                                                                mw.requestIdleCallback( flushWrites );
                                                        }
 +                                              }
 +
 +                                              return function () {
 +                                                      // Cancel the previous timer (if it hasn't fired yet)
 +                                                      clearTimeout( timer );
 +                                                      timer = setTimeout( request, 2000 );
                                                };
                                        }() )
                                }