context = $context; $this->resourceLoader = $context->getResourceLoader(); $this->options = $options + [ 'target' => null, 'safemode' => null, 'nonce' => null, ]; } /** * Set mw.config variables. * * @param array $vars Array of key/value pairs */ public function setConfig( array $vars ) { foreach ( $vars as $key => $value ) { $this->config[$key] = $value; } } /** * Ensure one or more modules are loaded. * * @param array $modules Array of module names */ public function setModules( array $modules ) { $this->modules = $modules; } /** * Ensure the styles of one or more modules are loaded. * * @param array $modules Array of module names */ public function setModuleStyles( array $modules ) { $this->moduleStyles = $modules; } /** * Set state of special modules that are handled by the caller manually. * * See OutputPage::buildExemptModules() for use cases. * * @param array $states Module state keyed by module name */ public function setExemptStates( array $states ) { $this->exemptStates = $states; } /** * @return array */ private function getData() { if ( $this->data ) { // @codeCoverageIgnoreStart return $this->data; // @codeCoverageIgnoreEnd } $rl = $this->resourceLoader; $data = [ 'states' => [ // moduleName => state ], 'general' => [], 'styles' => [], // Embedding for private modules 'embed' => [ 'styles' => [], 'general' => [], ], // Deprecations for style-only modules 'styleDeprecations' => [], ]; foreach ( $this->modules as $name ) { $module = $rl->getModule( $name ); if ( !$module ) { continue; } $group = $module->getGroup(); $context = $this->getContext( $group, ResourceLoaderModule::TYPE_COMBINED ); $shouldEmbed = $module->shouldEmbedModule( $this->context ); if ( ( $group === 'user' || $shouldEmbed ) && $module->isKnownEmpty( $context ) ) { // This is a user-specific or embedded module, which means its output // can be specific to the current page or user. As such, we can optimise // the way we load it based on the current version of the module. // Avoid needless embed for empty module, preset ready state. $data['states'][$name] = 'ready'; } elseif ( $group === 'user' || $shouldEmbed ) { // - For group=user: We need to provide a pre-generated load.php // url to the client that has the 'user' and 'version' parameters // filled in. Without this, the client would wrongly use the static // version hash, per T64602. // - For shouldEmbed=true: Embed via mw.loader.implement, per T36907. $data['embed']['general'][] = $name; // Avoid duplicate request from mw.loader $data['states'][$name] = 'loading'; } else { // Load via mw.loader.load() $data['general'][] = $name; } } foreach ( $this->moduleStyles as $name ) { $module = $rl->getModule( $name ); if ( !$module ) { continue; } if ( $module->getType() !== ResourceLoaderModule::LOAD_STYLES ) { $logger = $rl->getLogger(); $logger->error( 'Unexpected general module "{module}" in styles queue.', [ 'module' => $name, ] ); continue; } // Stylesheet doesn't trigger mw.loader callback. // Set "ready" state to allow script modules to depend on this module (T87871). // And to avoid duplicate requests at run-time from mw.loader. $data['states'][$name] = 'ready'; $group = $module->getGroup(); $context = $this->getContext( $group, ResourceLoaderModule::TYPE_STYLES ); if ( $module->shouldEmbedModule( $this->context ) ) { // Avoid needless embed for private embeds we know are empty. // (Set "ready" state directly instead, which we do a few lines above.) if ( !$module->isKnownEmpty( $context ) ) { // Embed via