Merge "resourceloader: Wrap only=script responses in "if(window.mw)""
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 13 Aug 2014 00:50:10 +0000 (00:50 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 13 Aug 2014 00:50:10 +0000 (00:50 +0000)
1  2 
includes/OutputPage.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderStartUpModule.php
resources/Resources.php
tests/phpunit/includes/OutputPageTest.php

diff --combined includes/OutputPage.php
@@@ -1002,9 -1002,9 +1002,9 @@@ class OutputPage extends ContextSource 
         * Add a subtitle containing a backlink to a page
         *
         * @param Title $title Title to link to
 +       * @param array $query Array of additional parameters to include in the link
         */
 -      public function addBacklinkSubtitle( Title $title ) {
 -              $query = array();
 +      public function addBacklinkSubtitle( Title $title, $query = array() ) {
                if ( $title->isRedirect() ) {
                        $query['redirect'] = 'no';
                }
@@@ -2797,9 -2797,7 +2797,7 @@@ $template
                                                );
                                        } else {
                                                $links['html'] .= Html::inlineScript(
-                                                       ResourceLoader::makeLoaderConditionalScript(
-                                                               $resourceLoader->makeModuleResponse( $context, $grpModules )
-                                                       )
+                                                       $resourceLoader->makeModuleResponse( $context, $grpModules )
                                                );
                                        }
                                        $links['html'] .= "\n";
@@@ -44,9 -44,6 +44,9 @@@ 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', .. ), .. )
         * @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
                        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 */";
        /**
         * Register core modules and runs registration hooks.
         */
 -      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' ) )
 +                      array( 'loadScript' => wfScript( 'load' ), 'apiScript' => wfScript( 'api' ) )
                );
  
                // 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.
         *
  
                        // 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;
        /**
         */
        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.' );
                                } else {
                                        $class = $info['class'];
                                }
 +                              /** @var ResourceLoaderModule $object */
                                $object = new $class( $info );
 +                              $object->setConfig( $this->getConfig() );
                        }
                        $object->setName( $name );
                        $this->modules[$name] = $object;
         * @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
  
                // 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
         * @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' );
        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 ) {
                        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(
        /**
         * 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.
  
                $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;
        /**
         * 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;
@@@ -36,14 -36,22 +36,14 @@@ class ResourceLoaderStartUpModule exten
         * @param ResourceLoaderContext $context
         * @return array
         */
 -      protected function getConfig( $context ) {
 +      protected function getConfigSettings( $context ) {
  
                $hash = $context->getHash();
                if ( isset( $this->configVars[$hash] ) ) {
                        return $this->configVars[$hash];
                }
  
 -              global $wgLoadScript, $wgScript, $wgStylePath, $wgScriptExtension,
 -                      $wgArticlePath, $wgScriptPath, $wgServer, $wgServerName,
 -                      $wgContLang, $wgVariantArticlePath, $wgActionPaths, $wgVersion,
 -                      $wgEnableAPI, $wgEnableWriteAPI, $wgDBname,
 -                      $wgSitename, $wgFileExtensions, $wgExtensionAssetsPath,
 -                      $wgCookiePrefix, $wgCookieDomain, $wgCookiePath,
 -                      $wgCookieExpiration, $wgResourceLoaderMaxQueryLength,
 -                      $wgResourceLoaderStorageEnabled, $wgResourceLoaderStorageVersion,
 -                      $wgSearchType;
 +              global $wgContLang;
  
                $mainPage = Title::newMainPage();
  
                        }
                }
  
 +              $conf = $this->getConfig();
                // Build list of variables
                $vars = array(
 -                      'wgLoadScript' => $wgLoadScript,
 +                      'wgLoadScript' => wfScript( 'load' ),
                        'debug' => $context->getDebug(),
                        'skin' => $context->getSkin(),
 -                      'stylepath' => $wgStylePath,
 +                      'stylepath' => $conf->get( 'StylePath' ),
                        'wgUrlProtocols' => wfUrlProtocols(),
 -                      'wgArticlePath' => $wgArticlePath,
 -                      'wgScriptPath' => $wgScriptPath,
 -                      'wgScriptExtension' => $wgScriptExtension,
 -                      'wgScript' => $wgScript,
 -                      'wgSearchType' => $wgSearchType,
 -                      'wgVariantArticlePath' => $wgVariantArticlePath,
 +                      'wgArticlePath' => $conf->get( 'ArticlePath' ),
 +                      'wgScriptPath' => $conf->get( 'ScriptPath' ),
 +                      'wgScriptExtension' => $conf->get( 'ScriptExtension' ),
 +                      'wgScript' => wfScript(),
 +                      'wgSearchType' => $conf->get( 'SearchType' ),
 +                      'wgVariantArticlePath' => $conf->get( 'VariantArticlePath' ),
                        // Force object to avoid "empty" associative array from
                        // becoming [] instead of {} in JS (bug 34604)
 -                      'wgActionPaths' => (object)$wgActionPaths,
 -                      'wgServer' => $wgServer,
 -                      'wgServerName' => $wgServerName,
 +                      'wgActionPaths' => (object)$conf->get( 'ActionPaths' ),
 +                      'wgServer' => $conf->get( 'Server' ),
 +                      'wgServerName' => $conf->get( 'ServerName' ),
                        'wgUserLanguage' => $context->getLanguage(),
                        'wgContentLanguage' => $wgContLang->getCode(),
 -                      'wgVersion' => $wgVersion,
 -                      'wgEnableAPI' => $wgEnableAPI,
 -                      'wgEnableWriteAPI' => $wgEnableWriteAPI,
 +                      'wgVersion' => $conf->get( 'Version' ),
 +                      'wgEnableAPI' => $conf->get( 'EnableAPI' ),
 +                      'wgEnableWriteAPI' => $conf->get( 'EnableWriteAPI' ),
                        'wgMainPageTitle' => $mainPage->getPrefixedText(),
                        'wgFormattedNamespaces' => $wgContLang->getFormattedNamespaces(),
                        'wgNamespaceIds' => $namespaceIds,
                        'wgContentNamespaces' => MWNamespace::getContentNamespaces(),
 -                      'wgSiteName' => $wgSitename,
 -                      'wgFileExtensions' => array_values( array_unique( $wgFileExtensions ) ),
 -                      'wgDBname' => $wgDBname,
 +                      'wgSiteName' => $conf->get( 'Sitename' ),
 +                      'wgFileExtensions' => array_values( array_unique( $conf->get( 'FileExtensions' ) ) ),
 +                      'wgDBname' => $conf->get( 'DBname' ),
                        // This sucks, it is only needed on Special:Upload, but I could
                        // not find a way to add vars only for a certain module
                        'wgFileCanRotate' => BitmapHandler::canRotate(),
                        'wgAvailableSkins' => Skin::getSkinNames(),
 -                      'wgExtensionAssetsPath' => $wgExtensionAssetsPath,
 +                      'wgExtensionAssetsPath' => $conf->get( 'ExtensionAssetsPath' ),
                        // MediaWiki sets cookies to have this prefix by default
 -                      'wgCookiePrefix' => $wgCookiePrefix,
 -                      'wgCookieDomain' => $wgCookieDomain,
 -                      'wgCookiePath' => $wgCookiePath,
 -                      'wgCookieExpiration' => $wgCookieExpiration,
 -                      'wgResourceLoaderMaxQueryLength' => $wgResourceLoaderMaxQueryLength,
 +                      'wgCookiePrefix' => $conf->get( 'CookiePrefix' ),
 +                      'wgCookieDomain' => $conf->get( 'CookieDomain' ),
 +                      'wgCookiePath' => $conf->get( 'CookiePath' ),
 +                      'wgCookieExpiration' => $conf->get( 'CookieExpiration' ),
 +                      'wgResourceLoaderMaxQueryLength' => $conf->get( 'ResourceLoaderMaxQueryLength' ),
                        'wgCaseSensitiveNamespaces' => $caseSensitiveNamespaces,
                        'wgLegalTitleChars' => Title::convertByteClassToUnicodeClass( Title::legalChars() ),
 -                      'wgResourceLoaderStorageVersion' => $wgResourceLoaderStorageVersion,
 -                      'wgResourceLoaderStorageEnabled' => $wgResourceLoaderStorageEnabled,
 +                      'wgResourceLoaderStorageVersion' => $conf->get( 'ResourceLoaderStorageVersion' ),
 +                      'wgResourceLoaderStorageEnabled' => $conf->get( 'ResourceLoaderStorageEnabled' ),
                );
  
                wfRunHooks( 'ResourceLoaderGetConfigVars', array( &$vars ) );
         * @param ResourceLoaderContext $context
         * @return string JavaScript code for registering all modules with the client loader
         */
 -      public static function getModuleRegistrations( ResourceLoaderContext $context ) {
 -              global $wgCacheEpoch;
 +      public function getModuleRegistrations( ResourceLoaderContext $context ) {
                wfProfileIn( __METHOD__ );
  
                $resourceLoader = $context->getResourceLoader();
                        // getModifiedTime() is supposed to return a UNIX timestamp, but it doesn't always
                        // seem to do that, and custom implementations might forget. Coerce it to TS_UNIX
                        $moduleMtime = wfTimestamp( TS_UNIX, $module->getModifiedTime( $context ) );
 -                      $mtime = max( $moduleMtime, wfTimestamp( TS_UNIX, $wgCacheEpoch ) );
 +                      $mtime = max( $moduleMtime, wfTimestamp( TS_UNIX, $this->getConfig()->get( 'CacheEpoch' ) ) );
  
                        // FIXME: Convert to numbers, wfTimestamp always gives us stings, even for TS_UNIX
  
                return true;
        }
  
+       /**
+        * Base modules required for the the base environment of ResourceLoader
+        *
+        * @return array
+        */
+       public static function getStartupModules() {
+               return array( 'jquery', 'mediawiki' );
+       }
        /**
         * Get the load URL of the startup modules.
         *
         * @return string
         */
        public static function getStartupModulesUrl( ResourceLoaderContext $context ) {
-               // The core modules:
-               $moduleNames = array( 'jquery', 'mediawiki' );
+               $moduleNames = self::getStartupModules();
  
                // Get the latest version
                $loader = $context->getResourceLoader();
         * @return string
         */
        public function getScript( ResourceLoaderContext $context ) {
 -              global $IP, $wgLegacyJavaScriptGlobals;
 +              global $IP;
  
                $out = file_get_contents( "$IP/resources/src/startup.js" );
                if ( $context->getOnly() === 'scripts' ) {
  
                        // Startup function
 -                      $configuration = $this->getConfig( $context );
 -                      $registrations = self::getModuleRegistrations( $context );
 +                      $configuration = $this->getConfigSettings( $context );
 +                      $registrations = $this->getModuleRegistrations( $context );
                        // Fix indentation
                        $registrations = str_replace( "\n", "\n\t", trim( $registrations ) );
                        $out .= "var startUp = function () {\n" .
                                "\tmw.config = new " .
 -                              Xml::encodeJsCall( 'mw.Map', array( $wgLegacyJavaScriptGlobals ) ) . "\n" .
 +                              Xml::encodeJsCall( 'mw.Map', array( $this->getConfig()->get( 'LegacyJavaScriptGlobals' ) ) ) . "\n" .
                                "\t$registrations\n" .
                                "\t" . Xml::encodeJsCall( 'mw.config.set', array( $configuration ) ) .
                                "};\n";
         * @return array|mixed
         */
        public function getModifiedTime( ResourceLoaderContext $context ) {
 -              global $IP, $wgCacheEpoch;
 +              global $IP;
  
                $hash = $context->getHash();
                if ( isset( $this->modifiedTime[$hash] ) ) {
                $loader->preloadModuleInfo( $loader->getModuleNames(), $context );
  
                $time = max(
 -                      wfTimestamp( TS_UNIX, $wgCacheEpoch ),
 +                      wfTimestamp( TS_UNIX, $this->getConfig()->get( 'CacheEpoch' ) ),
                        filemtime( "$IP/resources/src/startup.js" ),
                        $this->getHashMtime( $context )
                );
         * @return string Hash
         */
        public function getModifiedHash( ResourceLoaderContext $context ) {
 -              global $wgLegacyJavaScriptGlobals;
 -
                $data = array(
 -                      'vars' => $this->getConfig( $context ),
 -                      'wgLegacyJavaScriptGlobals' => $wgLegacyJavaScriptGlobals,
 +                      'vars' => $this->getConfigSettings( $context ),
 +                      'wgLegacyJavaScriptGlobals' => $this->getConfig()->get( 'LegacyJavaScriptGlobals' ),
                );
  
                return md5( serialize( $data ) );
diff --combined resources/Resources.php
@@@ -128,7 -128,7 +128,7 @@@ return array
                        array(
                                'resources/lib/jquery/jquery.js'
                        ) ),
-               'debugRaw' => false,
+               'raw' => true,
                'targets' => array( 'desktop', 'mobile' ),
        ),
  
        'mediawiki' => array(
                'scripts' => 'resources/src/mediawiki/mediawiki.js',
                'debugScripts' => 'resources/src/mediawiki/mediawiki.log.js',
-               'debugRaw' => false,
+               'raw' => true,
                'targets' => array( 'desktop', 'mobile' ),
        ),
        'mediawiki.api' => array(
                        'user.tokens',
                ),
        ),
 +      'mediawiki.content.json' => array(
 +              'styles' => 'resources/src/mediawiki/mediawiki.content.json.css',
 +      ),
        'mediawiki.debug' => array(
                'scripts' => array(
                        'resources/src/mediawiki/mediawiki.debug.js',
@@@ -25,7 -25,7 +25,7 @@@ class OutputPageTest extends MediaWikiT
         * options['expectedReturn'] - expected return value
         * options['message'] - PHPUnit message for assertion
         *
 -       * @param array $args key-value array of arguments as shown above
 +       * @param array $args Key-value array of arguments as shown above
         */
        protected function assertTransformCssMediaCase( $args ) {
                $queryData = array();
                        // Load module script only
                        array(
                                array( 'test.foo', ResourceLoaderModule::TYPE_SCRIPTS ),
 -                              '<script src="http://127.0.0.1:8080/w/load.php?debug=false&amp;lang=en&amp;modules=test.foo&amp;only=scripts&amp;skin=vector&amp;*"></script>
 +                              '<script src="http://127.0.0.1:8080/w/load.php?debug=false&amp;lang=en&amp;modules=test.foo&amp;only=scripts&amp;skin=fallback&amp;*"></script>
  '
                        ),
                        // Load module styles only
                        // This also tests the order the modules are put into the url
                        array(
                                array( array( 'test.baz', 'test.foo', 'test.bar' ), ResourceLoaderModule::TYPE_STYLES ),
 -                              '<link rel=stylesheet href="http://127.0.0.1:8080/w/load.php?debug=false&amp;lang=en&amp;modules=test.bar%2Cbaz%2Cfoo&amp;only=styles&amp;skin=vector&amp;*">
 +                              '<link rel=stylesheet href="http://127.0.0.1:8080/w/load.php?debug=false&amp;lang=en&amp;modules=test.bar%2Cbaz%2Cfoo&amp;only=styles&amp;skin=fallback&amp;*">
  '
                        ),
                        // Load private module (combined)
                        array(
                                array( 'test.quux', ResourceLoaderModule::TYPE_COMBINED ),
-                               '<script>if(window.mw){
- mw.loader.implement("test.quux",function($,jQuery){mw.test.baz({token:123});},{"css":[".mw-icon{transition:none}\n/* cache key: wiki:resourceloader:filter:minify-css:7:fd8ea20b3336b2bfb230c789d430067a */"]},{});
- /* cache key: wiki:resourceloader:filter:minify-js:7:274ccee17be73cd5f4dda5dc2a819188 */
- }</script>
+                               '<script>if(window.mw){mw.loader.implement("test.quux",function($,jQuery){mw.test.baz({token:123});},{"css":[".mw-icon{transition:none}\n"]},{});}
+ </script>
  '
                        ),
                        // Load module script with with ESI
                        array(
                                array( 'test.foo', ResourceLoaderModule::TYPE_SCRIPTS, true ),
 -                              '<script><esi:include src="http://127.0.0.1:8080/w/load.php?debug=false&amp;lang=en&amp;modules=test.foo&amp;only=scripts&amp;skin=vector&amp;*" /></script>
 +                              '<script><esi:include src="http://127.0.0.1:8080/w/load.php?debug=false&amp;lang=en&amp;modules=test.foo&amp;only=scripts&amp;skin=fallback&amp;*" /></script>
  '
                        ),
                        // Load module styles with with ESI
                        array(
                                array( 'test.foo', ResourceLoaderModule::TYPE_STYLES, true ),
 -                              '<style><esi:include src="http://127.0.0.1:8080/w/load.php?debug=false&amp;lang=en&amp;modules=test.foo&amp;only=styles&amp;skin=vector&amp;*" /></style>
 +                              '<style><esi:include src="http://127.0.0.1:8080/w/load.php?debug=false&amp;lang=en&amp;modules=test.foo&amp;only=styles&amp;skin=fallback&amp;*" /></style>
  ',
                        ),
                );
                        'wgLoadScript' => 'http://127.0.0.1:8080/w/load.php',
                        // Affects whether CDATA is inserted
                        'wgWellFormedXml' => false,
-                       // Cache key is based on database name, and affects output;
-                       // this test should not touch the database anyways.
-                       'wgDBname' => 'wiki',
-                       'wgDBprefix' => '',
                ) );
                $class = new ReflectionClass( 'OutputPage' );
                $method = $class->getMethod( 'makeResourceLoaderLink' );
                $method->setAccessible( true );
                $ctx = new RequestContext();
 -              $ctx->setSkin( Skin::newFromKey( 'vector' ) );
 +              $ctx->setSkin( SkinFactory::getDefaultInstance()->makeSkin( 'fallback' ) );
                $ctx->setLanguage( 'en' );
                $out = new OutputPage( $ctx );
                $rl = $out->getResourceLoader();
                        )),
                ) );
                $links = $method->invokeArgs( $out, $args );
-               $this->assertEquals( $expectedHtml, $links['html'] );
+               // Strip comments to avoid variation due to wgDBname in WikiID and cache key
+               $actualHtml = preg_replace( '#/\*[^*]+\*/#', '', $links['html'] );
+               $this->assertEquals( $expectedHtml, $actualHtml );
        }
  }