/** @var ResourceLoader */
private $resourceLoader;
- /** @var string|null */
- private $target;
+ /** @var array */
+ private $options;
/** @var array */
private $config = [];
/**
* @param ResourceLoaderContext $context
- * @param string|null $target [optional] Custom 'target' parameter for the startup module
+ * @param array $options [optional] Array of options
+ * - 'target': Custom parameter passed to StartupModule.
*/
- public function __construct( ResourceLoaderContext $context, $target = null ) {
+ public function __construct( ResourceLoaderContext $context, array $options = [] ) {
$this->context = $context;
$this->resourceLoader = $context->getResourceLoader();
- $this->target = $target;
+ $this->options = $options;
}
/**
continue;
}
- $context = $this->getContext( $module->getGroup(), ResourceLoaderModule::TYPE_COMBINED );
+ $group = $module->getGroup();
+ $context = $this->getContext( $group, ResourceLoaderModule::TYPE_COMBINED );
if ( $module->isKnownEmpty( $context ) ) {
// Avoid needless request or embed for empty module
$data['states'][$name] = 'ready';
continue;
}
- if ( $module->shouldEmbedModule( $this->context ) ) {
- // Embed via mw.loader.implement per T36907.
+ if ( $group === 'user' || $module->shouldEmbedModule( $this->context ) ) {
+ // Call makeLoad() to decide how to load these, instead of
+ // loading via mw.loader.load().
+ // - For group=user: We need to provide a pre-generated load.php
+ // url to the client that has the 'user' and 'version' parameters
+ // filled in. Without this, the client would wrongly use the static
+ // version hash, per T64602.
+ // - For shouldEmbed=true: Embed via mw.loader.implement, per T36907.
$data['embed']['general'][] = $name;
// Avoid duplicate request from mw.loader
$data['states'][$name] = 'loading';
* - Inline scripts can't be asynchronous.
* - For styles, earlier is better.
*
+ * @param string $nonce From OutputPage::getCSPNonce()
* @return string|WrappedStringList HTML
*/
- public function getHeadHtml() {
+ public function getHeadHtml( $nonce ) {
$data = $this->getData();
$chunks = [];
// See also #getDocumentAttributes() and /resources/src/startup.js.
$chunks[] = Html::inlineScript(
'document.documentElement.className = document.documentElement.className'
- . '.replace( /(^|\s)client-nojs(\s|$)/, "$1client-js$2" );'
+ . '.replace( /(^|\s)client-nojs(\s|$)/, "$1client-js$2" );',
+ $nonce
);
// Inline RLQ: Set page variables
if ( $this->config ) {
$chunks[] = ResourceLoader::makeInlineScript(
- ResourceLoader::makeConfigSetScript( $this->config )
+ ResourceLoader::makeConfigSetScript( $this->config ),
+ $nonce
);
}
$states = array_merge( $this->exemptStates, $data['states'] );
if ( $states ) {
$chunks[] = ResourceLoader::makeInlineScript(
- ResourceLoader::makeLoaderStateScript( $states )
+ ResourceLoader::makeLoaderStateScript( $states ),
+ $nonce
);
}
if ( $data['embed']['general'] ) {
$chunks[] = $this->getLoad(
$data['embed']['general'],
- ResourceLoaderModule::TYPE_COMBINED
+ ResourceLoaderModule::TYPE_COMBINED,
+ $nonce
);
}
// Inline RLQ: Load general modules
if ( $data['general'] ) {
$chunks[] = ResourceLoader::makeInlineScript(
- Xml::encodeJsCall( 'mw.loader.load', [ $data['general'] ] )
+ Xml::encodeJsCall( 'mw.loader.load', [ $data['general'] ] ),
+ $nonce
);
}
if ( $data['scripts'] ) {
$chunks[] = $this->getLoad(
$data['scripts'],
- ResourceLoaderModule::TYPE_SCRIPTS
+ ResourceLoaderModule::TYPE_SCRIPTS,
+ $nonce
);
}
if ( $data['styles'] ) {
$chunks[] = $this->getLoad(
$data['styles'],
- ResourceLoaderModule::TYPE_STYLES
+ ResourceLoaderModule::TYPE_STYLES,
+ $nonce
);
}
if ( $data['embed']['styles'] ) {
$chunks[] = $this->getLoad(
$data['embed']['styles'],
- ResourceLoaderModule::TYPE_STYLES
+ ResourceLoaderModule::TYPE_STYLES,
+ $nonce
);
}
// Async scripts. Once the startup is loaded, inline RLQ scripts will run.
- // Pass-through a custom target from OutputPage (T143066).
- $startupQuery = $this->target ? [ 'target' => $this->target ] : [];
+ // Pass-through a custom 'target' from OutputPage (T143066).
+ $startupQuery = isset( $this->options['target'] )
+ ? [ 'target' => (string)$this->options['target'] ]
+ : [];
$chunks[] = $this->getLoad(
'startup',
ResourceLoaderModule::TYPE_SCRIPTS,
+ $nonce,
$startupQuery
);
return self::makeContext( $this->context, $group, $type );
}
- private function getLoad( $modules, $only, array $extraQuery = [] ) {
- return self::makeLoad( $this->context, (array)$modules, $only, $extraQuery );
+ private function getLoad( $modules, $only, $nonce, array $extraQuery = [] ) {
+ return self::makeLoad( $this->context, (array)$modules, $only, $extraQuery, $nonce );
}
private static function makeContext( ResourceLoaderContext $mainContext, $group, $type,
}
$context = new ResourceLoaderContext( $mainContext->getResourceLoader(), $req );
// Allow caller to setVersion() and setModules()
- return new DerivativeResourceLoaderContext( $context );
+ $ret = new DerivativeResourceLoaderContext( $context );
+ $ret->setContentOverrideCallback( $mainContext->getContentOverrideCallback() );
+ return $ret;
}
/**
* @param ResourceLoaderContext $mainContext
* @param array $modules One or more module names
* @param string $only ResourceLoaderModule TYPE_ class constant
- * @param array $extraQuery [optional] Array with extra query parameters for the request
+ * @param array $extraQuery Array with extra query parameters for the request
+ * @param string $nonce See OutputPage::getCSPNonce() [Since 1.32]
* @return string|WrappedStringList HTML
*/
public static function makeLoad( ResourceLoaderContext $mainContext, array $modules, $only,
- array $extraQuery = []
+ array $extraQuery, $nonce
) {
$rl = $mainContext->getResourceLoader();
$chunks = [];
$chunks = [];
// Recursively call us for every item
foreach ( $modules as $name ) {
- $chunks[] = self::makeLoad( $mainContext, [ $name ], $only, $extraQuery );
+ $chunks[] = self::makeLoad( $mainContext, [ $name ], $only, $extraQuery, $nonce );
}
return new WrappedStringList( "\n", $chunks );
}
);
} else {
$chunks[] = ResourceLoader::makeInlineScript(
- $rl->makeModuleResponse( $context, $moduleSet )
+ $rl->makeModuleResponse( $context, $moduleSet ),
+ $nonce
);
}
} else {
] );
} else {
$chunk = ResourceLoader::makeInlineScript(
- Xml::encodeJsCall( 'mw.loader.load', [ $url ] )
+ Xml::encodeJsCall( 'mw.loader.load', [ $url ] ),
+ $nonce
);
}
}