continue;
}
- $group = $module->getGroup();
-
- if ( $group === 'private' ) {
+ if ( $module->shouldEmbedModule( $this->context ) ) {
// Embed via mw.loader.implement per T36907.
$data['embed']['general'][] = $name;
// Avoid duplicate request from mw.loader
// Avoid needless request for empty module
$data['states'][$name] = 'ready';
} else {
- if ( $group === 'private' ) {
+ if ( $module->shouldEmbedModule( $this->context ) ) {
// Embed via style element
$data['embed']['styles'][] = $name;
// Avoid duplicate request from mw.loader
foreach ( $sortedModules as $source => $groups ) {
foreach ( $groups as $group => $grpModules ) {
$context = self::makeContext( $mainContext, $group, $only, $extraQuery );
- $context->setModules( array_keys( $grpModules ) );
-
- if ( $group === 'private' ) {
- // Decide whether to use style or script element
- if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
- $chunks[] = Html::inlineStyle(
- $rl->makeModuleResponse( $context, $grpModules )
- );
- } else {
- $chunks[] = ResourceLoader::makeInlineScript(
- $rl->makeModuleResponse( $context, $grpModules )
- );
- }
- continue;
- }
-
- // See if we have one or more raw modules
- $isRaw = false;
- foreach ( $grpModules as $key => $module ) {
- $isRaw |= $module->isRaw();
- }
- // Special handling for the user group; because users might change their stuff
- // on-wiki like user pages, or user preferences; we need to find the highest
- // timestamp of these user-changeable modules so we can ensure cache misses on change
- // This should NOT be done for the site group (T29564) because anons get that too
- // and we shouldn't be putting timestamps in CDN-cached HTML
- if ( $group === 'user' ) {
- // Must setModules() before makeVersionQuery()
- $context->setVersion( $rl->makeVersionQuery( $context ) );
+ // Separate sets of linked and embedded modules while preserving order
+ $moduleSets = [];
+ $idx = -1;
+ foreach ( $grpModules as $name => $module ) {
+ $shouldEmbed = $module->shouldEmbedModule( $context );
+ if ( !$moduleSets || $moduleSets[$idx][0] !== $shouldEmbed ) {
+ $moduleSets[++$idx] = [ $shouldEmbed, [] ];
+ }
+ $moduleSets[$idx][1][$name] = $module;
}
- $url = $rl->createLoaderURL( $source, $context, $extraQuery );
-
- // Decide whether to use 'style' or 'script' element
- if ( $only === ResourceLoaderModule::TYPE_STYLES ) {
- $chunk = Html::linkedStyle( $url );
- } else {
- if ( $context->getRaw() || $isRaw ) {
- $chunk = Html::element( 'script', [
- // In SpecialJavaScriptTest, QUnit must load synchronous
- 'async' => !isset( $extraQuery['sync'] ),
- 'src' => $url
- ] );
+ // Link/embed each set
+ foreach ( $moduleSets as list( $embed, $moduleSet ) ) {
+ $context->setModules( array_keys( $moduleSet ) );
+ if ( $embed ) {
+ // Decide whether to use style or script element
+ if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
+ $chunks[] = Html::inlineStyle(
+ $rl->makeModuleResponse( $context, $moduleSet )
+ );
+ } else {
+ $chunks[] = ResourceLoader::makeInlineScript(
+ $rl->makeModuleResponse( $context, $moduleSet )
+ );
+ }
} else {
- $chunk = ResourceLoader::makeInlineScript(
- Xml::encodeJsCall( 'mw.loader.load', [ $url ] )
- );
+ // See if we have one or more raw modules
+ $isRaw = false;
+ foreach ( $moduleSet as $key => $module ) {
+ $isRaw |= $module->isRaw();
+ }
+
+ // Special handling for the user group; because users might change their stuff
+ // on-wiki like user pages, or user preferences; we need to find the highest
+ // timestamp of these user-changeable modules so we can ensure cache misses on change
+ // This should NOT be done for the site group (T29564) because anons get that too
+ // and we shouldn't be putting timestamps in CDN-cached HTML
+ if ( $group === 'user' ) {
+ // Must setModules() before makeVersionQuery()
+ $context->setVersion( $rl->makeVersionQuery( $context ) );
+ }
+
+ $url = $rl->createLoaderURL( $source, $context, $extraQuery );
+
+ // Decide whether to use 'style' or 'script' element
+ if ( $only === ResourceLoaderModule::TYPE_STYLES ) {
+ $chunk = Html::linkedStyle( $url );
+ } else {
+ if ( $context->getRaw() || $isRaw ) {
+ $chunk = Html::element( 'script', [
+ // In SpecialJavaScriptTest, QUnit must load synchronous
+ 'async' => !isset( $extraQuery['sync'] ),
+ 'src' => $url
+ ] );
+ } else {
+ $chunk = ResourceLoader::makeInlineScript(
+ Xml::encodeJsCall( 'mw.loader.load', [ $url ] )
+ );
+ }
+ }
+
+ if ( $group == 'noscript' ) {
+ $chunks[] = Html::rawElement( 'noscript', [], $chunk );
+ } else {
+ $chunks[] = $chunk;
+ }
}
}
-
- if ( $group == 'noscript' ) {
- $chunks[] = Html::rawElement( 'noscript', [], $chunk );
- } else {
- $chunks[] = $chunk;
- }
}
}
return false;
}
+ /**
+ * Check whether this module should be embeded rather than linked
+ *
+ * Modules returning true here will be embedded rather than loaded by
+ * ResourceLoaderClientHtml.
+ *
+ * @since 1.30
+ * @param ResourceLoaderContext $context
+ * @return bool
+ */
+ public function shouldEmbedModule( ResourceLoaderContext $context ) {
+ return $this->getGroup() === 'private';
+ }
+
/** @var JSParser Lazy-initialized; use self::javaScriptParser() */
private static $jsParser;
private static $parseCacheVersion = 1;
protected $isKnownEmpty = false;
protected $type = ResourceLoaderModule::LOAD_GENERAL;
protected $targets = [ 'phpunit' ];
+ protected $shouldEmbed = null;
public function __construct( $options = [] ) {
foreach ( $options as $key => $value ) {
return $this->isKnownEmpty;
}
+ public function shouldEmbedModule( ResourceLoaderContext $context ) {
+ return $this->shouldEmbed !== null ? $this->shouldEmbed : parent::shouldEmbedModule( $context );
+ }
+
public function enableModuleContentVersion() {
return true;
}
'test.top' => [ 'position' => 'top' ],
'test.private.top' => [ 'group' => 'private', 'position' => 'top' ],
'test.private.bottom' => [ 'group' => 'private', 'position' => 'bottom' ],
+ 'test.shouldembed' => [ 'shouldEmbed' => true ],
'test.styles.pure' => [ 'type' => ResourceLoaderModule::LOAD_STYLES ],
'test.styles.mixed' => [],
'group' => 'private',
'styles' => '.private{}',
],
+ 'test.styles.shouldembed' => [
+ 'type' => ResourceLoaderModule::LOAD_STYLES,
+ 'shouldEmbed' => true,
+ 'styles' => '.shouldembed{}',
+ ],
'test.scripts' => [],
'test.scripts.top' => [ 'position' => 'top' ],
'test.scripts.user' => [ 'group' => 'user' ],
'test.scripts.user.empty' => [ 'group' => 'user', 'isKnownEmpty' => true ],
'test.scripts.raw' => [ 'isRaw' => true ],
+ 'test.scripts.shouldembed' => [ 'shouldEmbed' => true ],
+
+ 'test.ordering.a' => [ 'shouldEmbed' => false ],
+ 'test.ordering.b' => [ 'shouldEmbed' => false ],
+ 'test.ordering.c' => [ 'shouldEmbed' => true, 'styles' => '.orderingC{}' ],
+ 'test.ordering.d' => [ 'shouldEmbed' => true, 'styles' => '.orderingD{}' ],
+ 'test.ordering.e' => [ 'shouldEmbed' => false ],
];
return array_map( function ( $options ) {
return self::makeModule( $options );
'test.private.bottom',
'test.private.top',
'test.top',
+ 'test.shouldembed',
'test.unregistered',
] );
$client->setModuleStyles( [
'test.styles.user.empty',
'test.styles.private',
'test.styles.pure',
+ 'test.styles.shouldembed',
'test.unregistered.styles',
] );
$client->setModuleScripts( [
'test.scripts',
'test.scripts.user.empty',
'test.scripts.top',
+ 'test.scripts.shouldembed',
'test.unregistered.scripts',
] );
'states' => [
'test.private.top' => 'loading',
'test.private.bottom' => 'loading',
+ 'test.shouldembed' => 'loading',
'test.styles.pure' => 'ready',
'test.styles.user.empty' => 'ready',
'test.styles.private' => 'ready',
+ 'test.styles.shouldembed' => 'ready',
'test.scripts' => 'loading',
'test.scripts.top' => 'loading',
'test.scripts.user.empty' => 'ready',
+ 'test.scripts.shouldembed' => 'loading',
],
'general' => [
'test',
'scripts' => [
'test.scripts',
'test.scripts.top',
+ 'test.scripts.shouldembed',
],
'embed' => [
- 'styles' => [ 'test.styles.private' ],
+ 'styles' => [ 'test.styles.private', 'test.styles.shouldembed' ],
'general' => [
'test.private.bottom',
'test.private.top',
+ 'test.shouldembed',
],
],
];
'only' => ResourceLoaderModule::TYPE_STYLES,
'output' => '<noscript><link rel="stylesheet" href="/w/load.php?debug=false&lang=nl&modules=test.styles.noscript&only=styles&skin=fallback"/></noscript>',
],
+ [
+ 'context' => [],
+ 'modules' => [ 'test.shouldembed' ],
+ 'only' => ResourceLoaderModule::TYPE_COMBINED,
+ 'output' => '<script>(window.RLQ=window.RLQ||[]).push(function(){mw.loader.implement("test.shouldembed@09p30q0",function($,jQuery,require,module){},{"css":[]});});</script>',
+ ],
+ [
+ 'context' => [],
+ 'modules' => [ 'test.styles.shouldembed' ],
+ 'only' => ResourceLoaderModule::TYPE_STYLES,
+ 'output' => '<style>.shouldembed{}</style>',
+ ],
+ [
+ 'context' => [],
+ 'modules' => [ 'test.scripts.shouldembed' ],
+ 'only' => ResourceLoaderModule::TYPE_SCRIPTS,
+ 'output' => '<script>(window.RLQ=window.RLQ||[]).push(function(){mw.loader.state({"test.scripts.shouldembed":"ready"});});</script>',
+ ],
+ [
+ 'context' => [],
+ 'modules' => [ 'test', 'test.shouldembed' ],
+ 'only' => ResourceLoaderModule::TYPE_COMBINED,
+ 'output' => '<script>(window.RLQ=window.RLQ||[]).push(function(){mw.loader.load("/w/load.php?debug=false\u0026lang=nl\u0026modules=test\u0026skin=fallback");mw.loader.implement("test.shouldembed@09p30q0",function($,jQuery,require,module){},{"css":[]});});</script>',
+ ],
+ [
+ 'context' => [],
+ 'modules' => [ 'test.styles.pure', 'test.styles.shouldembed' ],
+ 'only' => ResourceLoaderModule::TYPE_STYLES,
+ 'output' =>
+ '<link rel="stylesheet" href="/w/load.php?debug=false&lang=nl&modules=test.styles.pure&only=styles&skin=fallback"/>' . "\n"
+ . '<style>.shouldembed{}</style>'
+ ],
+ [
+ 'context' => [],
+ 'modules' => [ 'test.ordering.a', 'test.ordering.e', 'test.ordering.b', 'test.ordering.d', 'test.ordering.c' ],
+ 'only' => ResourceLoaderModule::TYPE_STYLES,
+ 'output' =>
+ '<link rel="stylesheet" href="/w/load.php?debug=false&lang=nl&modules=test.ordering.a%2Cb&only=styles&skin=fallback"/>' . "\n"
+ . '<style>.orderingC{}.orderingD{}</style>' . "\n"
+ . '<link rel="stylesheet" href="/w/load.php?debug=false&lang=nl&modules=test.ordering.e&only=styles&skin=fallback"/>'
+ ],
// @codingStandardsIgnoreEnd
];
}