/** * 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; }
/** * @covers ResourceLoaderClientHtml::getBodyHtml * @covers ResourceLoaderClientHtml::getLoad */ public function testGetBodyHtml() { $context = self::makeContext(); $context->getResourceLoader()->register(self::makeSampleModules()); $client = new ResourceLoaderClientHtml($context); $client->setConfig(['key' => 'value']); $client->setModules(['test', 'test.private.bottom']); $client->setModuleScripts(['test.scripts']); // @codingStandardsIgnoreStart Generic.Files.LineLength $expected = '<script>(window.RLQ=window.RLQ||[]).push(function(){' . 'mw.loader.implement("test.private.bottom@{blankVer}",function($,jQuery,require,module){},{"css":[]});' . 'mw.loader.load("/w/load.php?debug=false\\u0026lang=nl\\u0026modules=test.scripts\\u0026only=scripts\\u0026skin=fallback");' . 'mw.loader.load(["test"]);' . '});</script>'; // @codingStandardsIgnoreEnd $expected = self::expandVariables($expected); $this->assertEquals($expected, $client->getBodyHtml()); }