public function testShouldInclude()
 {
     $ctx = new ResourceLoaderContext($this->getResourceLoader(), new FauxRequest([]));
     $this->assertTrue($ctx->shouldIncludeScripts(), 'Scripts in combined');
     $this->assertTrue($ctx->shouldIncludeStyles(), 'Styles in combined');
     $this->assertTrue($ctx->shouldIncludeMessages(), 'Messages in combined');
     $ctx = new ResourceLoaderContext($this->getResourceLoader(), new FauxRequest(['only' => 'styles']));
     $this->assertFalse($ctx->shouldIncludeScripts(), 'Scripts not in styles-only');
     $this->assertTrue($ctx->shouldIncludeStyles(), 'Styles in styles-only');
     $this->assertFalse($ctx->shouldIncludeMessages(), 'Messages not in styles-only');
     $ctx = new ResourceLoaderContext($this->getResourceLoader(), new FauxRequest(['only' => 'scripts']));
     $this->assertTrue($ctx->shouldIncludeScripts(), 'Scripts in scripts-only');
     $this->assertFalse($ctx->shouldIncludeStyles(), 'Styles not in scripts-only');
     $this->assertFalse($ctx->shouldIncludeMessages(), 'Messages not in scripts-only');
 }
Пример #2
0
 /**
  * Generates code for a response
  *
  * @param $context ResourceLoaderContext: Context in which to generate a response
  * @param $modules Array: List of module objects keyed by module name
  * @param $missing Array: List of unavailable modules (optional)
  * @return String: Response data
  */
 public function makeModuleResponse(ResourceLoaderContext $context, array $modules, $missing = array())
 {
     $out = '';
     $exceptions = '';
     if ($modules === array() && $missing === array()) {
         return '/* No modules requested. Max made me put this here */';
     }
     wfProfileIn(__METHOD__);
     // Pre-fetch blobs
     if ($context->shouldIncludeMessages()) {
         try {
             $blobs = MessageBlobStore::get($this, $modules, $context->getLanguage());
         } catch (Exception $e) {
             // Add exception to the output as a comment
             $exceptions .= $this->makeComment($e->__toString());
         }
     } else {
         $blobs = array();
     }
     // Generate output
     $isRaw = false;
     foreach ($modules as $name => $module) {
         /**
          * @var $module ResourceLoaderModule
          */
         wfProfileIn(__METHOD__ . '-' . $name);
         try {
             $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() && $module->supportsURLLoading()) {
                     $scripts = $module->getScriptURLsForDebug($context);
                 } else {
                     $scripts = $module->getScript($context);
                     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";
                     }
                 }
             }
             // 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
             switch ($context->getOnly()) {
                 case 'scripts':
                     if (is_string($scripts)) {
                         // Load scripts raw...
                         $out .= $scripts;
                     } elseif (is_array($scripts)) {
                         // ...except when $scripts is an array of URLs
                         $out .= self::makeLoaderImplementScript($name, $scripts, array(), array());
                     }
                     break;
                 case '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:
                     $out .= self::makeLoaderImplementScript($name, $scripts, $styles, new XmlJsCode($messagesBlob));
                     break;
             }
         } catch (Exception $e) {
             // Add exception to the output as a comment
             $exceptions .= $this->makeComment($e->__toString());
             // Register module as missing
             $missing[] = $name;
             unset($modules[$name]);
         }
         $isRaw |= $module->isRaw();
         wfProfileOut(__METHOD__ . '-' . $name);
     }
     // Update module states
     if ($context->shouldIncludeScripts() && !$context->getRaw() && !$isRaw) {
         // Set the state of modules loaded as only scripts to ready
         if (count($modules) && $context->getOnly() === 'scripts') {
             $out .= self::makeLoaderStateScript(array_fill_keys(array_keys($modules), 'ready'));
         }
         // Set the state of modules which were requested but unavailable as missing
         if (is_array($missing) && count($missing)) {
             $out .= self::makeLoaderStateScript(array_fill_keys($missing, 'missing'));
         }
     }
     if (!$context->getDebug()) {
         if ($context->getOnly() === 'styles') {
             $out = $this->filter('minify-css', $out);
         } else {
             $out = $this->filter('minify-js', $out);
         }
     }
     wfProfileOut(__METHOD__);
     return $exceptions . $out;
 }
Пример #3
0
 /**
  * Generates code for a response
  *
  * @param $context ResourceLoaderContext: Context in which to generate a response
  * @param $modules Array: List of module objects keyed by module name
  * @param $missing Array: List of unavailable modules (optional)
  * @return String: Response data
  */
 public function makeModuleResponse(ResourceLoaderContext $context, array $modules, $missing = array())
 {
     $out = '';
     $exceptions = '';
     if ($modules === array() && $missing === array()) {
         return '/* No modules requested. Max made me put this here */';
     }
     wfProfileIn(__METHOD__);
     // Pre-fetch blobs
     if ($context->shouldIncludeMessages()) {
         try {
             $blobs = MessageBlobStore::get($this, $modules, $context->getLanguage());
         } catch (Exception $e) {
             // Add exception to the output as a comment
             $exceptions .= $this->formatException($e);
         }
     } else {
         $blobs = array();
     }
     // Generate output
     foreach ($modules as $name => $module) {
         /**
          * @var $module ResourceLoaderModule
          */
         wfProfileIn(__METHOD__ . '-' . $name);
         try {
             $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() && $module->supportsURLLoading()) {
                     $scripts = $module->getScriptURLsForDebug($context);
                 } else {
                     $scripts = $module->getScript($context);
                     if (is_string($scripts)) {
                         // bug 27054: Append semicolon to prevent weird bugs
                         // caused by files not terminating their statements right
                         $scripts .= ";\n";
                     }
                 }
             }
             // 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);
                 }
             }
             // Messages
             $messagesBlob = isset($blobs[$name]) ? $blobs[$name] : '{}';
             // Append output
             switch ($context->getOnly()) {
                 case 'scripts':
                     if (is_string($scripts)) {
                         // Load scripts raw...
                         $out .= $scripts;
                     } elseif (is_array($scripts)) {
                         // ...except when $scripts is an array of URLs
                         $out .= self::makeLoaderImplementScript($name, $scripts, array(), array());
                     }
                     break;
                 case 'styles':
                     $out .= self::makeCombinedStyles($styles);
                     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));
                     break;
             }
         } catch (Exception $e) {
             // Add exception to the output as a comment
             $exceptions .= $this->formatException($e);
             // Register module as missing
             $missing[] = $name;
             unset($modules[$name]);
         }
         wfProfileOut(__METHOD__ . '-' . $name);
     }
     // Update module states
     if ($context->shouldIncludeScripts()) {
         // Set the state of modules loaded as only scripts to ready
         if (count($modules) && $context->getOnly() === 'scripts' && !isset($modules['startup'])) {
             $out .= self::makeLoaderStateScript(array_fill_keys(array_keys($modules), 'ready'));
         }
         // Set the state of modules which were requested but unavailable as missing
         if (is_array($missing) && count($missing)) {
             $out .= self::makeLoaderStateScript(array_fill_keys($missing, 'missing'));
         }
     }
     if (!$context->getDebug()) {
         if ($context->getOnly() === 'styles') {
             $out = $this->filter('minify-css', $out);
         } else {
             $out = $this->filter('minify-js', $out);
         }
     }
     wfProfileOut(__METHOD__);
     return $exceptions . $out;
 }
 /**
  * Bundle all resources attached to this module into an array.
  *
  * @since 1.26
  * @param ResourceLoaderContext $context
  * @return array
  */
 protected final function buildContent(ResourceLoaderContext $context)
 {
     $rl = $context->getResourceLoader();
     $stats = RequestContext::getMain()->getStats();
     $statStart = microtime(true);
     // 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;
     }
     $statTiming = microtime(true) - $statStart;
     $statName = strtr($this->getName(), '.', '_');
     $stats->timing("resourceloader_build.all", 1000 * $statTiming);
     $stats->timing("resourceloader_build.{$statName}", 1000 * $statTiming);
     return $content;
 }
Пример #5
0
 /**
  * Generate code for a response.
  *
  * @param ResourceLoaderContext $context Context in which to generate a response
  * @param array $modules List of module objects keyed by module name
  * @param array $missing List of requested module names that are unregistered (optional)
  * @return string Response data
  */
 public function makeModuleResponse(ResourceLoaderContext $context, array $modules, array $missing = array())
 {
     $out = '';
     $exceptions = '';
     $states = array();
     if (!count($modules) && !count($missing)) {
         return "/* This file is the Web entry point for MediaWiki's ResourceLoader:\n   <https://www.mediawiki.org/wiki/ResourceLoader>. In this request,\n   no modules were requested. Max made me put this here. */";
     }
     wfProfileIn(__METHOD__);
     // Pre-fetch blobs
     if ($context->shouldIncludeMessages()) {
         try {
             $blobs = MessageBlobStore::getInstance()->get($this, $modules, $context->getLanguage());
         } catch (Exception $e) {
             MWExceptionHandler::logException($e);
             wfDebugLog('resourceloader', __METHOD__ . ": pre-fetching blobs from MessageBlobStore failed: {$e}");
             $this->hasErrors = true;
             // Add exception to the output as a comment
             $exceptions .= self::formatException($e);
         }
     } else {
         $blobs = array();
     }
     foreach ($missing as $name) {
         $states[$name] = 'missing';
     }
     // Generate output
     $isRaw = false;
     foreach ($modules as $name => $module) {
         /**
          * @var $module ResourceLoaderModule
          */
         wfProfileIn(__METHOD__ . '-' . $name);
         try {
             $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() && $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
             switch ($context->getOnly()) {
                 case 'scripts':
                     if (is_string($scripts)) {
                         // Load scripts raw...
                         $out .= $scripts;
                     } elseif (is_array($scripts)) {
                         // ...except when $scripts is an array of URLs
                         $out .= self::makeLoaderImplementScript($name, $scripts, array(), array());
                     }
                     break;
                 case '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:
                     $out .= self::makeLoaderImplementScript($name, $scripts, $styles, new XmlJsCode($messagesBlob));
                     break;
             }
         } catch (Exception $e) {
             MWExceptionHandler::logException($e);
             wfDebugLog('resourceloader', __METHOD__ . ": generating module package failed: {$e}");
             $this->hasErrors = true;
             // Add exception to the output as a comment
             $exceptions .= self::formatException($e);
             // Respond to client with error-state instead of module implementation
             $states[$name] = 'error';
             unset($modules[$name]);
         }
         $isRaw |= $module->isRaw();
         wfProfileOut(__METHOD__ . '-' . $name);
     }
     // Update module states
     if ($context->shouldIncludeScripts() && !$context->getRaw() && !$isRaw) {
         if (count($modules) && $context->getOnly() === 'scripts') {
             // Set the state of modules loaded as only scripts to ready as
             // they don't have an mw.loader.implement wrapper that sets the state
             foreach ($modules as $name => $module) {
                 $states[$name] = 'ready';
             }
         }
         // Set the state of modules we didn't respond to with mw.loader.implement
         if (count($states)) {
             $out .= self::makeLoaderStateScript($states);
         }
     } else {
         if (count($states)) {
             $exceptions .= self::makeComment('Problematic modules: ' . FormatJson::encode($states, ResourceLoader::inDebugMode()));
         }
     }
     if (!$context->getDebug()) {
         if ($context->getOnly() === 'styles') {
             $out = $this->filter('minify-css', $out);
         } else {
             $out = $this->filter('minify-js', $out);
         }
     }
     wfProfileOut(__METHOD__);
     return $exceptions . $out;
 }
Пример #6
0
 /**
  * Generates code for a response
  * 
  * @param $context ResourceLoaderContext: Context in which to generate a response
  * @param $modules Array: List of module objects keyed by module name
  * @param $missing Array: List of unavailable modules (optional)
  * @return String: Response data
  */
 public function makeModuleResponse(ResourceLoaderContext $context, array $modules, $missing = array())
 {
     $out = '';
     $exceptions = '';
     if ($modules === array() && $missing === array()) {
         return '/* No modules requested. Max made me put this here */';
     }
     wfProfileIn(__METHOD__);
     // Pre-fetch blobs
     if ($context->shouldIncludeMessages()) {
         try {
             $blobs = MessageBlobStore::get($this, $modules, $context->getLanguage());
         } catch (Exception $e) {
             // Add exception to the output as a comment
             $exceptions .= "/*\n{$e->__toString()}\n*/\n";
         }
     } else {
         $blobs = array();
     }
     // Generate output
     foreach ($modules as $name => $module) {
         wfProfileIn(__METHOD__ . '-' . $name);
         try {
             // Scripts
             $scripts = '';
             if ($context->shouldIncludeScripts()) {
                 // bug 27054: Append semicolon to prevent weird bugs
                 // caused by files not terminating their statements right
                 $scripts .= $module->getScript($context) . ";\n";
             }
             // Styles
             $styles = array();
             if ($context->shouldIncludeStyles()) {
                 $styles = $module->getStyles($context);
             }
             // Messages
             $messagesBlob = isset($blobs[$name]) ? $blobs[$name] : '{}';
             // Append output
             switch ($context->getOnly()) {
                 case 'scripts':
                     $out .= $scripts;
                     break;
                 case 'styles':
                     $out .= self::makeCombinedStyles($styles);
                     break;
                 case 'messages':
                     $out .= self::makeMessageSetScript(new XmlJsCode($messagesBlob));
                     break;
                 default:
                     // Minify CSS before embedding in mediaWiki.loader.implement call
                     // (unless in debug mode)
                     if (!$context->getDebug()) {
                         foreach ($styles as $media => $style) {
                             $styles[$media] = $this->filter('minify-css', $style);
                         }
                     }
                     $out .= self::makeLoaderImplementScript($name, $scripts, $styles, new XmlJsCode($messagesBlob));
                     break;
             }
         } catch (Exception $e) {
             // Add exception to the output as a comment
             $exceptions .= "/*\n{$e->__toString()}\n*/\n";
             // Register module as missing
             $missing[] = $name;
             unset($modules[$name]);
         }
         wfProfileOut(__METHOD__ . '-' . $name);
     }
     // Update module states
     if ($context->shouldIncludeScripts()) {
         // Set the state of modules loaded as only scripts to ready
         if (count($modules) && $context->getOnly() === 'scripts' && !isset($modules['startup'])) {
             $out .= self::makeLoaderStateScript(array_fill_keys(array_keys($modules), 'ready'));
         }
         // Set the state of modules which were requested but unavailable as missing
         if (is_array($missing) && count($missing)) {
             $out .= self::makeLoaderStateScript(array_fill_keys($missing, 'missing'));
         }
     }
     if (!$context->getDebug()) {
         if ($context->getOnly() === 'styles') {
             $out = $this->filter('minify-css', $out);
         } else {
             $out = $this->filter('minify-js', $out);
         }
     }
     wfProfileOut(__METHOD__);
     return $exceptions . $out;
 }