resourceloader: Move packaging to a new getModuleContent() method
Centralise the building of module packages into this method so that it can be easily re-used. This is in preparation for providing the option for modules to use content-based hashing (instead of based on meta-data) in the version string of modules. Having a getModuleContent method allows that to be implemented in a way that is well-cached (as we'll be calling it from multiple multiple code paths) and without duplicating this logic. Bug: T98087 Change-Id: I376233caaabe44b6101565b70a50904abdf8ab4f
This commit is contained in:
parent
50c6cd2458
commit
5ddd7f91c7
2 changed files with 135 additions and 76 deletions
|
|
@ -298,8 +298,16 @@ class ResourceLoader implements LoggerAwareInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param MessageBlobStore $blobStore
|
* @since 1.26
|
||||||
|
* @return MessageBlobStore
|
||||||
|
*/
|
||||||
|
public function getMessageBlobStore() {
|
||||||
|
return $this->blobStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* @since 1.25
|
* @since 1.25
|
||||||
|
* @param MessageBlobStore $blobStore
|
||||||
*/
|
*/
|
||||||
public function setMessageBlobStore( MessageBlobStore $blobStore ) {
|
public function setMessageBlobStore( MessageBlobStore $blobStore ) {
|
||||||
$this->blobStore = $blobStore;
|
$this->blobStore = $blobStore;
|
||||||
|
|
@ -962,7 +970,7 @@ MESSAGE;
|
||||||
// Pre-fetch blobs
|
// Pre-fetch blobs
|
||||||
if ( $context->shouldIncludeMessages() ) {
|
if ( $context->shouldIncludeMessages() ) {
|
||||||
try {
|
try {
|
||||||
$blobs = $this->blobStore->get( $this, $modules, $context->getLanguage() );
|
$this->blobStore->get( $this, $modules, $context->getLanguage() );
|
||||||
} catch ( Exception $e ) {
|
} catch ( Exception $e ) {
|
||||||
MWExceptionHandler::logException( $e );
|
MWExceptionHandler::logException( $e );
|
||||||
$this->logger->warning( 'Prefetching MessageBlobStore failed: {exception}', array(
|
$this->logger->warning( 'Prefetching MessageBlobStore failed: {exception}', array(
|
||||||
|
|
@ -970,8 +978,6 @@ MESSAGE;
|
||||||
) );
|
) );
|
||||||
$this->errors[] = self::formatExceptionNoComment( $e );
|
$this->errors[] = self::formatExceptionNoComment( $e );
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$blobs = array();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ( $missing as $name ) {
|
foreach ( $missing as $name ) {
|
||||||
|
|
@ -981,79 +987,13 @@ MESSAGE;
|
||||||
// Generate output
|
// Generate output
|
||||||
$isRaw = false;
|
$isRaw = false;
|
||||||
foreach ( $modules as $name => $module ) {
|
foreach ( $modules as $name => $module ) {
|
||||||
/**
|
|
||||||
* @var $module ResourceLoaderModule
|
|
||||||
*/
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$scripts = '';
|
$content = $module->getModuleContent( $context );
|
||||||
if ( $context->shouldIncludeScripts() ) {
|
|
||||||
// If we are in debug mode, we'll want to return an array of URLs if possible
|
|
||||||
// However, we can't do this if the module doesn't support it
|
|
||||||
// We also can't do this if there is an only= parameter, because we have to give
|
|
||||||
// the module a way to return a load.php URL without causing an infinite loop
|
|
||||||
if ( $context->getDebug() && !$context->getOnly() && $module->supportsURLLoading() ) {
|
|
||||||
$scripts = $module->getScriptURLsForDebug( $context );
|
|
||||||
} else {
|
|
||||||
$scripts = $module->getScript( $context );
|
|
||||||
// rtrim() because there are usually a few line breaks
|
|
||||||
// after the last ';'. A new line at EOF, a new line
|
|
||||||
// added by ResourceLoaderFileModule::readScriptFiles, etc.
|
|
||||||
if ( is_string( $scripts )
|
|
||||||
&& strlen( $scripts )
|
|
||||||
&& substr( rtrim( $scripts ), -1 ) !== ';'
|
|
||||||
) {
|
|
||||||
// Append semicolon to prevent weird bugs caused by files not
|
|
||||||
// terminating their statements right (bug 27054)
|
|
||||||
$scripts .= ";\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Styles
|
|
||||||
$styles = array();
|
|
||||||
if ( $context->shouldIncludeStyles() ) {
|
|
||||||
// Don't create empty stylesheets like array( '' => '' ) for modules
|
|
||||||
// that don't *have* any stylesheets (bug 38024).
|
|
||||||
$stylePairs = $module->getStyles( $context );
|
|
||||||
if ( count( $stylePairs ) ) {
|
|
||||||
// If we are in debug mode without &only= set, we'll want to return an array of URLs
|
|
||||||
// See comment near shouldIncludeScripts() for more details
|
|
||||||
if ( $context->getDebug() && !$context->getOnly() && $module->supportsURLLoading() ) {
|
|
||||||
$styles = array(
|
|
||||||
'url' => $module->getStyleURLsForDebug( $context )
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Minify CSS before embedding in mw.loader.implement call
|
|
||||||
// (unless in debug mode)
|
|
||||||
if ( !$context->getDebug() ) {
|
|
||||||
foreach ( $stylePairs as $media => $style ) {
|
|
||||||
// Can be either a string or an array of strings.
|
|
||||||
if ( is_array( $style ) ) {
|
|
||||||
$stylePairs[$media] = array();
|
|
||||||
foreach ( $style as $cssText ) {
|
|
||||||
if ( is_string( $cssText ) ) {
|
|
||||||
$stylePairs[$media][] = $this->filter( 'minify-css', $cssText );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elseif ( is_string( $style ) ) {
|
|
||||||
$stylePairs[$media] = $this->filter( 'minify-css', $style );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Wrap styles into @media groups as needed and flatten into a numerical array
|
|
||||||
$styles = array(
|
|
||||||
'css' => self::makeCombinedStyles( $stylePairs )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Messages
|
|
||||||
$messagesBlob = isset( $blobs[$name] ) ? $blobs[$name] : '{}';
|
|
||||||
|
|
||||||
// Append output
|
// Append output
|
||||||
switch ( $context->getOnly() ) {
|
switch ( $context->getOnly() ) {
|
||||||
case 'scripts':
|
case 'scripts':
|
||||||
|
$scripts = $content['scripts'];
|
||||||
if ( is_string( $scripts ) ) {
|
if ( is_string( $scripts ) ) {
|
||||||
// Load scripts raw...
|
// Load scripts raw...
|
||||||
$out .= $scripts;
|
$out .= $scripts;
|
||||||
|
|
@ -1063,6 +1003,7 @@ MESSAGE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'styles':
|
case 'styles':
|
||||||
|
$styles = $content['styles'];
|
||||||
// We no longer seperate into media, they are all combined now with
|
// We no longer seperate into media, they are all combined now with
|
||||||
// custom media type groups into @media .. {} sections as part of the css string.
|
// custom media type groups into @media .. {} sections as part of the css string.
|
||||||
// Module returns either an empty array or a numerical array with css strings.
|
// Module returns either an empty array or a numerical array with css strings.
|
||||||
|
|
@ -1071,10 +1012,10 @@ MESSAGE;
|
||||||
default:
|
default:
|
||||||
$out .= self::makeLoaderImplementScript(
|
$out .= self::makeLoaderImplementScript(
|
||||||
$name,
|
$name,
|
||||||
$scripts,
|
isset( $content['scripts'] ) ? $content['scripts'] : '',
|
||||||
$styles,
|
isset( $content['styles'] ) ? $content['styles'] : array(),
|
||||||
new XmlJsCode( $messagesBlob ),
|
isset( $content['messagesBlob'] ) ? new XmlJsCode( $content['messagesBlob'] ) : array(),
|
||||||
$module->getTemplates()
|
isset( $content['templates'] ) ? $content['templates'] : array()
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,8 @@ abstract class ResourceLoaderModule {
|
||||||
protected $msgBlobMtime = array();
|
protected $msgBlobMtime = array();
|
||||||
// In-object cache for version hash
|
// In-object cache for version hash
|
||||||
protected $versionHash = array();
|
protected $versionHash = array();
|
||||||
|
// In-object cache for module content
|
||||||
|
protected $contents = array();
|
||||||
|
|
||||||
// Whether the position returned by getPosition() is defined in the module configuration
|
// Whether the position returned by getPosition() is defined in the module configuration
|
||||||
// and not a default value
|
// and not a default value
|
||||||
|
|
@ -439,6 +441,122 @@ abstract class ResourceLoaderModule {
|
||||||
$this->msgBlobMtime[$lang] = $mtime;
|
$this->msgBlobMtime[$lang] = $mtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of this module's resources. Ready for serving to the web.
|
||||||
|
*
|
||||||
|
* @since 1.26
|
||||||
|
* @param ResourceLoaderContext $context
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getModuleContent( ResourceLoaderContext $context ) {
|
||||||
|
$contextHash = $context->getHash();
|
||||||
|
// Cache this expensive operation. This calls builds the scripts, styles, and messages
|
||||||
|
// content which typically involves filesystem and/or database access.
|
||||||
|
if ( !array_key_exists( $contextHash, $this->contents ) ) {
|
||||||
|
$this->contents[ $contextHash ] = $this->buildContent( $context );
|
||||||
|
}
|
||||||
|
return $this->contents[ $contextHash ];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bundle all resources attached to this module into an array.
|
||||||
|
*
|
||||||
|
* @since 1.26
|
||||||
|
* @param ResourceLoaderContext $context
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
final protected function buildContent( ResourceLoaderContext $context ) {
|
||||||
|
$rl = $context->getResourceLoader();
|
||||||
|
|
||||||
|
// Only include properties that are relevant to this context (e.g. only=scripts)
|
||||||
|
// and that are non-empty (e.g. don't include "templates" for modules without
|
||||||
|
// templates). This helps prevent invalidating cache for all modules when new
|
||||||
|
// optional properties are introduced.
|
||||||
|
$content = array();
|
||||||
|
|
||||||
|
// Scripts
|
||||||
|
if ( $context->shouldIncludeScripts() ) {
|
||||||
|
// If we are in debug mode, we'll want to return an array of URLs if possible
|
||||||
|
// However, we can't do this if the module doesn't support it
|
||||||
|
// We also can't do this if there is an only= parameter, because we have to give
|
||||||
|
// the module a way to return a load.php URL without causing an infinite loop
|
||||||
|
if ( $context->getDebug() && !$context->getOnly() && $this->supportsURLLoading() ) {
|
||||||
|
$scripts = $this->getScriptURLsForDebug( $context );
|
||||||
|
} else {
|
||||||
|
$scripts = $this->getScript( $context );
|
||||||
|
// rtrim() because there are usually a few line breaks
|
||||||
|
// after the last ';'. A new line at EOF, a new line
|
||||||
|
// added by ResourceLoaderFileModule::readScriptFiles, etc.
|
||||||
|
if ( is_string( $scripts )
|
||||||
|
&& strlen( $scripts )
|
||||||
|
&& substr( rtrim( $scripts ), -1 ) !== ';'
|
||||||
|
) {
|
||||||
|
// Append semicolon to prevent weird bugs caused by files not
|
||||||
|
// terminating their statements right (bug 27054)
|
||||||
|
$scripts .= ";\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$content['scripts'] = $scripts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Styles
|
||||||
|
if ( $context->shouldIncludeStyles() ) {
|
||||||
|
$styles = array();
|
||||||
|
// Don't create empty stylesheets like array( '' => '' ) for modules
|
||||||
|
// that don't *have* any stylesheets (bug 38024).
|
||||||
|
$stylePairs = $this->getStyles( $context );
|
||||||
|
if ( count( $stylePairs ) ) {
|
||||||
|
// If we are in debug mode without &only= set, we'll want to return an array of URLs
|
||||||
|
// See comment near shouldIncludeScripts() for more details
|
||||||
|
if ( $context->getDebug() && !$context->getOnly() && $this->supportsURLLoading() ) {
|
||||||
|
$styles = array(
|
||||||
|
'url' => $this->getStyleURLsForDebug( $context )
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Minify CSS before embedding in mw.loader.implement call
|
||||||
|
// (unless in debug mode)
|
||||||
|
if ( !$context->getDebug() ) {
|
||||||
|
foreach ( $stylePairs as $media => $style ) {
|
||||||
|
// Can be either a string or an array of strings.
|
||||||
|
if ( is_array( $style ) ) {
|
||||||
|
$stylePairs[$media] = array();
|
||||||
|
foreach ( $style as $cssText ) {
|
||||||
|
if ( is_string( $cssText ) ) {
|
||||||
|
$stylePairs[$media][] = $rl->filter( 'minify-css', $cssText );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ( is_string( $style ) ) {
|
||||||
|
$stylePairs[$media] = $rl->filter( 'minify-css', $style );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Wrap styles into @media groups as needed and flatten into a numerical array
|
||||||
|
$styles = array(
|
||||||
|
'css' => $rl->makeCombinedStyles( $stylePairs )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$content['styles'] = $styles;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages
|
||||||
|
$blobs = $rl->getMessageBlobStore()->get(
|
||||||
|
$rl,
|
||||||
|
array( $this->getName() => $this ),
|
||||||
|
$context->getLanguage()
|
||||||
|
);
|
||||||
|
if ( isset( $blobs[$this->getName()] ) ) {
|
||||||
|
$content['messagesBlob'] = $blobs[$this->getName()];
|
||||||
|
}
|
||||||
|
|
||||||
|
$templates = $this->getTemplates();
|
||||||
|
if ( $templates ) {
|
||||||
|
$content['templates'] = $templates;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a string identifying the current version of this module in a given context.
|
* Get a string identifying the current version of this module in a given context.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue