summaryrefslogtreecommitdiff
path: root/includes/resourceloader/ResourceLoader.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/resourceloader/ResourceLoader.php')
-rw-r--r--includes/resourceloader/ResourceLoader.php166
1 files changed, 110 insertions, 56 deletions
diff --git a/includes/resourceloader/ResourceLoader.php b/includes/resourceloader/ResourceLoader.php
index 9175b10d..3b48a266 100644
--- a/includes/resourceloader/ResourceLoader.php
+++ b/includes/resourceloader/ResourceLoader.php
@@ -1,5 +1,7 @@
<?php
/**
+ * Base class for resource loading system.
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -161,11 +163,11 @@ class ResourceLoader {
$wgResourceLoaderMinifierStatementsOnOwnLine,
$wgResourceLoaderMinifierMaxLineLength
);
- $result .= "\n\n/* cache key: $key */\n";
+ $result .= "\n/* cache key: $key */";
break;
case 'minify-css':
$result = CSSMin::minify( $data );
- $result .= "\n\n/* cache key: $key */\n";
+ $result .= "\n/* cache key: $key */";
break;
}
@@ -215,7 +217,7 @@ class ResourceLoader {
* Registers a module with the ResourceLoader system.
*
* @param $name Mixed: Name of module as a string or List of name/object pairs as an array
- * @param $info Module info array. For backwards compatibility with 1.17alpha,
+ * @param $info array Module info array. For backwards compatibility with 1.17alpha,
* this may also be a ResourceLoaderModule object. Optional when using
* multiple-registration calling style.
* @throws MWException: If a duplicate module registration is attempted
@@ -239,9 +241,9 @@ class ResourceLoader {
);
}
- // Check $name for illegal characters
- if ( preg_match( '/[|,!]/', $name ) ) {
- throw new MWException( "ResourceLoader module name '$name' is invalid. Names may not contain pipes (|), commas (,) or exclamation marks (!)" );
+ // Check $name for validity
+ if ( !self::isValidModuleName( $name ) ) {
+ throw new MWException( "ResourceLoader module name '$name' is invalid, see ResourceLoader::isValidModuleName()" );
}
// Attach module
@@ -354,6 +356,7 @@ class ResourceLoader {
* @return Array
*/
public function getTestModuleNames( $framework = 'all' ) {
+ /// @TODO: api siteinfo prop testmodulenames modulenames
if ( $framework == 'all' ) {
return $this->testModuleNames;
} elseif ( isset( $this->testModuleNames[$framework] ) && is_array( $this->testModuleNames[$framework] ) ) {
@@ -499,10 +502,6 @@ class ResourceLoader {
$response = $this->makeComment( $warnings ) . $response;
}
- // Remove the output buffer and output the response
- ob_end_clean();
- echo $response;
-
// Save response to file cache unless there are errors
if ( isset( $fileCache ) && !$errors && !$missing ) {
// Cache single modules...and other requests if there are enough hits
@@ -515,6 +514,10 @@ class ResourceLoader {
}
}
+ // Remove the output buffer and output the response
+ ob_end_clean();
+ echo $response;
+
wfProfileOut( __METHOD__ );
}
@@ -598,7 +601,7 @@ class ResourceLoader {
/**
* Send out code for a response from file cache if possible
*
- * @param $fileCache ObjectFileCache: Cache object for this request URL
+ * @param $fileCache ResourceFileCache: Cache object for this request URL
* @param $context ResourceLoaderContext: Context in which to generate a response
* @return bool If this found a cache file and handled the response
*/
@@ -682,6 +685,7 @@ class ResourceLoader {
}
// Generate output
+ $isRaw = false;
foreach ( $modules as $name => $module ) {
/**
* @var $module ResourceLoaderModule
@@ -699,7 +703,7 @@ class ResourceLoader {
$scripts = $module->getScriptURLsForDebug( $context );
} else {
$scripts = $module->getScript( $context );
- if ( is_string( $scripts ) ) {
+ if ( is_string( $scripts ) && strlen( $scripts ) && substr( $scripts, -1 ) !== ';' ) {
// bug 27054: Append semicolon to prevent weird bugs
// caused by files not terminating their statements right
$scripts .= ";\n";
@@ -709,12 +713,39 @@ class ResourceLoader {
// Styles
$styles = array();
if ( $context->shouldIncludeStyles() ) {
- // If we are in debug mode, we'll want to return an array of URLs
- // See comment near shouldIncludeScripts() for more details
- if ( $context->getDebug() && !$context->getOnly() && $module->supportsURLLoading() ) {
- $styles = $module->getStyleURLsForDebug( $context );
- } else {
- $styles = $module->getStyles( $context );
+ // 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 )
+ );
+ }
}
}
@@ -733,23 +764,21 @@ class ResourceLoader {
}
break;
case 'styles':
- $out .= self::makeCombinedStyles( $styles );
+ // 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.
+ // Module returns either an empty array or a numerical array with css strings.
+ $out .= isset( $styles['css'] ) ? implode( '', $styles['css'] ) : '';
break;
case 'messages':
$out .= self::makeMessageSetScript( new XmlJsCode( $messagesBlob ) );
break;
default:
- // Minify CSS before embedding in mw.loader.implement call
- // (unless in debug mode)
- if ( !$context->getDebug() ) {
- foreach ( $styles as $media => $style ) {
- if ( is_string( $style ) ) {
- $styles[$media] = $this->filter( 'minify-css', $style );
- }
- }
- }
- $out .= self::makeLoaderImplementScript( $name, $scripts, $styles,
- new XmlJsCode( $messagesBlob ) );
+ $out .= self::makeLoaderImplementScript(
+ $name,
+ $scripts,
+ $styles,
+ new XmlJsCode( $messagesBlob )
+ );
break;
}
} catch ( Exception $e ) {
@@ -760,15 +789,14 @@ class ResourceLoader {
$missing[] = $name;
unset( $modules[$name] );
}
+ $isRaw |= $module->isRaw();
wfProfileOut( __METHOD__ . '-' . $name );
}
// Update module states
- if ( $context->shouldIncludeScripts() ) {
+ if ( $context->shouldIncludeScripts() && !$context->getRaw() && !$isRaw ) {
// Set the state of modules loaded as only scripts to ready
- if ( count( $modules ) && $context->getOnly() === 'scripts'
- && !isset( $modules['startup'] ) )
- {
+ if ( count( $modules ) && $context->getOnly() === 'scripts' ) {
$out .= self::makeLoaderStateScript(
array_fill_keys( array_keys( $modules ), 'ready' ) );
}
@@ -796,9 +824,9 @@ class ResourceLoader {
* Returns JS code to call to mw.loader.implement for a module with
* given properties.
*
- * @param $name Module name
+ * @param $name string Module name
* @param $scripts Mixed: List of URLs to JavaScript files or String of JavaScript code
- * @param $styles Mixed: List of CSS strings keyed by media type, or list of lists of URLs to
+ * @param $styles Mixed: Array of CSS strings keyed by media type, or an array of lists of URLs to
* CSS files keyed by media type
* @param $messages Mixed: List of messages associated with this module. May either be an
* associative array mapping message key to value, or a JSON-encoded message blob containing
@@ -808,7 +836,7 @@ class ResourceLoader {
*/
public static function makeLoaderImplementScript( $name, $scripts, $styles, $messages ) {
if ( is_string( $scripts ) ) {
- $scripts = new XmlJsCode( "function( $ ) {{$scripts}}" );
+ $scripts = new XmlJsCode( "function () {\n{$scripts}\n}" );
} elseif ( !is_array( $scripts ) ) {
throw new MWException( 'Invalid scripts error. Array of URLs or string of code expected.' );
}
@@ -817,6 +845,11 @@ class ResourceLoader {
array(
$name,
$scripts,
+ // Force objects. mw.loader.implement requires them to be javascript objects.
+ // Although these variables are associative arrays, which become javascript
+ // objects through json_encode. In many cases they will be empty arrays, and
+ // PHP/json_encode() consider empty arrays to be numerical arrays and
+ // output javascript "[]" instead of "{}". This fixes that.
(object)$styles,
(object)$messages
) );
@@ -836,26 +869,34 @@ class ResourceLoader {
/**
* Combines an associative array mapping media type to CSS into a
- * single stylesheet with @media blocks.
+ * single stylesheet with "@media" blocks.
*
- * @param $styles Array: List of CSS strings keyed by media type
+ * @param $styles Array: Array keyed by media type containing (arrays of) CSS strings.
*
- * @return string
+ * @return Array
*/
- public static function makeCombinedStyles( array $styles ) {
- $out = '';
- foreach ( $styles as $media => $style ) {
- // Transform the media type based on request params and config
- // The way that this relies on $wgRequest to propagate request params is slightly evil
- $media = OutputPage::transformCssMedia( $media );
-
- if ( $media === null ) {
- // Skip
- } elseif ( $media === '' || $media == 'all' ) {
- // Don't output invalid or frivolous @media statements
- $out .= "$style\n";
- } else {
- $out .= "@media $media {\n" . str_replace( "\n", "\n\t", "\t" . $style ) . "\n}\n";
+ private static function makeCombinedStyles( array $stylePairs ) {
+ $out = array();
+ foreach ( $stylePairs as $media => $styles ) {
+ // ResourceLoaderFileModule::getStyle can return the styles
+ // as a string or an array of strings. This is to allow separation in
+ // the front-end.
+ $styles = (array) $styles;
+ foreach ( $styles as $style ) {
+ $style = trim( $style );
+ // Don't output an empty "@media print { }" block (bug 40498)
+ if ( $style !== '' ) {
+ // Transform the media type based on request params and config
+ // The way that this relies on $wgRequest to propagate request params is slightly evil
+ $media = OutputPage::transformCssMedia( $media );
+
+ if ( $media === '' || $media == 'all' ) {
+ $out[] = $style;
+ } else if ( is_string( $media ) ) {
+ $out[] = "@media $media {\n" . str_replace( "\n", "\n\t", "\t" . $style ) . "}";
+ }
+ // else: skip
+ }
}
}
return $out;
@@ -902,7 +943,7 @@ class ResourceLoader {
public static function makeCustomLoaderScript( $name, $version, $dependencies, $group, $source, $script ) {
$script = str_replace( "\n", "\n\t", trim( $script ) );
return Xml::encodeJsCall(
- "( function( name, version, dependencies, group, source ) {\n\t$script\n} )",
+ "( function ( name, version, dependencies, group, source ) {\n\t$script\n} )",
array( $name, $version, $dependencies, $group, $source ) );
}
@@ -975,7 +1016,7 @@ class ResourceLoader {
* @return string
*/
public static function makeLoaderConditionalScript( $script ) {
- return "if(window.mw){\n".trim( $script )."\n}";
+ return "if(window.mw){\n" . trim( $script ) . "\n}";
}
/**
@@ -1091,4 +1132,17 @@ class ResourceLoader {
ksort( $query );
return $query;
}
+
+ /**
+ * Check a module name for validity.
+ *
+ * Module names may not contain pipes (|), commas (,) or exclamation marks (!) and can be
+ * at most 255 bytes.
+ *
+ * @param $moduleName string Module name to check
+ * @return bool Whether $moduleName is a valid module name
+ */
+ public static function isValidModuleName( $moduleName ) {
+ return !preg_match( '/[|,!]/', $moduleName ) && strlen( $moduleName ) <= 255;
+ }
}