Merge "resourceloader: Remove redundant check in mw.loader.store for Opera 12"
[lhc/web/wiklou.git] / resources / src / startup / mediawiki.js
index a80f453..95dde2d 100644 (file)
                                 * 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 {
                                                                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 );
                                                };
                                        }() )
                                }