/** * Call this to freeze the module queue and JS config and create a formatter. * * Depending on the Skin, this may get lazy-initialised in either headElement() or * getBottomScripts(). See SkinTemplate::prepareQuickTemplate(). Calling this too early may * cause unexpected side-effects since disallowUserJs() may be called at any time to change * the module filters retroactively. Skins and extension hooks may also add modules until very * late in the request lifecycle. * * @return ResourceLoaderClientHtml */ public function getRlClient() { if (!$this->rlClient) { $context = $this->getRlClientContext(); $rl = $this->getResourceLoader(); $this->addModules(['user.options', 'user.tokens']); $this->addModuleStyles(['site.styles', 'noscript', 'user.styles', 'user.cssprefs']); $this->getSkin()->setupSkinUserCss($this); // Prepare exempt modules for buildExemptModules() $exemptGroups = ['site' => [], 'noscript' => [], 'private' => [], 'user' => []]; $exemptStates = []; $moduleStyles = $this->getModuleStyles(true); // Preload getTitleInfo for isKnownEmpty calls below and in ResourceLoaderClientHtml // Separate user-specific batch for improved cache-hit ratio. $userBatch = ['user.styles', 'user']; $siteBatch = array_diff($moduleStyles, $userBatch); $dbr = wfGetDB(DB_REPLICA); ResourceLoaderWikiModule::preloadTitleInfo($context, $dbr, $siteBatch); ResourceLoaderWikiModule::preloadTitleInfo($context, $dbr, $userBatch); // Filter out modules handled by buildExemptModules() $moduleStyles = array_filter($moduleStyles, function ($name) use($rl, $context, &$exemptGroups, &$exemptStates) { $module = $rl->getModule($name); if ($module) { if ($name === 'user.styles' && $this->isUserCssPreview()) { $exemptStates[$name] = 'ready'; // Special case in buildExemptModules() return false; } $group = $module->getGroup(); if (isset($exemptGroups[$group])) { $exemptStates[$name] = 'ready'; if (!$module->isKnownEmpty($context)) { // E.g. Don't output empty <styles> $exemptGroups[$group][] = $name; } return false; } } return true; }); $this->rlExemptStyleModules = $exemptGroups; $isUserModuleFiltered = !$this->filterModules(['user']); // If this page filters out 'user', makeResourceLoaderLink will drop it. // Avoid indefinite "loading" state or untrue "ready" state (T145368). if (!$isUserModuleFiltered) { // Manually handled by getBottomScripts() $userModule = $rl->getModule('user'); $userState = $userModule->isKnownEmpty($context) && !$this->isUserJsPreview() ? 'ready' : 'loading'; $this->rlUserModuleState = $exemptStates['user'] = $userState; } $rlClient = new ResourceLoaderClientHtml($context, $this->getTarget()); $rlClient->setConfig($this->getJSVars()); $rlClient->setModules($this->getModules(true)); $rlClient->setModuleStyles($moduleStyles); $rlClient->setModuleScripts($this->getModuleScripts(true)); $rlClient->setExemptStates($exemptStates); $this->rlClient = $rlClient; } return $this->rlClient; }
/** * Load information stored in the database about modules. * * This method grabs modules dependencies from the database and updates modules * objects. * * This is not inside the module code because it is much faster to * request all of the information at once than it is to have each module * requests its own information. This sacrifice of modularity yields a substantial * performance improvement. * * @param array $moduleNames List of module names to preload information for * @param ResourceLoaderContext $context Context to load the information within */ public function preloadModuleInfo(array $moduleNames, ResourceLoaderContext $context) { if (!$moduleNames) { // Or else Database*::select() will explode, plus it's cheaper! return; } $dbr = wfGetDB(DB_REPLICA); $skin = $context->getSkin(); $lang = $context->getLanguage(); // Batched version of ResourceLoaderModule::getFileDependencies $vary = "{$skin}|{$lang}"; $res = $dbr->select('module_deps', ['md_module', 'md_deps'], ['md_module' => $moduleNames, 'md_skin' => $vary], __METHOD__); // Prime in-object cache for file dependencies $modulesWithDeps = []; foreach ($res as $row) { $module = $this->getModule($row->md_module); if ($module) { $module->setFileDependencies($context, ResourceLoaderModule::expandRelativePaths(FormatJson::decode($row->md_deps, true))); $modulesWithDeps[] = $row->md_module; } } // Register the absence of a dependency row too foreach (array_diff($moduleNames, $modulesWithDeps) as $name) { $module = $this->getModule($name); if ($module) { $this->getModule($name)->setFileDependencies($context, []); } } // Batched version of ResourceLoaderWikiModule::getTitleInfo ResourceLoaderWikiModule::preloadTitleInfo($context, $dbr, $moduleNames); // Prime in-object cache for message blobs for modules with messages $modules = []; foreach ($moduleNames as $name) { $module = $this->getModule($name); if ($module && $module->getMessages()) { $modules[$name] = $module; } } $store = $this->getMessageBlobStore(); $blobs = $store->getBlobs($modules, $lang); foreach ($blobs as $name => $blob) { $modules[$name]->setMessageBlob($blob, $lang); } }