use MediaWiki\Logger\LoggerFactory;
use MediaWiki\Session\SessionManager;
use WrappedString\WrappedString;
+use WrappedString\WrappedStringList;
/**
* This class should be covered by a general architecture document which does
protected $mStatusCode;
/**
- * @var string Variable mLastModified and mEtag are used for sending cache control.
+ * @var string Used for sending cache control.
* The whole caching system should probably be moved into its own class.
*/
protected $mLastModified = '';
- /**
- * Contains an HTTP Entity Tags (see RFC 2616 section 3.13) which is used
- * as a unique identifier for the content. It is later used by the client
- * to compare its cached version with the server version. Client sends
- * headers If-Match and If-None-Match containing its locally cached ETAG value.
- *
- * To get more information, you will have to look at HTTP/1.1 protocol which
- * is properly described in RFC 2616 : http://tools.ietf.org/html/rfc2616
- */
- private $mETag = false;
-
/** @var array */
protected $mCategoryLinks = [];
*/
protected $styles = [];
- /**
- * Whether jQuery is already handled.
- */
- protected $mJQueryDone = false;
-
private $mIndexPolicy = 'index';
private $mFollowPolicy = 'follow';
private $mVaryHeader = [
*/
private $copyrightUrl;
+ /** @var array Profiling data */
+ private $limitReportData = [];
+
/**
* Constructor for OutputPage. This should not be called directly.
* Instead a new RequestContext should be created and it will implicitly create
$this->mModuleStyles = array_merge( $this->mModuleStyles, (array)$modules );
}
- /**
- * Get the list of module messages to include on this page
- *
- * @deprecated since 1.26 Obsolete
- * @param bool $filter
- * @param string|null $position
- * @return array Array of module names
- */
- public function getModuleMessages( $filter = false, $position = null ) {
- wfDeprecated( __METHOD__, '1.26' );
- return [];
- }
-
- /**
- * Load messages of one or more ResourceLoader modules.
- *
- * @deprecated since 1.26 Use addModules() instead
- * @param string|array $modules Module name (string) or array of module names
- */
- public function addModuleMessages( $modules ) {
- wfDeprecated( __METHOD__, '1.26' );
- }
-
/**
* @return null|string ResourceLoader target
*/
}
/**
- * Set the value of the ETag HTTP header, only used if $wgUseETag is true
- *
- * @param string $tag Value of "ETag" header
+ * @deprecated since 1.28 Obsolete - wgUseETag experiment was removed.
+ * @param string $tag
*/
- function setETag( $tag ) {
- $this->mETag = $tag;
+ public function setETag( $tag ) {
}
/**
# Fetch existence plus the hiddencat property
$dbr = wfGetDB( DB_SLAVE );
- $fields = [ 'page_id', 'page_namespace', 'page_title', 'page_len',
- 'page_is_redirect', 'page_latest', 'pp_value' ];
-
- if ( $this->getConfig()->get( 'ContentHandlerUseDB' ) ) {
- $fields[] = 'page_content_model';
- }
- if ( $this->getConfig()->get( 'PageLanguageUseDB' ) ) {
- $fields[] = 'page_lang';
- }
+ $fields = array_merge(
+ LinkCache::getSelectFields(),
+ [ 'page_namespace', 'page_title', 'pp_value' ]
+ );
$res = $dbr->select( [ 'page', 'page_props' ],
$fields,
}
}
- // enable OOUI if requested via ParserOutput
+ // Enable OOUI if requested via ParserOutput
if ( $parserOutput->getEnableOOUI() ) {
$this->enableOOUI();
}
+ // Include profiling data
+ $this->limitReportData = $parserOutput->getLimitReportData();
+
// Link flags are ignored for now, but may in the future be
// used to mark individual language links.
$linkFlags = [];
public function sendCacheControl() {
$response = $this->getRequest()->response();
$config = $this->getConfig();
- if ( $config->get( 'UseETag' ) && $this->mETag ) {
- $response->header( "ETag: $this->mETag" );
- }
$this->addVaryHeader( 'Cookie' );
$this->addAcceptLanguage();
$userdir = $this->getLanguage()->getDir();
$sitedir = $wgContLang->getDir();
- $ret = Html::htmlHeader( $sk->getHtmlElementAttributes() );
+ $pieces = [];
+ $pieces[] = Html::htmlHeader( $sk->getHtmlElementAttributes() );
if ( $this->getHTMLTitle() == '' ) {
$this->setHTMLTitle( $this->msg( 'pagetitle', $this->getPageTitle() )->inContentLanguage() );
$openHead = Html::openElement( 'head' );
if ( $openHead ) {
- # Don't bother with the newline if $head == ''
- $ret .= "$openHead\n";
+ $pieces[] = $openHead;
}
if ( !Html::isXmlMimeType( $this->getConfig()->get( 'MimeType' ) ) ) {
// Our XML declaration is output by Html::htmlHeader.
// http://www.whatwg.org/html/semantics.html#attr-meta-http-equiv-content-type
// http://www.whatwg.org/html/semantics.html#charset
- $ret .= Html::element( 'meta', [ 'charset' => 'UTF-8' ] ) . "\n";
+ $pieces[] = Html::element( 'meta', [ 'charset' => 'UTF-8' ] );
}
- $ret .= Html::element( 'title', null, $this->getHTMLTitle() ) . "\n";
- $ret .= $this->getInlineHeadScripts() . "\n";
- $ret .= $this->buildCssLinks() . "\n";
- $ret .= $this->getExternalHeadScripts() . "\n";
+ $pieces[] = Html::element( 'title', null, $this->getHTMLTitle() );
+ $pieces[] = $this->getInlineHeadScripts();
+ $pieces[] = $this->buildCssLinks();
+ $pieces[] = $this->getExternalHeadScripts();
foreach ( $this->getHeadLinksArray() as $item ) {
- $ret .= $item . "\n";
+ $pieces[] = $item;
}
foreach ( $this->mHeadItems as $item ) {
- $ret .= $item . "\n";
+ $pieces[] = $item;
}
$closeHead = Html::closeElement( 'head' );
if ( $closeHead ) {
- $ret .= "$closeHead\n";
+ $pieces[] = $closeHead;
}
$bodyClasses = [];
$bodyClasses[] = 'capitalize-all-nouns';
}
+ // Parser feature migration class
+ // The idea is that this will eventually be removed, after the wikitext
+ // which requires it is cleaned up.
+ $bodyClasses[] = 'mw-hide-empty-elt';
+
$bodyClasses[] = $sk->getPageClasses( $this->getTitle() );
$bodyClasses[] = 'skin-' . Sanitizer::escapeClass( $sk->getSkinName() );
$bodyClasses[] =
$sk->addToBodyAttributes( $this, $bodyAttrs );
Hooks::run( 'OutputPageBodyAttributes', [ $this, $sk, &$bodyAttrs ] );
- $ret .= Html::openElement( 'body', $bodyAttrs ) . "\n";
+ $pieces[] = Html::openElement( 'body', $bodyAttrs );
- return $ret;
+ return WrappedStringList::join( "\n", $pieces );
}
/**
continue;
}
+ if ( $only === ResourceLoaderModule::TYPE_STYLES ) {
+ if ( $module->getType() !== ResourceLoaderModule::LOAD_STYLES ) {
+ $logger = $resourceLoader->getLogger();
+ $logger->debug( 'Unexpected general module "{module}" in styles queue.', [
+ 'module' => $name,
+ ] );
+ } else {
+ $links['states'][$name] = 'ready';
+ }
+ }
+
$sortedModules[$module->getSource()][$module->getGroup()][$name] = $module;
}
/**
* Build html output from an array of links from makeResourceLoaderLink.
* @param array $links
- * @return string HTML
+ * @return string|WrappedStringList HTML
*/
protected static function getHtmlFromLoaderLinks( array $links ) {
$html = [];
// Filter out empty values
$html = array_filter( $html, 'strlen' );
- if ( count( $states ) ) {
+ if ( $states ) {
array_unshift( $html, ResourceLoader::makeInlineScript(
ResourceLoader::makeLoaderStateScript( $states )
) );
}
/**
- * <script src="..."> tags for "<head>". This is the startup module
+ * <script src="..."> tags for "<head>".This is the startup module
* and other modules marked with position 'top'.
*
- * @return string HTML fragment
+ * @return string|WrappedStringList HTML
*/
function getExternalHeadScripts() {
- $links = [];
-
// Startup - this provides the client with the module
// manifest and loads jquery and mediawiki base modules
+ $links = [];
$links[] = $this->makeResourceLoaderLink( 'startup', ResourceLoaderModule::TYPE_SCRIPTS );
-
return self::getHtmlFromLoaderLinks( $links );
}
/**
- * <script>...</script> tags to put in "<head>".
+ * Inline "<script>" tags to put in "<head>".
*
- * @return string HTML fragment
+ * @return string|WrappedStringList HTML
*/
function getInlineHeadScripts() {
$links = [];
*
* @param bool $unused Previously used to let this method change its output based
* on whether it was called by getExternalHeadScripts() or getBottomScripts().
- * @return string
+ * @return string|WrappedStringList HTML
*/
function getScriptsForBottomQueue( $unused = null ) {
// Scripts "only" requests marked for bottom inclusion
$links[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_COMBINED );
}
- // Group JS is only enabled if site JS is enabled.
- $links[] = $this->makeResourceLoaderLink(
- 'user.groups',
- ResourceLoaderModule::TYPE_COMBINED
- );
-
return self::getHtmlFromLoaderLinks( $links );
}
* @return string
*/
function getBottomScripts() {
- return $this->getScriptsForBottomQueue();
+ return $this->getScriptsForBottomQueue() .
+ ResourceLoader::makeInlineScript(
+ ResourceLoader::makeConfigSetScript(
+ [ 'wgPageParseReport' => $this->limitReportData ],
+ true
+ )
+ );
}
/**
}
/**
- * Build a set of "<link>" elements for the stylesheets specified in the $this->styles array.
- * These will be applied to various media & IE conditionals.
+ * Build a set of "<link>" elements for stylesheets specified in the $this->styles array.
*
- * @return string
+ * @return string|WrappedStringList HTML
*/
public function buildCssLinks() {
global $wgContLang;
$moduleStyles = $this->getModuleStyles();
// Per-site custom styles
- $moduleStyles[] = 'site';
+ $moduleStyles[] = 'site.styles';
$moduleStyles[] = 'noscript';
- $moduleStyles[] = 'user.groups';
// Per-user custom styles
if ( $this->getConfig()->get( 'AllowUserCss' ) && $this->getTitle()->isCssSubpage()
if ( !$module ) {
continue;
}
- if ( $name === 'site' ) {
+ if ( $name === 'site.styles' ) {
// HACK: The site module shouldn't be fragmented with a cache group and
// http request. But in order to ensure its styles are separated and after the
// ResourceLoaderDynamicStyles marker, pretend it is in a group called 'site'.
}
// Add stuff in $otherTags (previewed user CSS if applicable)
- return self::getHtmlFromLoaderLinks( $links ) . implode( '', $otherTags );
+ $links[] = implode( '', $otherTags );
+
+ return self::getHtmlFromLoaderLinks( $links );
}
/**