/** * @covers ResourceLoaderClientHtml::setConfig * @covers ResourceLoaderClientHtml::setExemptStates * @covers ResourceLoaderClientHtml::getHeadHtml * @covers ResourceLoaderClientHtml::getLoad * @covers ResourceLoader::makeLoaderStateScript */ public function testGetHeadHtml() { $context = self::makeContext(); $context->getResourceLoader()->register(self::makeSampleModules()); $client = new ResourceLoaderClientHtml($context); $client->setConfig(['key' => 'value']); $client->setModules(['test.top', 'test.private.top']); $client->setModuleStyles(['test.styles.pure', 'test.styles.private']); $client->setModuleScripts(['test.scripts.top']); $client->setExemptStates(['test.exempt' => 'ready']); // @codingStandardsIgnoreStart Generic.Files.LineLength $expected = '<script>document.documentElement.className = document.documentElement.className.replace( /(^|\\s)client-nojs(\\s|$)/, "$1client-js$2" );</script>' . "\n" . '<script>(window.RLQ=window.RLQ||[]).push(function(){' . 'mw.config.set({"key":"value"});' . 'mw.loader.state({"test.exempt":"ready","test.private.top":"loading","test.styles.pure":"ready","test.styles.private":"ready","test.scripts.top":"loading"});' . 'mw.loader.implement("test.private.top@{blankVer}",function($,jQuery,require,module){},{"css":[]});' . 'mw.loader.load(["test.top"]);' . 'mw.loader.load("/w/load.php?debug=false\\u0026lang=nl\\u0026modules=test.scripts.top\\u0026only=scripts\\u0026skin=fallback");' . '});</script>' . "\n" . '<link rel="stylesheet" href="/w/load.php?debug=false&lang=nl&modules=test.styles.pure&only=styles&skin=fallback"/>' . "\n" . '<style>.private{}</style>' . "\n" . '<script async="" src="/w/load.php?debug=false&lang=nl&modules=startup&only=scripts&skin=fallback"></script>'; // @codingStandardsIgnoreEnd $expected = self::expandVariables($expected); $this->assertEquals($expected, $client->getHeadHtml()); }
/** * 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; }