wiki.techinc.nl/includes/resourceloader/ResourceLoaderClientHtml.php

495 lines
15 KiB
PHP
Raw Normal View History

resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
use Wikimedia\WrappedString;
use Wikimedia\WrappedStringList;
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
/**
* Load and configure a ResourceLoader client on an HTML page.
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
*
* @ingroup ResourceLoader
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
* @since 1.28
*/
class ResourceLoaderClientHtml {
/** @var ResourceLoaderContext */
private $context;
/** @var ResourceLoader */
private $resourceLoader;
/** @var array<string,string|null|false> */
private $options;
/** @var array<string,mixed> */
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
private $config = [];
/** @var string[] */
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
private $modules = [];
/** @var string[] */
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
private $moduleStyles = [];
/** @var array<string,string> */
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
private $exemptStates = [];
/** @var array */
private $data;
/**
* @param ResourceLoaderContext $context
* @param array $options [optional] Array of options
* - 'target': Parameter for modules=startup request, see ResourceLoaderStartUpModule.
* - 'safemode': Parameter for modules=startup request, see ResourceLoaderStartUpModule.
* - 'nonce': From OutputPage->getCSP->getNonce().
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
*/
public function __construct( ResourceLoaderContext $context, array $options = [] ) {
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
$this->context = $context;
$this->resourceLoader = $context->getResourceLoader();
$this->options = $options + [
'target' => null,
'safemode' => null,
'nonce' => null,
];
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
/**
* 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 string[] $modules Array of module names
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
*/
public function setModules( array $modules ) {
$this->modules = $modules;
}
/**
* Ensure the styles of one or more modules are loaded.
*
* @param string[] $modules Array of module names
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
*/
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<string,string> $states Module state keyed by module name
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
*/
public function setExemptStates( array $states ) {
$this->exemptStates = $states;
}
private function getData() : array {
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
if ( $this->data ) {
// @codeCoverageIgnoreStart
return $this->data;
// @codeCoverageIgnoreEnd
}
$rl = $this->resourceLoader;
$data = [
'states' => [
// moduleName => state
],
'general' => [],
'styles' => [],
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
// Embedding for private modules
'embed' => [
'styles' => [],
'general' => [],
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
],
// Deprecations for style-only modules
'styleDeprecations' => [],
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
];
foreach ( $this->modules as $name ) {
$module = $rl->getModule( $name );
if ( !$module ) {
continue;
}
resourceloader: Support loading group=user modules with addModules() When a module has group=user specified, it means that its module contents can vary by user. These kinds of requests have two special needs: 1) They need an additional "user" parameter in their load.php request, so that the response knows which user-context to use. 2) They need to have their 'version' hash pre-computed based on which assets will be loaded for this user. The general 'version' hash associated with this module name in the main registry (modules=startup) will be "wrong" as that is computed based on logged-out status. We do this by omitting the module name from the `mw.load.load(Array modules)` call in the HTML, and instead output a request for the full url. This currently works fine for most cases, such as the 'user' module loaded by MediaWiki core. The branch in getData() dealing with legacy 'only=scripts' behaviour also covers this case. But the case of an extension registering a group=user module and loading it the general way (e.g. not with legacy only=scripts behaviour), would currently end up in the Array-queue and dynamically loaded by the client-side without knowing the correct version hash. Fortunately, no code exists that I know of that meets these three critera (extension registered, group=user, non-legacy). However, for the GlobalCssJs extension to migrate from legacy to non-legacy, they will need to start doing this. This commit makes sure that that will work. The makeLoad() method in ClientHtml has code ensuring the full-url form (with pre-computed 'version' hash) is used for any modules with group=user. Before this patch, we didn't get to call makeLoad() because getData() was assuming that we only need makeLoad() when either the module should be embedded (group=private), or when it is a style/scripts-only module. It didn't consider group=user. Bug: T188689 Change-Id: Iaab15e5f5c12e7e28b8c81beab90948cd07cd352
2018-03-06 00:52:28 +00:00
$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 ) {
resourceloader: Support loading group=user modules with addModules() When a module has group=user specified, it means that its module contents can vary by user. These kinds of requests have two special needs: 1) They need an additional "user" parameter in their load.php request, so that the response knows which user-context to use. 2) They need to have their 'version' hash pre-computed based on which assets will be loaded for this user. The general 'version' hash associated with this module name in the main registry (modules=startup) will be "wrong" as that is computed based on logged-out status. We do this by omitting the module name from the `mw.load.load(Array modules)` call in the HTML, and instead output a request for the full url. This currently works fine for most cases, such as the 'user' module loaded by MediaWiki core. The branch in getData() dealing with legacy 'only=scripts' behaviour also covers this case. But the case of an extension registering a group=user module and loading it the general way (e.g. not with legacy only=scripts behaviour), would currently end up in the Array-queue and dynamically loaded by the client-side without knowing the correct version hash. Fortunately, no code exists that I know of that meets these three critera (extension registered, group=user, non-legacy). However, for the GlobalCssJs extension to migrate from legacy to non-legacy, they will need to start doing this. This commit makes sure that that will work. The makeLoad() method in ClientHtml has code ensuring the full-url form (with pre-computed 'version' hash) is used for any modules with group=user. Before this patch, we didn't get to call makeLoad() because getData() was assuming that we only need makeLoad() when either the module should be embedded (group=private), or when it is a style/scripts-only module. It didn't consider group=user. Bug: T188689 Change-Id: Iaab15e5f5c12e7e28b8c81beab90948cd07cd352
2018-03-06 00:52:28 +00:00
// - 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;
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
// Avoid duplicate request from mw.loader
$data['states'][$name] = 'loading';
} else {
// Load via mw.loader.load()
$data['general'][] = $name;
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
}
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.', [
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
'module' => $name,
] );
continue;
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
// 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';
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
$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 <style> element
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
$data['embed']['styles'][] = $name;
}
// For other style modules, always request them, regardless of whether they are
// currently known to be empty. Because:
// 1. Those modules are requested in batch, so there is no extra request overhead
// or extra HTML element to be avoided.
// 2. Checking isKnownEmpty for those can be expensive and slow down page view
// generation (T230260).
// 3. We don't want cached HTML to vary on the current state of a module.
// If the module becomes non-empty a few minutes later, it should start working
// on cached HTML without requiring a purge.
//
// But, user-specific modules:
// * ... are used on page views not publicly cached.
// * ... are in their own group and thus a require a request we can avoid
// * ... have known-empty status preloaded by ResourceLoader.
} elseif ( $group !== 'user' || !$module->isKnownEmpty( $context ) ) {
// Load from load.php?only=styles via <link rel=stylesheet>
$data['styles'][] = $name;
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
$deprecation = $module->getDeprecationInformation( $context );
if ( $deprecation ) {
$data['styleDeprecations'][] = $deprecation;
}
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
return $data;
}
/**
* @return array<string,string> Attributes pairs for the HTML document element
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
*/
public function getDocumentAttributes() {
return [ 'class' => 'client-nojs' ];
}
/**
* The order of elements in the head is as follows:
* - Inline scripts.
* - Stylesheets.
* - Async external script-src.
*
* Reasons:
* - Script execution may be blocked on preceding stylesheets.
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
* - Async scripts are not blocked on stylesheets.
* - Inline scripts can't be asynchronous.
* - For styles, earlier is better.
*
* @param string|null $nojsClass Class name that caller uses on HTML document element
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
* @return string|WrappedStringList HTML
*/
public function getHeadHtml( $nojsClass = null ) {
$nonce = $this->options['nonce'];
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
$data = $this->getData();
$chunks = [];
// Change "client-nojs" class to client-js. This allows easy toggling of UI components.
// This must happen synchronously on every page view to avoid flashes of wrong content.
// See also startup/startup.js.
$nojsClass = $nojsClass ?? $this->getDocumentAttributes()['class'];
$jsClass = preg_replace( '/(^|\s)client-nojs(\s|$)/', '$1client-js$2', $nojsClass );
$jsClassJson = $this->context->encodeJson( $jsClass );
$script = <<<JAVASCRIPT
document.documentElement.className = {$jsClassJson};
JAVASCRIPT;
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
// Inline script: Declare mw.config variables for this page.
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
if ( $this->config ) {
$confJson = $this->context->encodeJson( $this->config );
$script .= <<<JAVASCRIPT
RLCONF = {$confJson};
JAVASCRIPT;
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
// Inline script: Declare initial module states for this page.
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
$states = array_merge( $this->exemptStates, $data['states'] );
if ( $states ) {
$stateJson = $this->context->encodeJson( $states );
$script .= <<<JAVASCRIPT
RLSTATE = {$stateJson};
JAVASCRIPT;
}
// Inline script: Declare general modules to load on this page.
if ( $data['general'] ) {
$pageModulesJson = $this->context->encodeJson( $data['general'] );
$script .= <<<JAVASCRIPT
RLPAGEMODULES = {$pageModulesJson};
JAVASCRIPT;
}
if ( !$this->context->getDebug() ) {
$script = ResourceLoader::filter( 'minify-js', $script, [ 'cache' => false ] );
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
$chunks[] = Html::inlineScript( $script, $nonce );
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
// Inline RLQ: Embedded modules
if ( $data['embed']['general'] ) {
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
$chunks[] = $this->getLoad(
$data['embed']['general'],
Initial support for Content Security Policy, disabled by default The primary goal here is a defense in depth measure to stop an attacker who found a bug in the parser allowing them to insert malicious attributes. This wouldn't stop someone who could insert a full script tag (since at current it can't distinguish between malicious and legit user js). It also would not prevent DOM-based or reflected XSS for anons, as the nonce value is guessable for anons when receiving a response cached by varnish. However, the limited protection of just stopping stored XSS where the attacker only has control of attributes, is still a big win in my opinion. (But it wouldn't prevent someone who has that type of xss from abusing things like data-ooui attribute). This will likely break many gadgets. Its expected that any sort of rollout on Wikimedia will be done very slowly, with lots of testing and the report-only option to begin with. This is behind feature flags that are off by default, so merging this patch should not cause any change in default behaviour. This may break some extensions (The most obvious one is charinsert (See fe648d41005), but will probably need some testing in report-only mode to see if anything else breaks) This uses the unsafe-eval option of CSP, in order to support RL's local storage thingy. For better security, we may want to remove some of the sillier uses of eval (e.g. jquery.ui.datepicker.js). For more info, see spec: https://www.w3.org/TR/CSP2/ Additionally see: https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy Bug: T135963 Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
ResourceLoaderModule::TYPE_COMBINED,
$nonce
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
);
}
// External stylesheets (only=styles)
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
if ( $data['styles'] ) {
$chunks[] = $this->getLoad(
$data['styles'],
Initial support for Content Security Policy, disabled by default The primary goal here is a defense in depth measure to stop an attacker who found a bug in the parser allowing them to insert malicious attributes. This wouldn't stop someone who could insert a full script tag (since at current it can't distinguish between malicious and legit user js). It also would not prevent DOM-based or reflected XSS for anons, as the nonce value is guessable for anons when receiving a response cached by varnish. However, the limited protection of just stopping stored XSS where the attacker only has control of attributes, is still a big win in my opinion. (But it wouldn't prevent someone who has that type of xss from abusing things like data-ooui attribute). This will likely break many gadgets. Its expected that any sort of rollout on Wikimedia will be done very slowly, with lots of testing and the report-only option to begin with. This is behind feature flags that are off by default, so merging this patch should not cause any change in default behaviour. This may break some extensions (The most obvious one is charinsert (See fe648d41005), but will probably need some testing in report-only mode to see if anything else breaks) This uses the unsafe-eval option of CSP, in order to support RL's local storage thingy. For better security, we may want to remove some of the sillier uses of eval (e.g. jquery.ui.datepicker.js). For more info, see spec: https://www.w3.org/TR/CSP2/ Additionally see: https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy Bug: T135963 Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
ResourceLoaderModule::TYPE_STYLES,
$nonce
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
);
}
// Inline stylesheets (embedded only=styles)
if ( $data['embed']['styles'] ) {
$chunks[] = $this->getLoad(
$data['embed']['styles'],
Initial support for Content Security Policy, disabled by default The primary goal here is a defense in depth measure to stop an attacker who found a bug in the parser allowing them to insert malicious attributes. This wouldn't stop someone who could insert a full script tag (since at current it can't distinguish between malicious and legit user js). It also would not prevent DOM-based or reflected XSS for anons, as the nonce value is guessable for anons when receiving a response cached by varnish. However, the limited protection of just stopping stored XSS where the attacker only has control of attributes, is still a big win in my opinion. (But it wouldn't prevent someone who has that type of xss from abusing things like data-ooui attribute). This will likely break many gadgets. Its expected that any sort of rollout on Wikimedia will be done very slowly, with lots of testing and the report-only option to begin with. This is behind feature flags that are off by default, so merging this patch should not cause any change in default behaviour. This may break some extensions (The most obvious one is charinsert (See fe648d41005), but will probably need some testing in report-only mode to see if anything else breaks) This uses the unsafe-eval option of CSP, in order to support RL's local storage thingy. For better security, we may want to remove some of the sillier uses of eval (e.g. jquery.ui.datepicker.js). For more info, see spec: https://www.w3.org/TR/CSP2/ Additionally see: https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy Bug: T135963 Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
ResourceLoaderModule::TYPE_STYLES,
$nonce
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
);
}
// Async scripts. Once the startup is loaded, inline RLQ scripts will run.
// Pass-through a custom 'target' from OutputPage (T143066).
$startupQuery = [ 'raw' => '1' ];
foreach ( [ 'target', 'safemode' ] as $param ) {
if ( $this->options[$param] !== null ) {
$startupQuery[$param] = (string)$this->options[$param];
}
}
$chunks[] = $this->getLoad(
'startup',
ResourceLoaderModule::TYPE_SCRIPTS,
Initial support for Content Security Policy, disabled by default The primary goal here is a defense in depth measure to stop an attacker who found a bug in the parser allowing them to insert malicious attributes. This wouldn't stop someone who could insert a full script tag (since at current it can't distinguish between malicious and legit user js). It also would not prevent DOM-based or reflected XSS for anons, as the nonce value is guessable for anons when receiving a response cached by varnish. However, the limited protection of just stopping stored XSS where the attacker only has control of attributes, is still a big win in my opinion. (But it wouldn't prevent someone who has that type of xss from abusing things like data-ooui attribute). This will likely break many gadgets. Its expected that any sort of rollout on Wikimedia will be done very slowly, with lots of testing and the report-only option to begin with. This is behind feature flags that are off by default, so merging this patch should not cause any change in default behaviour. This may break some extensions (The most obvious one is charinsert (See fe648d41005), but will probably need some testing in report-only mode to see if anything else breaks) This uses the unsafe-eval option of CSP, in order to support RL's local storage thingy. For better security, we may want to remove some of the sillier uses of eval (e.g. jquery.ui.datepicker.js). For more info, see spec: https://www.w3.org/TR/CSP2/ Additionally see: https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy Bug: T135963 Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
$nonce,
$startupQuery
);
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
return WrappedString::join( "\n", $chunks );
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
/**
* @return string|WrappedStringList HTML
*/
public function getBodyHtml() {
$data = $this->getData();
$chunks = [];
// Deprecations for only=styles modules
if ( $data['styleDeprecations'] ) {
$chunks[] = ResourceLoader::makeInlineScript(
implode( '', $data['styleDeprecations'] ),
$this->options['nonce']
);
}
return WrappedString::join( "\n", $chunks );
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
private function getContext( $group, $type ) {
return self::makeContext( $this->context, $group, $type );
}
Initial support for Content Security Policy, disabled by default The primary goal here is a defense in depth measure to stop an attacker who found a bug in the parser allowing them to insert malicious attributes. This wouldn't stop someone who could insert a full script tag (since at current it can't distinguish between malicious and legit user js). It also would not prevent DOM-based or reflected XSS for anons, as the nonce value is guessable for anons when receiving a response cached by varnish. However, the limited protection of just stopping stored XSS where the attacker only has control of attributes, is still a big win in my opinion. (But it wouldn't prevent someone who has that type of xss from abusing things like data-ooui attribute). This will likely break many gadgets. Its expected that any sort of rollout on Wikimedia will be done very slowly, with lots of testing and the report-only option to begin with. This is behind feature flags that are off by default, so merging this patch should not cause any change in default behaviour. This may break some extensions (The most obvious one is charinsert (See fe648d41005), but will probably need some testing in report-only mode to see if anything else breaks) This uses the unsafe-eval option of CSP, in order to support RL's local storage thingy. For better security, we may want to remove some of the sillier uses of eval (e.g. jquery.ui.datepicker.js). For more info, see spec: https://www.w3.org/TR/CSP2/ Additionally see: https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy Bug: T135963 Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
private function getLoad( $modules, $only, $nonce, array $extraQuery = [] ) {
return self::makeLoad( $this->context, (array)$modules, $only, $extraQuery, $nonce );
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
private static function makeContext( ResourceLoaderContext $mainContext, $group, $type,
array $extraQuery = []
) {
// Allow caller to setVersion() and setModules()
$ret = new DerivativeResourceLoaderContext( $mainContext );
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
// Set 'only' if not combined
$ret->setOnly( $type === ResourceLoaderModule::TYPE_COMBINED ? null : $type );
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
// Remove user parameter in most cases
if ( $group !== 'user' && $group !== 'private' ) {
$ret->setUser( null );
}
if ( isset( $extraQuery['raw'] ) ) {
$ret->setRaw( true );
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
return $ret;
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
/**
* Explicitly load or embed modules on a page.
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
*
* @param ResourceLoaderContext $mainContext
* @param array $modules One or more module names
* @param string $only ResourceLoaderModule TYPE_ class constant
* @param array $extraQuery [optional] Array with extra query parameters for the request
* @param string|null $nonce [optional] Content-Security-Policy nonce
* (from OutputPage->getCSP->getNonce())
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
* @return string|WrappedStringList HTML
*/
public static function makeLoad( ResourceLoaderContext $mainContext, array $modules, $only,
array $extraQuery = [], $nonce = null
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
) {
$rl = $mainContext->getResourceLoader();
$chunks = [];
// Sort module names so requests are more uniform
sort( $modules );
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
if ( $mainContext->getDebug() && count( $modules ) > 1 ) {
$chunks = [];
// Recursively call us for every item
foreach ( $modules as $name ) {
Initial support for Content Security Policy, disabled by default The primary goal here is a defense in depth measure to stop an attacker who found a bug in the parser allowing them to insert malicious attributes. This wouldn't stop someone who could insert a full script tag (since at current it can't distinguish between malicious and legit user js). It also would not prevent DOM-based or reflected XSS for anons, as the nonce value is guessable for anons when receiving a response cached by varnish. However, the limited protection of just stopping stored XSS where the attacker only has control of attributes, is still a big win in my opinion. (But it wouldn't prevent someone who has that type of xss from abusing things like data-ooui attribute). This will likely break many gadgets. Its expected that any sort of rollout on Wikimedia will be done very slowly, with lots of testing and the report-only option to begin with. This is behind feature flags that are off by default, so merging this patch should not cause any change in default behaviour. This may break some extensions (The most obvious one is charinsert (See fe648d41005), but will probably need some testing in report-only mode to see if anything else breaks) This uses the unsafe-eval option of CSP, in order to support RL's local storage thingy. For better security, we may want to remove some of the sillier uses of eval (e.g. jquery.ui.datepicker.js). For more info, see spec: https://www.w3.org/TR/CSP2/ Additionally see: https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy Bug: T135963 Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
$chunks[] = self::makeLoad( $mainContext, [ $name ], $only, $extraQuery, $nonce );
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
return new WrappedStringList( "\n", $chunks );
}
// Create keyed-by-source and then keyed-by-group list of module objects from modules list
$sortedModules = [];
foreach ( $modules as $name ) {
$module = $rl->getModule( $name );
if ( !$module ) {
$rl->getLogger()->warning( 'Unknown module "{module}"', [ 'module' => $name ] );
continue;
}
$sortedModules[$module->getSource()][$module->getGroup()][$name] = $module;
}
foreach ( $sortedModules as $source => $groups ) {
foreach ( $groups as $group => $grpModules ) {
$context = self::makeContext( $mainContext, $group, $only, $extraQuery );
// Separate sets of linked and embedded modules while preserving order
$moduleSets = [];
$idx = -1;
foreach ( $grpModules as $name => $module ) {
$shouldEmbed = $module->shouldEmbedModule( $context );
if ( !$moduleSets || $moduleSets[$idx][0] !== $shouldEmbed ) {
$moduleSets[++$idx] = [ $shouldEmbed, [] ];
}
$moduleSets[$idx][1][$name] = $module;
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
// Link/embed each set
foreach ( $moduleSets as list( $embed, $moduleSet ) ) {
$moduleSetNames = array_keys( $moduleSet );
$context->setModules( $moduleSetNames );
if ( $embed ) {
// Decide whether to use style or script element
if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
$chunks[] = Html::inlineStyle(
$rl->makeModuleResponse( $context, $moduleSet )
);
} else {
$chunks[] = ResourceLoader::makeInlineScript(
Initial support for Content Security Policy, disabled by default The primary goal here is a defense in depth measure to stop an attacker who found a bug in the parser allowing them to insert malicious attributes. This wouldn't stop someone who could insert a full script tag (since at current it can't distinguish between malicious and legit user js). It also would not prevent DOM-based or reflected XSS for anons, as the nonce value is guessable for anons when receiving a response cached by varnish. However, the limited protection of just stopping stored XSS where the attacker only has control of attributes, is still a big win in my opinion. (But it wouldn't prevent someone who has that type of xss from abusing things like data-ooui attribute). This will likely break many gadgets. Its expected that any sort of rollout on Wikimedia will be done very slowly, with lots of testing and the report-only option to begin with. This is behind feature flags that are off by default, so merging this patch should not cause any change in default behaviour. This may break some extensions (The most obvious one is charinsert (See fe648d41005), but will probably need some testing in report-only mode to see if anything else breaks) This uses the unsafe-eval option of CSP, in order to support RL's local storage thingy. For better security, we may want to remove some of the sillier uses of eval (e.g. jquery.ui.datepicker.js). For more info, see spec: https://www.w3.org/TR/CSP2/ Additionally see: https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy Bug: T135963 Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
$rl->makeModuleResponse( $context, $moduleSet ),
$nonce
);
}
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
} else {
// Special handling for the user group; because users might change their stuff
// on-wiki like user pages, or user preferences; we need to find the highest
// timestamp of these user-changeable modules so we can ensure cache misses on change
// This should NOT be done for the site group (T29564) because anons get that too
// and we shouldn't be putting timestamps in CDN-cached HTML
if ( $group === 'user' ) {
$context->setVersion( $rl->makeVersionQuery( $context, $moduleSetNames ) );
}
// Must setModules() before createLoaderURL()
$url = $rl->createLoaderURL( $source, $context, $extraQuery );
// Decide whether to use 'style' or 'script' element
if ( $only === ResourceLoaderModule::TYPE_STYLES ) {
$chunk = Html::linkedStyle( $url );
resourceloader: Remove support for raw modules Being a raw module means that when it is requested from load.php with "only=scripts" set, then the output is *not* wrapped in an 'mw.loader.implement' closure *and* there no 'mw.loader.state()' appendix. Instead, it is served "raw". Before 2018, the modules 'mediawiki' and 'jquery' were raw modules. They were needed before the client could define 'mw.loader.implement', and could never be valid dependencies. Module 'mediawiki' merged to 'startup', and 'jquery' became a regular module (T192623). Based on the architecture of modules being deliverable bundles, it doesn't make sense for there to ever be raw modules again. Anything that 'startup' needs should be bundled with it. Anything else is a regular module. On top of that, we never actually needed this feature because specifying the 'only=scripts' and 'raw=1' parameters does the same thing. The only special bit about marking modules (not requests) as "raw" was that it allowed the client to forget to specify "raw=1" and the server would automatically omit the 'mw.loader.state()' appendix based on whether the module is marked as raw. As of Ie4564ec8e26ad53f2, the two remaining use cases for raw responses now specify the 'raw=1' request parameter, and we can get rid of the "raw module" feature and all the complexity around it. == Startup module In the startup module there was an interesting use of isRaw() that has little to do with the above. The "ATTENTION" warning there applies to the startup module only, not raw modules in general. This is now fixed by explicitly checking for StartupModule. Above that warning, it talked about saving bytes, which was an optimisation given that "raw" modules don't communicate with mw.loader, they also don't need to be registered there because even if mw.loader would try to load them, the server would never inform mw.loader about the module having arrived. There are now no longer any such modules. Bug: T201483 Change-Id: I8839036e7b2b76919b6cd3aa42ccfde4d1247899
2019-06-13 18:41:56 +00:00
} elseif ( $context->getRaw() ) {
// This request is asking for the module to be delivered standalone,
// (aka "raw") without communicating to any mw.loader client.
// Use cases:
// - startup (naturally because this is what will define mw.loader)
// - html5shiv (loads synchronously in old IE before the async startup module arrives)
$chunk = Html::element( 'script', [
'async' => true,
'src' => $url
] );
} else {
$chunk = ResourceLoader::makeInlineScript(
'mw.loader.load(' . $mainContext->encodeJson( $url ) . ');',
$nonce
);
}
if ( $group == 'noscript' ) {
$chunks[] = Html::rawElement( 'noscript', [], $chunk );
} else {
$chunks[] = $chunk;
}
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
}
}
}
}
return new WrappedStringList( "\n", $chunks );
}
}