* See also: OutputPage::disallowUserJs()
*/
class ResourceLoaderStartUpModule extends ResourceLoaderModule {
-
- // Cache for getConfigSettings() as it's called by multiple methods
- protected $configVars = [];
protected $targets = [ 'desktop', 'mobile' ];
/**
* @param ResourceLoaderContext $context
* @return array
*/
- protected function getConfigSettings( $context ) {
- $hash = $context->getHash();
- if ( isset( $this->configVars[$hash] ) ) {
- return $this->configVars[$hash];
- }
-
+ private function getConfigSettings( $context ) {
$conf = $this->getConfig();
// We can't use Title::newMainPage() if 'mainpage' is in
Hooks::run( 'ResourceLoaderGetConfigVars', [ &$vars ] );
- $this->configVars[$hash] = $vars;
- return $this->configVars[$hash];
+ return $vars;
}
/**
$out = '';
$states = [];
$registryData = [];
+ $moduleNames = $resourceLoader->getModuleNames();
+
+ // Preload with a batch so that the below calls to getVersionHash() for each module
+ // don't require on-demand loading of more information.
+ try {
+ $resourceLoader->preloadModuleInfo( $moduleNames, $context );
+ } catch ( Exception $e ) {
+ // Don't fail the request (T152266)
+ // Also print the error in the main output
+ $resourceLoader->outputErrorAndLog( $e,
+ 'Preloading module info from startup failed: {exception}',
+ [ 'exception' => $e ]
+ );
+ }
// Get registry data
- foreach ( $resourceLoader->getModuleNames() as $name ) {
+ foreach ( $moduleNames as $name ) {
$module = $resourceLoader->getModule( $name );
$moduleTargets = $module->getTargets();
if (
}
if ( $module->isRaw() ) {
- // Don't register "raw" modules (like 'jquery' and 'mediawiki') client-side because
- // depending on them is illegal anyway and would only lead to them being reloaded
- // causing any state to be lost (like jQuery plugins, mw.config etc.)
+ // Don't register "raw" modules (like 'startup') client-side because depending on them
+ // is illegal anyway and would only lead to them being loaded a second time,
+ // causing any state to be lost.
+
+ // ATTENTION: Because of the line below, this is not going to cause infinite recursion.
+ // Think carefully before making changes to this code!
+ // The below code is going to call ResourceLoaderModule::getVersionHash() for every module.
+ // For StartUpModule (this module) the hash is computed based on the manifest content,
+ // which is the very thing we are computing right here. As such, this must skip iterating
+ // over 'startup' itself.
continue;
}
try {
$versionHash = $module->getVersionHash( $context );
} catch ( Exception $e ) {
- // See also T152266 and ResourceLoader::getCombinedVersion()
- MWExceptionHandler::logException( $e );
- $context->getLogger()->warning(
+ // Don't fail the request (T152266)
+ // Also print the error in the main output
+ $resourceLoader->outputErrorAndLog( $e,
'Calculating version for "{module}" failed: {exception}',
[
'module' => $name,
if ( $context->getDebug() ) {
$mwLoaderCode .= file_get_contents( "$IP/resources/src/startup/mediawiki.log.js" );
}
-
- $mapToJson = function ( $value ) {
- $value = FormatJson::encode( $value, ResourceLoader::inDebugMode(), FormatJson::ALL_OK );
- // Fix indentation
- $value = str_replace( "\n", "\n\t", $value );
- return $value;
- };
+ if ( $this->getConfig()->get( 'ResourceLoaderEnableJSProfiler' ) ) {
+ $mwLoaderCode .= file_get_contents( "$IP/resources/src/startup/profiler.js" );
+ }
// Perform replacements for mediawiki.js
- $mwLoaderCode = strtr( $mwLoaderCode, [
- '$VARS.baseModules' => $mapToJson( $this->getBaseModules() ),
- ] );
-
- // Perform replacements for startup.js
- $pairs = array_map( $mapToJson, [
- '$VARS.wgLegacyJavaScriptGlobals' => $this->getConfig()->get( 'LegacyJavaScriptGlobals' ),
- '$VARS.configuration' => $this->getConfigSettings( $context ),
- ] );
- // Raw JavaScript code (not for JSON)
- $pairs['$CODE.registrations();'] = str_replace(
- "\n",
- "\n\t",
- trim( $this->getModuleRegistrations( $context ) )
- );
- $pairs['$CODE.defineLoader();'] = $mwLoaderCode;
+ $mwLoaderPairs = [
+ '$VARS.baseModules' => ResourceLoader::encodeJsonForScript( $this->getBaseModules() ),
+ ];
+ $profilerStubs = [
+ '$CODE.profileExecuteStart();' => 'mw.loader.profiler.onExecuteStart( module );',
+ '$CODE.profileExecuteEnd();' => 'mw.loader.profiler.onExecuteEnd( module );',
+ '$CODE.profileScriptStart();' => 'mw.loader.profiler.onScriptStart( module );',
+ '$CODE.profileScriptEnd();' => 'mw.loader.profiler.onScriptEnd( module );',
+ ];
+ if ( $this->getConfig()->get( 'ResourceLoaderEnableJSProfiler' ) ) {
+ // When profiling is enabled, insert the calls.
+ $mwLoaderPairs += $profilerStubs;
+ } else {
+ // When disabled (by default), insert nothing.
+ $mwLoaderPairs += array_fill_keys( array_keys( $profilerStubs ), '' );
+ }
+ $mwLoaderCode = strtr( $mwLoaderCode, $mwLoaderPairs );
+
+ // Perform string replacements for startup.js
+ $pairs = [
+ '$VARS.wgLegacyJavaScriptGlobals' => ResourceLoader::encodeJsonForScript(
+ $this->getConfig()->get( 'LegacyJavaScriptGlobals' )
+ ),
+ '$VARS.configuration' => ResourceLoader::encodeJsonForScript(
+ $this->getConfigSettings( $context )
+ ),
+ // Raw JavaScript code (not JSON)
+ '$CODE.registrations();' => trim( $this->getModuleRegistrations( $context ) ),
+ '$CODE.defineLoader();' => $mwLoaderCode,
+ ];
$startupCode = strtr( $startupCode, $pairs );
return $startupCode;
}
/**
- * Get the definition summary for this module.
- *
- * @param ResourceLoaderContext $context
- * @return array
- */
- public function getDefinitionSummary( ResourceLoaderContext $context ) {
- global $IP;
- $summary = parent::getDefinitionSummary( $context );
- $summary[] = [
- // Detect changes to variables exposed in mw.config (T30899).
- 'vars' => $this->getConfigSettings( $context ),
- // Changes how getScript() creates mw.Map for mw.config
- 'wgLegacyJavaScriptGlobals' => $this->getConfig()->get( 'LegacyJavaScriptGlobals' ),
- // Detect changes to the module registrations
- 'moduleHashes' => $this->getAllModuleHashes( $context ),
-
- 'fileHashes' => [
- $this->safeFileHash( "$IP/resources/src/startup/startup.js" ),
- $this->safeFileHash( "$IP/resources/src/startup/mediawiki.js" ),
- $this->safeFileHash( "$IP/resources/src/startup/mediawiki.requestIdleCallback.js" ),
- ],
- ];
- return $summary;
- }
-
- /**
- * Helper method for getDefinitionSummary().
- *
- * @param ResourceLoaderContext $context
- * @return string SHA-1
+ * @return bool
*/
- protected function getAllModuleHashes( ResourceLoaderContext $context ) {
- $rl = $context->getResourceLoader();
- // Preload for getCombinedVersion()
- $rl->preloadModuleInfo( $rl->getModuleNames(), $context );
-
- // ATTENTION: Because of the line below, this is not going to cause infinite recursion.
- // Think carefully before making changes to this code!
- // Pre-populate versionHash with something because the loop over all modules below includes
- // the startup module (this module).
- // See ResourceLoaderModule::getVersionHash() for usage of this cache.
- $this->versionHash[$context->getHash()] = null;
-
- return $rl->getCombinedVersion( $context, $rl->getModuleNames() );
+ public function enableModuleContentVersion() {
+ // Enabling this means that ResourceLoader::getVersionHash will simply call getScript()
+ // and hash it to determine the version (as used by E-Tag HTTP response header).
+ return true;
}
/**