Merge "Change default of $wgResourceLoaderMaxQueryLength to 2000"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 27 Mar 2015 22:45:56 +0000 (22:45 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 27 Mar 2015 22:45:56 +0000 (22:45 +0000)
1  2 
includes/Setup.php
resources/src/mediawiki/mediawiki.js

diff --combined includes/Setup.php
@@@ -362,10 -362,15 +362,15 @@@ if ( $wgMetaNamespace === false ) 
        $wgMetaNamespace = str_replace( ' ', '_', $wgSitename );
  }
  
- // Default value is either the suhosin limit or -1 for unlimited
+ // Default value is 2000 or the suhosin limit if it is between 1 and 2000
  if ( $wgResourceLoaderMaxQueryLength === false ) {
-       $maxValueLength = ini_get( 'suhosin.get.max_value_length' );
-       $wgResourceLoaderMaxQueryLength = $maxValueLength > 0 ? $maxValueLength : -1;
+       $suhosinMaxValueLength = (int) ini_get( 'suhosin.get.max_value_length' );
+       if ( $suhosinMaxValueLength > 0 && $suhosinMaxValueLength < 2000 ) {
+               $wgResourceLoaderMaxQueryLength = $suhosinMaxValueLength;
+       } else {
+               $wgResourceLoaderMaxQueryLength = 2000;
+       }
+       unset($suhosinMaxValueLength);
  }
  
  /**
@@@ -573,6 -578,7 +578,6 @@@ $ps_memcached = Profiler::instance()->s
  $wgMemc = wfGetMainCache();
  $messageMemc = wfGetMessageCacheStorage();
  $parserMemc = wfGetParserCacheStorage();
 -$wgLangConvMemc = wfGetLangConverterCacheStorage();
  
  wfDebugLog( 'caches', 'main: ' . get_class( $wgMemc ) .
        ', message: ' . get_class( $messageMemc ) .
                trackCallbacks = $.Callbacks( 'memory' ),
                trackQueue = [];
  
 -      /**
 -       * Log a message to window.console, if possible.
 -       *
 -       * Useful to force logging of some  errors that are otherwise hard to detect (i.e., this logs
 -       * also in production mode). Gets console references in each invocation instead of caching the
 -       * reference, so that debugging tools loaded later are supported (e.g. Firebug Lite in IE).
 -       *
 -       * @private
 -       * @method log_
 -       * @param {string} msg Text for the log entry.
 -       * @param {Error} [e]
 -       */
 -      function log( msg, e ) {
 -              var console = window.console;
 -              if ( console && console.log ) {
 -                      console.log( msg );
 -                      // If we have an exception object, log it to the error channel to trigger a
 -                      // proper stacktraces in browsers that support it. No fallback as we have no browsers
 -                      // that don't support error(), but do support log().
 -                      if ( e && console.error ) {
 -                              console.error( String( e ), e );
 -                      }
 -              }
 -      }
 -
        /**
         * Create an object that can be read from or written to from methods that allow
         * interaction both with single and multiple properties at once.
                         *   error is not module-related or the module cannot be easily identified due to
                         *   batched handling.
                         * @param {string} source Source of the error. Possible values:
 +                       *
                         *   - style: stylesheet error (only affects old IE where a special style loading method
                         *     is used)
                         *   - load-callback: exception thrown by user callback
                         *   - module-execute: exception thrown by module code
 +                       *   - store-eval: could not evaluate module code cached in localStorage
 +                       *   - store-localstorage-init: localStorage or JSON parse error in mw.loader.store.init
 +                       *   - store-localstorage-json: JSON conversion error in mw.loader.store.set
 +                       *   - store-localstorage-update: localStorage or JSON conversion error in mw.loader.store.update
 +                       */
 +
 +                      /**
 +                       * Fired via mw.track on resource loading error conditions.
 +                       *
 +                       * @event resourceloader_assert
 +                       * @param {string} source Source of the error. Possible values:
 +                       *
 +                       *   - bug-T59567: failed to cache script due to an Opera function -> string conversion
 +                       *     bug; see <https://phabricator.wikimedia.org/T59567> for details
                         */
  
                        /**
                                return s;
                        }
  
 -                      /**
 -                       * Check whether given styles are safe to to a stylesheet.
 -                       *
 -                       * @private
 -                       * @param {string} cssText
 -                       * @return {boolean} False if a new one must be created.
 -                       */
 -                      function canExpandStylesheetWith( cssText ) {
 -                              // Makes sure that cssText containing `@import`
 -                              // rules will end up in a new stylesheet (as those only work when
 -                              // placed at the start of a stylesheet; bug 35562).
 -                              return cssText.slice( 0, '@import'.length ) !== '@import';
 -                      }
 -
                        /**
                         * Add a bit of CSS text to the current browser page.
                         *
                                // Appending a stylesheet and waiting for the browser to repaint
                                // is fairly expensive, this reduces that (bug 45810)
                                if ( cssText ) {
 -                                      // Be careful not to extend the buffer with css that needs a new stylesheet
 -                                      if ( !cssBuffer || canExpandStylesheetWith( cssText ) ) {
 +                                      // Be careful not to extend the buffer with css that needs a new stylesheet.
 +                                      // cssText containing `@import` rules needs to go at the start of a buffer,
 +                                      // since those only work when placed at the start of a stylesheet; bug 35562.
 +                                      if ( !cssBuffer || cssText.slice( 0, '@import'.length ) !== '@import' ) {
                                                // Linebreak for somewhat distinguishable sections
                                                // (the rl-cachekey comment separating each)
                                                cssBuffer += '\n' + cssText;
                                        // (not some other style tag or even a `<meta>` or `<script>`).
                                        if ( $style.data( 'ResourceLoaderDynamicStyleTag' ) === true ) {
                                                // There's already a dynamic <style> tag present and
 -                                              // canExpandStylesheetWith() gave a green light to append more to it.
 +                                              // we are able to append more to it.
                                                styleEl = $style.get( 0 );
                                                // Support: IE6-10
                                                if ( styleEl.styleSheet ) {
                                                        try {
                                                                styleEl.styleSheet.cssText += cssText;
                                                        } catch ( e ) {
 -                                                              log( 'Stylesheet error', e );
                                                                mw.track( 'resourceloader.exception', { exception: e, source: 'stylesheet' } );
                                                        }
                                                } else {
                         * @return {string}
                         */
                        function pad( a, b, c ) {
 -                              return [
 -                                      a < 10 ? '0' + a : a,
 -                                      b < 10 ? '0' + b : b,
 -                                      c < 10 ? '0' + c : c
 -                              ].join( '' );
 +                              return (
 +                                      ( a < 10 ? '0' : '' ) + a +
 +                                      ( b < 10 ? '0' : '' ) + b +
 +                                      ( c < 10 ? '0' : '' ) + c
 +                              );
                        }
  
                        /**
                         * order.
                         *
                         * @private
 -                       * @param {string} module Module name or array of string module names
 +                       * @param {string[]} module Array of string module names
                         * @return {Array} List of dependencies, including 'module'.
 -                       * @throws {Error} If circular reference is detected
                         */
 -                      function resolve( module ) {
 -                              var m, resolved;
 -
 -                              // Allow calling with an array of module names
 -                              if ( $.isArray( module ) ) {
 -                                      resolved = [];
 -                                      for ( m = 0; m < module.length; m += 1 ) {
 -                                              sortDependencies( module[m], resolved );
 -                                      }
 -                                      return resolved;
 -                              }
 -
 -                              if ( typeof module === 'string' ) {
 -                                      resolved = [];
 +                      function resolve( modules ) {
 +                              var resolved = [];
 +                              $.each( modules, function ( idx, module ) {
                                        sortDependencies( module, resolved );
 -                                      return resolved;
 -                              }
 -
 -                              throw new Error( 'Invalid module argument: ' + module );
 +                              } );
 +                              return resolved;
                        }
  
                        /**
 -                       * Narrow down a list of module names to those matching a specific
 -                       * state (see #registry for a list of valid states).
 -                       *
 -                       * One can also filter for 'unregistered', which will return the
 -                       * modules names that don't have a registry entry.
 +                       * Determine whether all dependencies are in state 'ready', which means we may
 +                       * execute the module or job now.
                         *
                         * @private
 -                       * @param {string|string[]} states Module states to filter by
 -                       * @param {Array} [modules] List of module names to filter (optional, by default the
 -                       * entire registry is used)
 -                       * @return {Array} List of filtered module names
 +                       * @param {Array} module Names of modules to be checked
 +                       * @return {boolean} True if all modules are in state 'ready', false otherwise
                         */
 -                      function filter( states, modules ) {
 -                              var list, module, s, m;
 -
 -                              // Allow states to be given as a string
 -                              if ( typeof states === 'string' ) {
 -                                      states = [states];
 -                              }
 -                              // If called without a list of modules, build and use a list of all modules
 -                              list = [];
 -                              if ( modules === undefined ) {
 -                                      modules = [];
 -                                      for ( module in registry ) {
 -                                              modules[modules.length] = module;
 -                                      }
 -                              }
 -                              // Build a list of modules which are in one of the specified states
 -                              for ( s = 0; s < states.length; s += 1 ) {
 -                                      for ( m = 0; m < modules.length; m += 1 ) {
 -                                              if ( !hasOwn.call( registry, modules[m] ) ) {
 -                                                      // Module does not exist
 -                                                      if ( states[s] === 'unregistered' ) {
 -                                                              // OK, undefined
 -                                                              list[list.length] = modules[m];
 -                                                      }
 -                                              } else {
 -                                                      // Module exists, check state
 -                                                      if ( registry[modules[m]].state === states[s] ) {
 -                                                              // OK, correct state
 -                                                              list[list.length] = modules[m];
 -                                                      }
 -                                              }
 +                      function allReady( modules ) {
 +                              var i;
 +                              for ( i = 0; i < modules.length; i++ ) {
 +                                      if ( mw.loader.getState( modules[i] ) !== 'ready' ) {
 +                                              return false;
                                        }
                                }
 -                              return list;
 +                              return true;
                        }
  
                        /**
                         * execute the module or job now.
                         *
                         * @private
 -                       * @param {Array} dependencies Dependencies (module names) to be checked.
 -                       * @return {boolean} True if all dependencies are in state 'ready', false otherwise
 +                       * @param {Array} modules Names of modules to be checked
 +                       * @return {boolean} True if no modules are in state 'error' or 'missing', false otherwise
                         */
 -                      function allReady( dependencies ) {
 -                              return filter( 'ready', dependencies ).length === dependencies.length;
 +                      function anyFailed( modules ) {
 +                              var i, state;
 +                              for ( i = 0; i < modules.length; i++ ) {
 +                                      state = mw.loader.getState( modules[i] );
 +                                      if ( state === 'error' || state === 'missing' ) {
 +                                              return true;
 +                                      }
 +                              }
 +                              return false;
                        }
  
                        /**
                        function handlePending( module ) {
                                var j, job, hasErrors, m, stateChange;
  
 -                              if ( $.inArray( registry[module].state, ['error', 'missing'] ) !== -1 ) {
 +                              if ( registry[module].state === 'error' || registry[module].state === 'missing' ) {
                                        // If the current module failed, mark all dependent modules also as failed.
                                        // Iterate until steady-state to propagate the error state upwards in the
                                        // dependency tree.
                                        do {
                                                stateChange = false;
                                                for ( m in registry ) {
 -                                                      if ( $.inArray( registry[m].state, ['error', 'missing'] ) === -1 ) {
 -                                                              if ( filter( ['error', 'missing'], registry[m].dependencies ).length ) {
 +                                                      if ( registry[m].state !== 'error' && registry[m].state !== 'missing' ) {
 +                                                              if ( anyFailed( registry[m].dependencies ) ) {
                                                                        registry[m].state = 'error';
                                                                        stateChange = true;
                                                                }
  
                                // Execute all jobs whose dependencies are either all satisfied or contain at least one failed module.
                                for ( j = 0; j < jobs.length; j += 1 ) {
 -                                      hasErrors = filter( ['error', 'missing'], jobs[j].dependencies ).length > 0;
 +                                      hasErrors = anyFailed( jobs[j].dependencies );
                                        if ( hasErrors || allReady( jobs[j].dependencies ) ) {
                                                // All dependencies satisfied, or some have errors
                                                job = jobs[j];
                                                } catch ( e ) {
                                                        // A user-defined callback raised an exception.
                                                        // Swallow it to protect our state machine!
 -                                                      log( 'Exception thrown by user callback', e );
 -                                                      mw.track( 'resourceloader.exception',
 -                                                              { exception: e, module: module, source: 'load-callback' } );
 +                                                      mw.track( 'resourceloader.exception', { exception: e, module: module, source: 'load-callback' } );
                                                }
                                        }
                                }
                                        } catch ( e ) {
                                                // This needs to NOT use mw.log because these errors are common in production mode
                                                // and not in debug mode, such as when a symbol that should be global isn't exported
 -                                              log( 'Exception thrown by ' + module, e );
                                                registry[module].state = 'error';
                                                mw.track( 'resourceloader.exception', { exception: e, module: module, source: 'module-execute' } );
                                                handlePending( module );
                         *  Ignored (and defaulted to `true`) if the document-ready event has already occurred.
                         */
                        function request( dependencies, ready, error, async ) {
 -                              var n;
 -
                                // Allow calling by single module name
                                if ( typeof dependencies === 'string' ) {
                                        dependencies = [dependencies];
                                // Add ready and error callbacks if they were given
                                if ( ready !== undefined || error !== undefined ) {
                                        jobs[jobs.length] = {
 -                                              'dependencies': filter(
 -                                                      ['registered', 'loading', 'loaded'],
 -                                                      dependencies
 -                                              ),
 -                                              'ready': ready,
 -                                              'error': error
 +                                              dependencies: $.grep( dependencies, function ( module ) {
 +                                                      var state = mw.loader.getState( module );
 +                                                      return state === 'registered' || state === 'loaded' || state === 'loading';
 +                                              ),
 +                                              ready: ready,
 +                                              error: error
                                        };
                                }
  
 -                              // Queue up any dependencies that are registered
 -                              dependencies = filter( ['registered'], dependencies );
 -                              for ( n = 0; n < dependencies.length; n += 1 ) {
 -                                      if ( $.inArray( dependencies[n], queue ) === -1 ) {
 -                                              queue[queue.length] = dependencies[n];
 +                              $.each( dependencies, function ( idx, module ) {
 +                                      var state = mw.loader.getState( module );
 +                                      if ( state === 'registered' && $.inArray( module, queue ) === -1 ) {
 +                                              queue.push( module );
                                                if ( async ) {
 -                                                      // Mark this module as async in the registry
 -                                                      registry[dependencies[n]].async = true;
 +                                                      registry[module].async = true;
                                                }
                                        }
 -                              }
 +                              } );
  
 -                              // Work the queue
                                mw.loader.work();
                        }
  
                         * @param {Array} modules Modules array
                         */
                        function resolveIndexedDependencies( modules ) {
 -                              var i, iLen, j, jLen, module, dependency;
 -
 -                              // Expand indexed dependency names
 -                              for ( i = 0, iLen = modules.length; i < iLen; i++ ) {
 -                                      module = modules[i];
 +                              $.each( modules, function ( idx, module ) {
                                        if ( module[2] ) {
 -                                              for ( j = 0, jLen = module[2].length; j < jLen; j++ ) {
 -                                                      dependency = module[2][j];
 -                                                      if ( typeof dependency === 'number' ) {
 -                                                              module[2][j] = modules[dependency][0];
 -                                                      }
 -                                              }
 +                                              module[2] = $.map( module[2], function ( dep ) {
 +                                                      return typeof dep === 'number' ? modules[dep][0] : dep;
 +                                              } );
                                        }
 -                              }
 +                              } );
                        }
  
                        /* Public Members */
                                        };
                                        // Split module batch by source and by group.
                                        splits = {};
-                                       maxQueryLength = mw.config.get( 'wgResourceLoaderMaxQueryLength', -1 );
+                                       maxQueryLength = mw.config.get( 'wgResourceLoaderMaxQueryLength', 2000 );
  
                                        // Appends a list of modules from the queue to the batch
                                        for ( q = 0; q < queue.length; q += 1 ) {
                                                        // repopulate these modules to the cache.
                                                        // This means that at most one module will be useless (the one that had
                                                        // the error) instead of all of them.
 -                                                      log( 'Error while evaluating data from mw.loader.store', err );
 +                                                      mw.track( 'resourceloader.exception', { exception: err, source: 'store-eval' } );
                                                        origBatch = $.grep( origBatch, function ( module ) {
                                                                return registry[module].state === 'loading';
                                                        } );
                                                                        moduleMap = {};
                                                                        async = true;
                                                                        l = currReqBaseLength + 9;
+                                                                       mw.track( 'resourceloader.splitRequest', { maxQueryLength: maxQueryLength } );
                                                                }
                                                                if ( !hasOwn.call( moduleMap, prefix ) ) {
                                                                        moduleMap[prefix] = [];
                                 *     { <media>: [url, ..] }
                                 *
                                 * The reason css strings are not concatenated anymore is bug 31676. We now check
 -                               * whether it's safe to extend the stylesheet (see #canExpandStylesheetWith).
 +                               * whether it's safe to extend the stylesheet.
                                 *
                                 * @param {Object} [msgs] List of key/value pairs to be added to mw#messages.
                                 * @param {Object} [templates] List of key/value pairs to be added to mw#templates.
                                        if ( allReady( dependencies ) ) {
                                                // Run ready immediately
                                                deferred.resolve();
 -                                      } else if ( filter( ['error', 'missing'], dependencies ).length ) {
 +                                      } else if ( anyFailed( dependencies ) ) {
                                                // Execute error immediately if any dependencies have errors
                                                deferred.reject(
                                                        new Error( 'One or more dependencies failed to load' ),
                                 *  Defaults to `true` if loading a URL, `false` otherwise.
                                 */
                                load: function ( modules, type, async ) {
 -                                      var filtered, m, module, l;
 +                                      var filtered, l;
  
                                        // Validate input
                                        if ( typeof modules !== 'object' && typeof modules !== 'string' ) {
                                        // Undefined modules are acceptable here in load(), because load() takes
                                        // an array of unrelated modules, whereas the modules passed to
                                        // using() are related and must all be loaded.
 -                                      for ( filtered = [], m = 0; m < modules.length; m += 1 ) {
 -                                              if ( hasOwn.call( registry, modules[m] ) ) {
 -                                                      module = registry[modules[m]];
 -                                                      if ( $.inArray( module.state, ['error', 'missing'] ) === -1 ) {
 -                                                              filtered[filtered.length] = modules[m];
 -                                                      }
 -                                              }
 -                                      }
 +                                      filtered = $.grep( modules, function ( module ) {
 +                                              var state = mw.loader.getState( module );
 +                                              return state !== null && state !== 'error' && state !== 'missing';
 +                                      } );
  
                                        if ( filtered.length === 0 ) {
                                                return;
                                        }
                                        // Resolve entire dependency map
                                        filtered = resolve( filtered );
 -                                      // If all modules are ready, nothing to be done
 -                                      if ( allReady( filtered ) ) {
 -                                              return;
 -                                      }
 -                                      // If any modules have errors: also quit.
 -                                      if ( filter( ['error', 'missing'], filtered ).length ) {
 +                                      // If all modules are ready, or if any modules have errors, nothing to be done.
 +                                      if ( allReady( filtered ) || anyFailed( filtered ) ) {
                                                return;
                                        }
                                        // Since some modules are not yet ready, queue up a request.
                                                                return;
                                                        }
                                                } catch ( e ) {
 -                                                      log( 'Storage error', e );
 +                                                      mw.track( 'resourceloader.exception', { exception: e, source: 'store-localstorage-init' } );
                                                }
  
                                                if ( raw === undefined ) {
                                                                JSON.stringify( descriptor.messages ),
                                                                JSON.stringify( descriptor.templates )
                                                        ];
 -                                                      // Attempted workaround for a possible Opera bug (bug 57567).
 +                                                      // 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];
 -                                                              log( 'Detected malformed function stringification (bug 57567)' );
 +                                                              mw.track( 'resourceloader.assert', { source: 'bug-T59567' } );
                                                        }
                                                } catch ( e ) {
 -                                                      log( 'Storage error', e );
 +                                                      mw.track( 'resourceloader.exception', { exception: e, source: 'store-localstorage-json' } );
                                                        return;
                                                }
  
                                                                data = JSON.stringify( mw.loader.store );
                                                                localStorage.setItem( key, data );
                                                        } catch ( e ) {
 -                                                              log( 'Storage error', e );
 +                                                              mw.track( 'resourceloader.exception', { exception: e, source: 'store-localstorage-update' } );
                                                        }
                                                }
  
        // @deprecated since 1.23 Use $ or jQuery instead
        mw.log.deprecate( window, '$j', $, 'Use $ or jQuery instead.' );
  
 +      /**
 +       * Log a message to window.console, if possible.
 +       *
 +       * Useful to force logging of some  errors that are otherwise hard to detect (i.e., this logs
 +       * also in production mode). Gets console references in each invocation instead of caching the
 +       * reference, so that debugging tools loaded later are supported (e.g. Firebug Lite in IE).
 +       *
 +       * @private
 +       * @method log_
 +       * @param {string} topic Stream name passed by mw.track
 +       * @param {Object} data Data passed by mw.track
 +       * @param {Error} [data.exception]
 +       * @param {string} data.source Error source
 +       * @param {string} [data.module] Name of module which caused the error
 +       */
 +      function log( topic, data ) {
 +              var msg,
 +                      e = data.exception,
 +                      source = data.source,
 +                      module = data.module,
 +                      console = window.console;
 +
 +              if ( console && console.log ) {
 +                      msg = ( e ? 'Exception' : 'Error' ) + ' in ' + source;
 +                      if ( module ) {
 +                              msg += ' in module ' + module;
 +                      }
 +                      msg += ( e ? ':' : '.' );
 +                      console.log( msg );
 +
 +                      // If we have an exception object, log it to the error channel to trigger a
 +                      // proper stacktraces in browsers that support it. No fallback as we have no browsers
 +                      // that don't support error(), but do support log().
 +                      if ( e && console.error ) {
 +                              console.error( String( e ), e );
 +                      }
 +              }
 +      }
 +
 +      // subscribe to error streams
 +      mw.trackSubscribe( 'resourceloader.exception', log );
 +      mw.trackSubscribe( 'resourceloader.assert', log );
 +
        // Attach to window and globally alias
        window.mw = window.mediaWiki = mw;
  }( jQuery ) );