X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fresourceloader%2FResourceLoader.php;h=855311667d58b0fca00e6743db278485f93c152d;hb=20fcae98034d264bdc1979d543c9ebf1f1b4506b;hp=c11fe5b6187b16cf663f46cd8beef69a1f11b49c;hpb=b2b983a3bd90cc081f33282fed50bb8ee37101d0;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/resourceloader/ResourceLoader.php b/includes/resourceloader/ResourceLoader.php index c11fe5b618..b2e1c49cfd 100644 --- a/includes/resourceloader/ResourceLoader.php +++ b/includes/resourceloader/ResourceLoader.php @@ -79,6 +79,15 @@ class ResourceLoader implements LoggerAwareInterface { */ protected $errors = []; + /** + * List of extra HTTP response headers provided by loaded modules. + * + * Populated by makeModuleResponse(). + * + * @var array + */ + protected $extraHeaders = []; + /** * @var MessageBlobStore */ @@ -178,7 +187,7 @@ class ResourceLoader implements LoggerAwareInterface { * @return string Filtered data, or a comment containing an error message */ public static function filter( $filter, $data, array $options = [] ) { - if ( strpos( $data, ResourceLoader::FILTER_NOMIN ) !== false ) { + if ( strpos( $data, self::FILTER_NOMIN ) !== false ) { return $data; } @@ -646,7 +655,7 @@ class ResourceLoader implements LoggerAwareInterface { * * @since 1.26 * @param ResourceLoaderContext $context - * @param string[] $modules List of known module names + * @param string[] $moduleNames List of known module names * @return string Hash */ public function getCombinedVersion( ResourceLoaderContext $context, array $moduleNames ) { @@ -719,6 +728,8 @@ class ResourceLoader implements LoggerAwareInterface { // See https://bugs.php.net/bug.php?id=36514 ob_start(); + $this->measureResponseTime( RequestContext::getMain()->getTiming() ); + // Find out which modules are missing and instantiate the others $modules = []; $missing = []; @@ -794,7 +805,7 @@ class ResourceLoader implements LoggerAwareInterface { } } - $this->sendResponseHeaders( $context, $etag, (bool)$this->errors ); + $this->sendResponseHeaders( $context, $etag, (bool)$this->errors, $this->extraHeaders ); // Remove the output buffer and output the response ob_end_clean(); @@ -819,6 +830,16 @@ class ResourceLoader implements LoggerAwareInterface { echo $response; } + protected function measureResponseTime( Timing $timing ) { + DeferredUpdates::addCallableUpdate( function () use ( $timing ) { + $measure = $timing->measure( 'responseTime', 'requestStart', 'requestShutdown' ); + if ( $measure !== false ) { + $stats = MediaWikiServices::getInstance()->getStatsdDataFactory(); + $stats->timing( 'resourceloader.responseTime', $measure['duration'] * 1000 ); + } + } ); + } + /** * Send main response headers to the client. * @@ -827,9 +848,12 @@ class ResourceLoader implements LoggerAwareInterface { * @param ResourceLoaderContext $context * @param string $etag ETag header value * @param bool $errors Whether there are errors in the response + * @param string[] $extra Array of extra HTTP response headers * @return void */ - protected function sendResponseHeaders( ResourceLoaderContext $context, $etag, $errors ) { + protected function sendResponseHeaders( + ResourceLoaderContext $context, $etag, $errors, array $extra = [] + ) { \MediaWiki\HeaderCallback::warnIfHeadersSent(); $rlMaxage = $this->config->get( 'ResourceLoaderMaxage' ); // Use a short cache expiry so that updates propagate to clients quickly, if: @@ -873,6 +897,9 @@ class ResourceLoader implements LoggerAwareInterface { $exp = min( $maxage, $smaxage ); header( 'Expires: ' . wfTimestamp( TS_RFC2822, $exp + time() ) ); } + foreach ( $extra as $header ) { + header( $header ); + } } /** @@ -1008,6 +1035,9 @@ class ResourceLoader implements LoggerAwareInterface { /** * Generate code for a response. * + * Calling this method also populates the `errors` and `headers` members, + * later used by respond(). + * * @param ResourceLoaderContext $context Context in which to generate a response * @param ResourceLoaderModule[] $modules List of module objects keyed by module name * @param string[] $missing List of requested module names that are unregistered (optional) @@ -1052,6 +1082,10 @@ MESSAGE; $implementKey = $name . '@' . $module->getVersionHash( $context ); $strContent = ''; + if ( isset( $content['headers'] ) ) { + $this->extraHeaders = array_merge( $this->extraHeaders, $content['headers'] ); + } + // Append output switch ( $context->getOnly() ) { case 'scripts': @@ -1079,7 +1113,7 @@ MESSAGE; // mw.loader.implement will use globalEval if scripts is a string. // Minify manually here, because general response minification is // not effective due it being a string literal, not a function. - if ( !ResourceLoader::inDebugMode() ) { + if ( !self::inDebugMode() ) { $scripts = self::filter( 'minify-js', $scripts ); // T107377 } } else { @@ -1100,7 +1134,12 @@ MESSAGE; $strContent = self::filter( $filter, $strContent ); } - $out .= $strContent; + if ( $context->getOnly() === 'scripts' ) { + // Use a linebreak between module scripts (T162719) + $out .= $this->ensureNewline( $strContent ); + } else { + $out .= $strContent; + } } catch ( Exception $e ) { $this->outputErrorAndLog( $e, 'Generating module package failed: {exception}' ); @@ -1128,18 +1167,32 @@ MESSAGE; if ( !$context->getDebug() ) { $stateScript = self::filter( 'minify-js', $stateScript ); } - $out .= $stateScript; + // Use a linebreak between module script and state script (T162719) + $out = $this->ensureNewline( $out ) . $stateScript; } } else { if ( count( $states ) ) { $this->errors[] = 'Problematic modules: ' . - FormatJson::encode( $states, ResourceLoader::inDebugMode() ); + FormatJson::encode( $states, self::inDebugMode() ); } } return $out; } + /** + * Ensure the string is either empty or ends in a line break + * @param string $str + * @return string + */ + private function ensureNewline( $str ) { + $end = substr( $str, -1 ); + if ( $end === false || $end === "\n" ) { + return $str; + } + return $str . "\n"; + } + /** * Get names of modules that use a certain message. * @@ -1195,7 +1248,7 @@ MESSAGE; ]; self::trimArray( $module ); - return Xml::encodeJsCall( 'mw.loader.implement', $module, ResourceLoader::inDebugMode() ); + return Xml::encodeJsCall( 'mw.loader.implement', $module, self::inDebugMode() ); } /** @@ -1209,7 +1262,7 @@ MESSAGE; return Xml::encodeJsCall( 'mw.messages.set', [ (object)$messages ], - ResourceLoader::inDebugMode() + self::inDebugMode() ); } @@ -1266,13 +1319,13 @@ MESSAGE; return Xml::encodeJsCall( 'mw.loader.state', [ $name ], - ResourceLoader::inDebugMode() + self::inDebugMode() ); } else { return Xml::encodeJsCall( 'mw.loader.state', [ $name, $state ], - ResourceLoader::inDebugMode() + self::inDebugMode() ); } } @@ -1298,7 +1351,7 @@ MESSAGE; return Xml::encodeJsCall( "( function ( name, version, dependencies, group, source ) {\n\t$script\n} )", [ $name, $version, $dependencies, $group, $source ], - ResourceLoader::inDebugMode() + self::inDebugMode() ); } @@ -1390,7 +1443,7 @@ MESSAGE; return Xml::encodeJsCall( 'mw.loader.register', [ $name ], - ResourceLoader::inDebugMode() + self::inDebugMode() ); } else { $registration = [ $name, $version, $dependencies, $group, $source, $skip ]; @@ -1398,7 +1451,7 @@ MESSAGE; return Xml::encodeJsCall( 'mw.loader.register', $registration, - ResourceLoader::inDebugMode() + self::inDebugMode() ); } } @@ -1422,13 +1475,13 @@ MESSAGE; return Xml::encodeJsCall( 'mw.loader.addSource', [ $id ], - ResourceLoader::inDebugMode() + self::inDebugMode() ); } else { return Xml::encodeJsCall( 'mw.loader.addSource', [ $id, $loadUrl ], - ResourceLoader::inDebugMode() + self::inDebugMode() ); } } @@ -1475,7 +1528,7 @@ MESSAGE; return Xml::encodeJsCall( 'mw.config.set', [ $configuration ], - ResourceLoader::inDebugMode() + self::inDebugMode() ); }