resourceloader: Condition-wrap the HTML tag instead of JS response
[lhc/web/wiklou.git] / includes / resourceloader / ResourceLoader.php
index 53b6d81..f6a9d84 100644 (file)
@@ -32,9 +32,6 @@ class ResourceLoader {
        /** @var int */
        protected static $filterCacheVersion = 7;
 
-       /** @var array */
-       protected static $requiredSourceProperties = array( 'loadScript' );
-
        /** @var bool */
        protected static $debugMode = null;
 
@@ -44,13 +41,16 @@ class ResourceLoader {
        /** @var array Associative array mapping module name to info associative array */
        protected $moduleInfos = array();
 
+       /** @var Config $config */
+       private $config;
+
        /**
         * @var array Associative array mapping framework ids to a list of names of test suite modules
         *      like array( 'qunit' => array( 'mediawiki.tests.qunit.suites', 'ext.foo.tests', .. ), .. )
         */
        protected $testModuleNames = array();
 
-       /** @var array E.g. array( 'source-id' => array( 'loadScript' => 'http://.../load.php' ) ) */
+       /** @var array E.g. array( 'source-id' => 'http://.../load.php' ) */
        protected $sources = array();
 
        /** @var bool */
@@ -152,7 +152,6 @@ class ResourceLoader {
         * @return string Filtered data, or a comment containing an error message
         */
        public function filter( $filter, $data, $cacheReport = true ) {
-               global $wgResourceLoaderMinifierStatementsOnOwnLine, $wgResourceLoaderMinifierMaxLineLength;
                wfProfileIn( __METHOD__ );
 
                // For empty/whitespace-only data or for unknown filters, don't perform
@@ -180,8 +179,8 @@ class ResourceLoader {
                        switch ( $filter ) {
                                case 'minify-js':
                                        $result = JavaScriptMinifier::minify( $data,
-                                               $wgResourceLoaderMinifierStatementsOnOwnLine,
-                                               $wgResourceLoaderMinifierMaxLineLength
+                                               $this->config->get( 'ResourceLoaderMinifierStatementsOnOwnLine' ),
+                                               $this->config->get( 'ResourceLoaderMinifierMaxLineLength' )
                                        );
                                        if ( $cacheReport ) {
                                                $result .= "\n/* cache key: $key */";
@@ -214,34 +213,46 @@ class ResourceLoader {
 
        /**
         * Register core modules and runs registration hooks.
+        * @param Config|null $config
         */
-       public function __construct() {
-               global $IP, $wgResourceModules, $wgResourceLoaderSources, $wgLoadScript, $wgEnableJavaScriptTest;
+       public function __construct( Config $config = null ) {
+               global $IP;
 
                wfProfileIn( __METHOD__ );
 
+               if ( $config === null ) {
+                       wfDebug( __METHOD__ . ' was called without providing a Config instance' );
+                       $config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
+               }
+
+               $this->config = $config;
+
                // Add 'local' source first
-               $this->addSource(
-                       'local',
-                       array( 'loadScript' => $wgLoadScript, 'apiScript' => wfScript( 'api' ) )
-               );
+               $this->addSource( 'local', wfScript( 'load' ) );
 
                // Add other sources
-               $this->addSource( $wgResourceLoaderSources );
+               $this->addSource( $config->get( 'ResourceLoaderSources' ) );
 
                // Register core modules
                $this->register( include "$IP/resources/Resources.php" );
                // Register extension modules
                wfRunHooks( 'ResourceLoaderRegisterModules', array( &$this ) );
-               $this->register( $wgResourceModules );
+               $this->register( $config->get( 'ResourceModules' ) );
 
-               if ( $wgEnableJavaScriptTest === true ) {
+               if ( $config->get( 'EnableJavaScriptTest' ) === true ) {
                        $this->registerTestModules();
                }
 
                wfProfileOut( __METHOD__ );
        }
 
+       /**
+        * @return Config
+        */
+       public function getConfig() {
+               return $this->config;
+       }
+
        /**
         * Register a module with the ResourceLoader system.
         *
@@ -298,8 +309,7 @@ class ResourceLoader {
 
                        // Apply custom skin-defined styles to existing modules.
                        if ( $this->isFileModule( $name ) ) {
-                               global $wgResourceModuleSkinStyles;
-                               foreach ( $wgResourceModuleSkinStyles as $skinName => $skinStyles ) {
+                               foreach ( $this->config->get( 'ResourceModuleSkinStyles' ) as $skinName => $skinStyles ) {
                                        // If this module already defines skinStyles for this skin, ignore $wgResourceModuleSkinStyles.
                                        if ( isset( $this->moduleInfos[$name]['skinStyles'][$skinName] ) ) {
                                                continue;
@@ -342,9 +352,9 @@ class ResourceLoader {
        /**
         */
        public function registerTestModules() {
-               global $IP, $wgEnableJavaScriptTest;
+               global $IP;
 
-               if ( $wgEnableJavaScriptTest !== true ) {
+               if ( $this->config->get( 'EnableJavaScriptTest' ) !== true ) {
                        throw new MWException( 'Attempt to register JavaScript test modules '
                                . 'but <code>$wgEnableJavaScriptTest</code> is false. '
                                . 'Edit your <code>LocalSettings.php</code> to enable it.' );
@@ -385,14 +395,12 @@ class ResourceLoader {
        /**
         * Add a foreign source of modules.
         *
-        * Source properties:
-        * 'loadScript': URL (either fully-qualified or protocol-relative) of load.php for this source
-        *
-        * @param mixed $id Source ID (string), or array( id1 => props1, id2 => props2, ... )
-        * @param array $properties Source properties
+        * @param array|string $id Source ID (string), or array( id1 => loadUrl, id2 => loadUrl, ... )
+        * @param string|array $loadUrl load.php url (string), or array with loadUrl key for
+        *  backwards-compatability.
         * @throws MWException
         */
-       public function addSource( $id, $properties = null ) {
+       public function addSource( $id, $loadUrl = null ) {
                // Allow multiple sources to be registered in one call
                if ( is_array( $id ) ) {
                        foreach ( $id as $key => $value ) {
@@ -409,14 +417,18 @@ class ResourceLoader {
                        );
                }
 
-               // Validate properties
-               foreach ( self::$requiredSourceProperties as $prop ) {
-                       if ( !isset( $properties[$prop] ) ) {
-                               throw new MWException( "Required property $prop missing from source ID $id" );
+               // Pre 1.24 backwards-compatability
+               if ( is_array( $loadUrl ) ) {
+                       if ( !isset( $loadUrl['loadScript'] ) ) {
+                               throw new MWException(
+                                       __METHOD__ . ' was passed an array with no "loadScript" key.'
+                               );
                        }
+
+                       $loadUrl = $loadUrl['loadScript'];
                }
 
-               $this->sources[$id] = $properties;
+               $this->sources[$id] = $loadUrl;
        }
 
        /**
@@ -480,7 +492,9 @@ class ResourceLoader {
                                } else {
                                        $class = $info['class'];
                                }
+                               /** @var ResourceLoaderModule $object */
                                $object = new $class( $info );
+                               $object->setConfig( $this->getConfig() );
                        }
                        $object->setName( $name );
                        $this->modules[$name] = $object;
@@ -509,7 +523,7 @@ class ResourceLoader {
        /**
         * Get the list of sources.
         *
-        * @return array Like array( id => array of properties, .. )
+        * @return array Like array( id => load.php url, .. )
         */
        public function getSources() {
                return $this->sources;
@@ -528,7 +542,7 @@ class ResourceLoader {
                if ( !isset( $this->sources[$source] ) ) {
                        throw new MWException( "The $source source was never registered in ResourceLoader." );
                }
-               return $this->sources[$source]['loadScript'];
+               return $this->sources[$source];
        }
 
        /**
@@ -537,10 +551,8 @@ class ResourceLoader {
         * @param ResourceLoaderContext $context Context in which a response should be formed
         */
        public function respond( ResourceLoaderContext $context ) {
-               global $wgCacheEpoch, $wgUseFileCache;
-
                // Use file cache if enabled and available...
-               if ( $wgUseFileCache ) {
+               if ( $this->config->get( 'UseFileCache' ) ) {
                        $fileCache = ResourceFileCache::newFromContext( $context );
                        if ( $this->tryRespondFromFileCache( $fileCache, $context ) ) {
                                return; // output handled
@@ -596,7 +608,7 @@ class ResourceLoader {
 
                // To send Last-Modified and support If-Modified-Since, we need to detect
                // the last modified time
-               $mtime = wfTimestamp( TS_UNIX, $wgCacheEpoch );
+               $mtime = wfTimestamp( TS_UNIX, $this->config->get( 'CacheEpoch' ) );
                foreach ( $modules as $module ) {
                        /**
                         * @var $module ResourceLoaderModule
@@ -664,18 +676,18 @@ class ResourceLoader {
         * @return void
         */
        protected function sendResponseHeaders( ResourceLoaderContext $context, $mtime, $errors ) {
-               global $wgResourceLoaderMaxage;
+               $rlMaxage = $this->config->get( 'ResourceLoaderMaxage' );
                // If a version wasn't specified we need a shorter expiry time for updates
                // to propagate to clients quickly
                // If there were errors, we also need a shorter expiry time so we can recover quickly
                if ( is_null( $context->getVersion() ) || $errors ) {
-                       $maxage = $wgResourceLoaderMaxage['unversioned']['client'];
-                       $smaxage = $wgResourceLoaderMaxage['unversioned']['server'];
+                       $maxage = $rlMaxage['unversioned']['client'];
+                       $smaxage = $rlMaxage['unversioned']['server'];
                // If a version was specified we can use a longer expiry time since changing
                // version numbers causes cache misses
                } else {
-                       $maxage = $wgResourceLoaderMaxage['versioned']['client'];
-                       $smaxage = $wgResourceLoaderMaxage['versioned']['server'];
+                       $maxage = $rlMaxage['versioned']['client'];
+                       $smaxage = $rlMaxage['versioned']['server'];
                }
                if ( $context->getOnly() === 'styles' ) {
                        header( 'Content-Type: text/css; charset=utf-8' );
@@ -743,13 +755,13 @@ class ResourceLoader {
        protected function tryRespondFromFileCache(
                ResourceFileCache $fileCache, ResourceLoaderContext $context
        ) {
-               global $wgResourceLoaderMaxage;
+               $rlMaxage = $this->config->get( 'ResourceLoaderMaxage' );
                // Buffer output to catch warnings.
                ob_start();
                // Get the maximum age the cache can be
                $maxage = is_null( $context->getVersion() )
-                       ? $wgResourceLoaderMaxage['unversioned']['server']
-                       : $wgResourceLoaderMaxage['versioned']['server'];
+                       ? $rlMaxage['unversioned']['server']
+                       : $rlMaxage['versioned']['server'];
                // Minimum timestamp the cache file must have
                $good = $fileCache->isCacheGood( wfTimestamp( TS_MW, time() - $maxage ) );
                if ( !$good ) {
@@ -839,7 +851,7 @@ class ResourceLoader {
                // Pre-fetch blobs
                if ( $context->shouldIncludeMessages() ) {
                        try {
-                               $blobs = MessageBlobStore::get( $this, $modules, $context->getLanguage() );
+                               $blobs = MessageBlobStore::getInstance()->get( $this, $modules, $context->getLanguage() );
                        } catch ( Exception $e ) {
                                MWExceptionHandler::logException( $e );
                                wfDebugLog(
@@ -990,13 +1002,6 @@ class ResourceLoader {
                        if ( count( $states ) ) {
                                $out .= self::makeLoaderStateScript( $states );
                        }
-
-                       // In only=script requests for modules that are not raw (e.g. not the startup module)
-                       // ensure the execution is conditional to avoid situations where browsers with an
-                       // unsupported environment do unconditionally execute a module's scripts. Otherwise users
-                       // will get things like "ReferenceError: mw is undefined" or "jQuery is undefined" from
-                       // legacy scripts loaded with only=scripts (such as the 'site' module).
-                       $out = self::makeLoaderConditionalScript( $out );
                } else {
                        if ( count( $states ) ) {
                                $exceptions .= self::makeComment(
@@ -1163,11 +1168,13 @@ class ResourceLoader {
         * Returns JS code which calls mw.loader.register with the given
         * parameters. Has three calling conventions:
         *
-        *   - ResourceLoader::makeLoaderRegisterScript( $name, $version, $dependencies, $group, $source, $skip ):
-        *       Register a single module.
+        *   - ResourceLoader::makeLoaderRegisterScript( $name, $version,
+        *        $dependencies, $group, $source, $skip
+        *     ):
+        *        Register a single module.
         *
         *   - ResourceLoader::makeLoaderRegisterScript( array( $name1, $name2 ) ):
-        *       Register modules with the given names.
+        *        Register modules with the given names.
         *
         *   - ResourceLoader::makeLoaderRegisterScript( array(
         *        array( $name1, $version1, $dependencies1, $group1, $source1, $skip1 ),
@@ -1210,7 +1217,7 @@ class ResourceLoader {
         *   - ResourceLoader::makeLoaderSourcesScript( $id, $properties ):
         *       Register a single source
         *
-        *   - ResourceLoader::makeLoaderSourcesScript( array( $id1 => $props1, $id2 => $props2, ... ) );
+        *   - ResourceLoader::makeLoaderSourcesScript( array( $id1 => $loadUrl, $id2 => $loadUrl, ... ) );
         *       Register sources with the given IDs and properties.
         *
         * @param string $id Source ID
@@ -1450,12 +1457,12 @@ class ResourceLoader {
        /**
         * Returns LESS compiler set up for use with MediaWiki
         *
+        * @param Config $config
+        * @throws MWException
         * @since 1.22
         * @return lessc
         */
-       public static function getLessCompiler() {
-               global $wgResourceLoaderLESSFunctions, $wgResourceLoaderLESSImportPaths;
-
+       public static function getLessCompiler( Config $config ) {
                // When called from the installer, it is possible that a required PHP extension
                // is missing (at least for now; see bug 47564). If this is the case, throw an
                // exception (caught by the installer) to prevent a fatal error later on.
@@ -1465,9 +1472,9 @@ class ResourceLoader {
 
                $less = new lessc();
                $less->setPreserveComments( true );
-               $less->setVariables( self::getLESSVars() );
-               $less->setImportDir( $wgResourceLoaderLESSImportPaths );
-               foreach ( $wgResourceLoaderLESSFunctions as $name => $func ) {
+               $less->setVariables( self::getLessVars( $config ) );
+               $less->setImportDir( $config->get( 'ResourceLoaderLESSImportPaths' ) );
+               foreach ( $config->get( 'ResourceLoaderLESSFunctions' ) as $name => $func ) {
                        $less->registerFunction( $name, $func );
                }
                return $less;
@@ -1476,13 +1483,12 @@ class ResourceLoader {
        /**
         * Get global LESS variables.
         *
-        * $since 1.22
+        * @param Config $config
+        * @since 1.22
         * @return array Map of variable names to string CSS values.
         */
-       public static function getLESSVars() {
-               global $wgResourceLoaderLESSVars;
-
-               $lessVars = $wgResourceLoaderLESSVars;
+       public static function getLessVars( Config $config ) {
+               $lessVars = $config->get( 'ResourceLoaderLESSVars' );
                // Sort by key to ensure consistent hashing for cache lookups.
                ksort( $lessVars );
                return $lessVars;