(bug14900) Make __INDEX__ and __NOINDEX__ not override $wgArticleRobotPolicies where...
[lhc/web/wiklou.git] / includes / OutputPage.php
index 53556b9..4bb2f95 100644 (file)
@@ -13,6 +13,11 @@ class OutputPage {
        var $mSubtitle = '', $mRedirect = '', $mStatusCode;
        var $mLastModified = '', $mETag = false;
        var $mCategoryLinks = array(), $mLanguageLinks = array();
+
+       var $mScriptLoaderClassList = array();
+       // the most recent id of any script that is grouped in the script request
+       var $mLatestScriptRevID = 0;
+
        var $mScripts = '', $mLinkColours, $mPageLinkTitle = '', $mHeadItems = array();
        var $mTemplateIds = array();
 
@@ -29,11 +34,13 @@ class OutputPage {
        var $mArticleBodyOnly = false;
 
        var $mNewSectionLink = false;
+       var $mHideNewSectionLink = false;
        var $mNoGallery = false;
        var $mPageTitleActionText = '';
        var $mParseWarnings = array();
        var $mSquidMaxage = 0;
        var $mRevisionId = null;
+       protected $mTitle = null;
 
        /**
         * An array of stylesheet filenames (relative from skins path), with options
@@ -83,12 +90,26 @@ class OutputPage {
                array_push( $this->mMetatags, array( $name, $val ) );
        }
 
-       function addKeyword( $text ) { array_push( $this->mKeywords, $text ); }
-       function addScript( $script ) { $this->mScripts .= "\t\t".$script; }
-       
-       function addExtensionStyle( $url ) {
-               $linkarr = array( 'rel' => 'stylesheet', 'href' => $url, 'type' => 'text/css' );
-               array_push( $this->mExtStyles, $linkarr );
+       function addKeyword( $text ) {
+               if( is_array( $text )) {
+                       $this->mKeywords = array_merge( $this->mKeywords, $text );
+               } else {
+                       array_push( $this->mKeywords, $text );
+               }
+       }
+       function addScript( $script ) {
+               $this->mScripts .= $script . "\n";
+       }
+
+       /**
+        * Register and add a stylesheet from an extension directory.
+        * @param $url String path to sheet.  Provide either a full url (beginning
+        *             with 'http', etc) or a relative path from the document root
+        *             (beginning with '/').  Otherwise it behaves identically to
+        *             addStyle() and draws from the /skins folder.
+        */
+       public function addExtensionStyle( $url ) {
+               array_push( $this->mExtStyles, $url );
        }
 
        /**
@@ -96,26 +117,175 @@ class OutputPage {
         * @param string $file filename in skins/common or complete on-server path (/foo/bar.js)
         */
        function addScriptFile( $file ) {
-               global $wgStylePath, $wgStyleVersion, $wgJsMimeType;
+               global $wgStylePath, $wgScript, $wgUser;
+               global $wgJSAutoloadClasses, $wgJSAutoloadLocalClasses, $wgEnableScriptLoader, $wgScriptPath;
+
                if( substr( $file, 0, 1 ) == '/' ) {
                        $path = $file;
                } else {
-                       $path =  "{$wgStylePath}/common/{$file}";
+                       $path = "{$wgStylePath}/common/{$file}";
+               }
+
+               if( $wgEnableScriptLoader ){
+                       if( strpos( $path, $wgScript ) !== false ){
+                               $reqPath = str_replace( $wgScript . '?', '', $path );
+                               $reqArgs = explode( '&', $reqPath );
+                               $reqSet = array();
+
+                               foreach( $reqArgs as $arg ){
+                                       list( $key, $var ) = explode( '=', $arg );
+                                       $reqSet[$key] = $var;
+                               }
+
+                               if( isset( $reqSet['title'] ) && $reqSet != '' ) {
+                                       // extract any extra param (for now just skin)
+                                       $ext_param = ( isset( $reqSet['useskin'] ) && $reqSet['useskin'] != '' ) ? '|useskin=' . ucfirst( $reqSet['useskin'] ) : '';
+                                       $this->mScriptLoaderClassList[] = 'WT:' . $reqSet['title'] . $ext_param ;
+                                       // add the title revision to the key
+                                       $t = Title::newFromText( $reqSet['title'] );
+                                       // if there is no title (don't worry we just use the $wgStyleVersion var (which should be updated on relevant commits)
+                                       if( $t && $t->exists() ){
+                                               if( $t->getLatestRevID() > $this->mLatestScriptRevID  )
+                                                       $this->mLatestScriptRevID = $t->getLatestRevID();
+                                       }
+                                       return true;
+                               }
+                       }
+
+                       // check for class from path:
+                       $js_class = $this->getJsClassFromPath( $path );
+                       if( $js_class ){
+                               // add to the class list:
+                               $this->mScriptLoaderClassList[] = $js_class;
+                               return true;
+                       }
+               }
+
+               // if the script loader did not find a way to add the script than add using addScript
+               $this->addScript( Html::linkedScript( wfAppendQuery( $path, $this->getURIDparam() ) ) );
+       }
+
+       /**
+        * This is the core script that is included on every page
+        * (they are requested separately to improve caching across
+        *  different page load types (edit, upload, view, etc)
+        */
+       function addCoreScripts2Top(){
+               global $wgEnableScriptLoader, $wgJSAutoloadLocalClasses, $wgScriptPath, $wgEnableJS2system;
+               //@@todo we should deprecate wikibits in favor of mv_embed and native jQuery functions
+
+               if( $wgEnableJS2system ){
+                       $core_classes = array( 'window.jQuery', 'mv_embed', 'wikibits' );
+               } else {
+                       $core_classes = array( 'wikibits' );
+               }
+
+               if( $wgEnableScriptLoader ){
+                       $this->mScripts = $this->getScriptLoaderJs( $core_classes ) . $this->mScripts;
+               } else {
+                       $so = '';
+                       foreach( $core_classes as $s ){
+                               if( isset( $wgJSAutoloadLocalClasses[$s] ) ){
+                                       $so .= Html::linkedScript( "{$wgScriptPath}/{$wgJSAutoloadLocalClasses[$s]}?" . $this->getURIDparam() );
+                               }
+                       }
+                       $this->mScripts =  $so . $this->mScripts;
+               }
+       }
+
+       /**
+        * @param $js_class String: name of JavaScript class
+        * @return Boolean: false if class wasn't found, true on success
+        */
+       function addScriptClass( $js_class ){
+               global $wgDebugJavaScript, $wgJSAutoloadLocalClasses, $wgJSAutoloadClasses,
+                               $wgEnableScriptLoader, $wgStyleVersion, $wgScriptPath;
+
+               if( isset( $wgJSAutoloadClasses[$js_class] ) || isset( $wgJSAutoloadLocalClasses[$js_class] ) ){
+                       if( $wgEnableScriptLoader ){
+                               if( !in_array( $js_class, $this->mScriptLoaderClassList ) ){
+                                       $this->mScriptLoaderClassList[] = $js_class;
+                               }
+                       } else {
+                               // do a normal load of without the script-loader:
+                               $path = $wgScriptPath . '/';
+                               if( isset( $wgJSAutoloadClasses[$js_class] ) ){
+                                       $path.= $wgJSAutoloadClasses[$js_class];
+                               }else if( isset( $wgJSAutoloadLocalClasses[$js_class] ) ){
+                                       $path.= $wgJSAutoloadLocalClasses[$js_class];
+                               }
+                               $urlAppend = ( $wgDebugJavaScript ) ? time() : $wgStyleVersion;
+                               $this->addScript( Html::linkedScript( "$path?$urlAppend" ) );
+                       }
+                       return true;
+               }
+               wfDebug( __METHOD__ . ' could not find js_class: ' . $js_class );
+               return false; // could not find the class
+       }
+
+       /**
+        * gets the scriptLoader javascript include
+        * @param $forcClassAry Boolean: false by default
+        */
+       function getScriptLoaderJs( $forceClassAry = false ){
+               global $wgRequest, $wgDebugJavaScript;
+
+               if( !$forceClassAry ){
+                       $class_list = implode( ',', $this->mScriptLoaderClassList );
+               } else {
+                       $class_list = implode( ',', $forceClassAry );
+               }
+
+               $debug_param = ( $wgDebugJavaScript ||
+                                                $wgRequest->getVal( 'debug' ) == 'true' ||
+                                                $wgRequest->getVal( 'debug' ) == '1' )
+                                        ? '&debug=true' : '';
+
+               //@@todo intelligent unique id generation based on svn version of file (rather than just grabbing the $wgStyleVersion var)
+               //@@todo we should check the packaged message text in this javascript file for updates and update the $mScriptLoaderURID id (in getJsClassFromPath)
+
+               //generate the unique request param (combine with the most recent revision id of any wiki page with the $wgStyleVersion var)
+
+               return Html::linkedScript( wfScript( 'mwScriptLoader' ) . "?class={$class_list}{$debug_param}&" . $this->getURIDparam() );
+       }
+
+       function getURIDparam(){
+               global $wgDebugJavaScript, $wgStyleVersion;
+               if( $wgDebugJavaScript ){
+                       return 'urid=' . time();
+               } else {
+                       return "urid={$wgStyleVersion}_{$this->mLatestScriptRevID}";
                }
-               $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"$path?$wgStyleVersion\"></script>\n" );
        }
-       
+
+       function getJsClassFromPath( $path ){
+               global $wgJSAutoloadClasses, $wgJSAutoloadLocalClasses, $wgScriptPath;
+
+               $scriptLoaderPaths = array_merge( $wgJSAutoloadClasses,  $wgJSAutoloadLocalClasses );
+               foreach( $scriptLoaderPaths as $js_class => $js_path ){
+                       $js_path = "{$wgScriptPath}/{$js_path}";
+                       if( $path == $js_path )
+                               return $js_class;
+               }
+               return false;
+       }
+
        /**
         * Add a self-contained script tag with the given contents
         * @param string $script JavaScript text, no <script> tags
         */
        function addInlineScript( $script ) {
-               global $wgJsMimeType;
-               $this->mScripts .= "<script type=\"$wgJsMimeType\">/*<![CDATA[*/\n$script\n/*]]>*/</script>";
+               $this->mScripts .= Html::inlineScript( "\n$script\n" ) . "\n";
        }
 
        function getScript() {
-               return $this->mScripts . $this->getHeadItems();
+               global $wgEnableScriptLoader;
+               if( $wgEnableScriptLoader ){
+                       //include $this->mScripts (for anything that we could not package into the scriptloader
+                       return $this->mScripts . "\n" . $this->getScriptLoaderJs() . $this->getHeadItems();
+               } else {
+                       return $this->mScripts . $this->getHeadItems();
+               }
        }
 
        function getHeadItems() {
@@ -142,7 +312,7 @@ class OutputPage {
                # $linkarr should be an associative array of attributes. We'll escape on output.
                array_push( $this->mLinktags, $linkarr );
        }
-       
+
        # Get all links added by extensions
        function getExtStyle() {
                return $this->mExtStyles;
@@ -165,9 +335,9 @@ class OutputPage {
         *
         * @return bool True iff cache-ok headers was sent.
         */
-       function checkLastModified ( $timestamp ) {
+       function checkLastModified( $timestamp ) {
                global $wgCachePages, $wgCacheEpoch, $wgUser, $wgRequest;
-               
+
                if ( !$timestamp || $timestamp == '19700101000000' ) {
                        wfDebug( __METHOD__ . ": CACHE DISABLED, NO TIMESTAMP\n" );
                        return false;
@@ -220,9 +390,9 @@ class OutputPage {
                }
                $clientHeaderTime = wfTimestamp( TS_MW, $clientHeaderTime );
 
-               wfDebug( __METHOD__ . ": client sent If-Modified-Since: " . 
+               wfDebug( __METHOD__ . ": client sent If-Modified-Since: " .
                        wfTimestamp( TS_ISO_8601, $clientHeaderTime ) . "\n", false );
-               wfDebug( __METHOD__ . ": effective Last-Modified: " . 
+               wfDebug( __METHOD__ . ": effective Last-Modified: " .
                        wfTimestamp( TS_ISO_8601, $maxModified ) . "\n", false );
                if( $clientHeaderTime < $maxModified ) {
                        wfDebug( __METHOD__ . ": STALE, $info\n", false );
@@ -230,8 +400,9 @@ class OutputPage {
                }
 
                # Not modified
-               # Give a 304 response code and disable body output 
+               # Give a 304 response code and disable body output
                wfDebug( __METHOD__ . ": NOT MODIFIED, $info\n", false );
+               ini_set('zlib.output_compression', 0);
                $wgRequest->response()->header( "HTTP/1.1 304 Not Modified" );
                $this->sendCacheControl();
                $this->disable();
@@ -263,22 +434,14 @@ class OutputPage {
         * @return null
         */
        public function setRobotPolicy( $policy ) {
-               $policy = explode( ',', $policy );
-               $policy = array_map( 'trim', $policy );
-
-               # The default policy is follow, so if nothing is said explicitly, we
-               # do that.
-               if( in_array( 'nofollow', $policy ) ) {
-                       $this->mFollowPolicy = 'nofollow';
-               } else {
-                       $this->mFollowPolicy = 'follow';
-               }
+               $policy = Article::formatRobotPolicy( $policy );
 
-               if( in_array( 'noindex', $policy ) ) {
-                       $this->mIndexPolicy = 'noindex';
-               } else {
-                       $this->mIndexPolicy = 'index';
-               }
+               if( isset( $policy['index'] ) ){
+                       $this->setIndexPolicy( $policy['index'] );
+               }
+               if( isset( $policy['follow'] ) ){
+                       $this->setFollowPolicy( $policy['follow'] );
+               }
        }
 
        /**
@@ -309,20 +472,51 @@ class OutputPage {
                }
        }
 
-       public function setHTMLTitle( $name ) {$this->mHTMLtitle = $name; }
+       /**
+        * "HTML title" means the contents of <title>. It is stored as plain, unescaped text and will be run through htmlspecialchars in the skin file.
+        */
+       public function setHTMLTitle( $name ) {
+               $this->mHTMLtitle = $name;
+       }
+
+       /**
+        * "Page title" means the contents of <h1>. It is stored as a valid HTML fragment.
+        * This function allows good tags like <sup> in the <h1> tag, but not bad tags like <script>.
+        * This function automatically sets <title> to the same content as <h1> but with all tags removed.
+        * Bad tags that were escaped in <h1> will still be escaped in <title>, and good tags like <i> will be dropped entirely.
+         */
        public function setPageTitle( $name ) {
-               global $action, $wgContLang;
-               $name = $wgContLang->convert($name, true);
-               $this->mPagetitle = $name;
-               if(!empty($action)) {
-                       $taction =  $this->getPageTitleActionText();
-                       if( !empty( $taction ) ) {
-                               $name .= ' - '.$taction;
-                       }
+               global $wgContLang;
+               $name = $wgContLang->convert( $name, true );
+               # change "<script>foo&bar</script>" to "&lt;script&gt;foo&amp;bar&lt;/script&gt;"
+               # but leave "<i>foobar</i>" alone
+               $nameWithTags = Sanitizer::normalizeCharReferences( Sanitizer::removeHTMLtags( $name ) );
+               $this->mPagetitle = $nameWithTags;
+
+               $taction =  $this->getPageTitleActionText();
+               if( !empty( $taction ) ) {
+                       $name .= ' - '.$taction;
                }
 
-               $this->setHTMLTitle( wfMsg( 'pagetitle', $name ) );
+               # change "<i>foo&amp;bar</i>" to "foo&bar"
+               $this->setHTMLTitle( wfMsg( 'pagetitle', Sanitizer::stripAllTags( $nameWithTags ) ) );
+       }
+
+       public function setTitle( $t ) {
+               $this->mTitle = $t;
        }
+
+       public function getTitle() {
+               if ( $this->mTitle instanceof Title ) {
+                       return $this->mTitle;
+               }
+               else {
+                       wfDebug( __METHOD__ . ' called and $mTitle is null. Return $wgTitle for sanity' );
+                       global $wgTitle;
+                       return $wgTitle;
+               }
+       }
+
        public function getHTMLTitle() { return $this->mHTMLtitle; }
        public function getPageTitle() { return $this->mPagetitle; }
        public function setSubtitle( $str ) { $this->mSubtitle = /*$this->parse(*/$str/*)*/; } // @bug 2514
@@ -338,6 +532,7 @@ class OutputPage {
        public function setOnloadHandler( $js ) { $this->mOnloadHandler = $js; }
        public function getOnloadHandler() { return $this->mOnloadHandler; }
        public function disable() { $this->mDoNothing = true; }
+       public function isDisabled() { return $this->mDoNothing; }
 
        public function setArticleRelated( $v ) {
                $this->mIsArticleRelated = $v;
@@ -408,9 +603,14 @@ class OutputPage {
                if ( wfRunHooks( 'OutputPageMakeCategoryLinks', array( &$this, $categories, &$this->mCategoryLinks ) ) ) {
                        $sk = $wgUser->getSkin();
                        foreach ( $categories as $category => $type ) {
+                               $origcategory = $category;
                                $title = Title::makeTitleSafe( NS_CATEGORY, $category );
+                               $wgContLang->findVariantLink( $category, $title, true );
+                               if ( $category != $origcategory )
+                                       if ( array_key_exists( $category, $categories ) )
+                                               continue;
                                $text = $wgContLang->convertHtml( $title->getText() );
-                               $this->mCategoryLinks[$type][] = $sk->makeLinkObj( $title, $text );
+                               $this->mCategoryLinks[$type][] = $sk->link( $title, $text );
                        }
                }
        }
@@ -455,7 +655,7 @@ class OutputPage {
                $val = is_null( $revid ) ? null : intval( $revid );
                return wfSetVar( $this->mRevisionId, $val );
        }
-       
+
        public function getRevisionId() {
                return $this->mRevisionId;
        }
@@ -469,8 +669,8 @@ class OutputPage {
         * @param bool   $linestart
         */
        public function addWikiText( $text, $linestart = true ) {
-               global $wgTitle;
-               $this->addWikiTextTitle($text, $wgTitle, $linestart);
+               $title = $this->getTitle(); // Work arround E_STRICT
+               $this->addWikiTextTitle( $text, $title, $linestart );
        }
 
        public function addWikiTextWithTitle($text, &$title, $linestart = true) {
@@ -506,24 +706,13 @@ class OutputPage {
         * @param ParserOutput object &$parserOutput
         */
        public function addParserOutputNoText( &$parserOutput ) {
-               global $wgTitle, $wgExemptFromUserRobotsControl, $wgContentNamespaces;
+               global $wgExemptFromUserRobotsControl, $wgContentNamespaces;
 
                $this->mLanguageLinks += $parserOutput->getLanguageLinks();
                $this->addCategoryLinks( $parserOutput->getCategories() );
                $this->mNewSectionLink = $parserOutput->getNewSection();
+               $this->mHideNewSectionLink = $parserOutput->getHideNewSection();
 
-               if( is_null( $wgExemptFromUserRobotsControl ) ) {
-                       $bannedNamespaces = $wgContentNamespaces;
-               } else {
-                       $bannedNamespaces = $wgExemptFromUserRobotsControl;
-               }
-               if( !in_array( $wgTitle->getNamespace(), $bannedNamespaces ) ) {
-                       # FIXME (bug 14900): This overrides $wgArticleRobotPolicies, and it
-                       # shouldn't
-                       $this->setIndexPolicy( $parserOutput->getIndexPolicy() );
-               }
-
-               $this->addKeywords( $parserOutput );
                $this->mParseWarnings = $parserOutput->getWarnings();
                if ( $parserOutput->getCacheTime() == -1 ) {
                        $this->enableClientCache( false );
@@ -531,11 +720,18 @@ class OutputPage {
                $this->mNoGallery = $parserOutput->getNoGallery();
                $this->mHeadItems = array_merge( $this->mHeadItems, (array)$parserOutput->mHeadItems );
                // Versioning...
-               $this->mTemplateIds = wfArrayMerge( $this->mTemplateIds, (array)$parserOutput->mTemplateIds );
-
-               // Display title
+               foreach ( (array)$parserOutput->mTemplateIds as $ns => $dbks ) {
+                       if ( isset( $this->mTemplateIds[$ns] ) ) {
+                               $this->mTemplateIds[$ns] = $dbks + $this->mTemplateIds[$ns];
+                       } else {
+                               $this->mTemplateIds[$ns] = $dbks;
+                       }
+               }
+               // Page title
                if( ( $dt = $parserOutput->getDisplayTitle() ) !== false )
                        $this->setPageTitle( $dt );
+               else if ( ( $title = $parserOutput->getTitleText() ) != '' )
+                       $this->setPageTitle( $title );
 
                // Hooks registered in the object
                global $wgParserOutputHooks;
@@ -570,7 +766,7 @@ class OutputPage {
         * @deprecated Use Article::outputWikitext
         */
        public function addPrimaryWikiText( $text, $article, $cache = true ) {
-               global $wgParser, $wgUser;
+               global $wgParser;
 
                wfDeprecated( __METHOD__ );
 
@@ -581,7 +777,7 @@ class OutputPage {
                $popts->setTidy(false);
                if ( $cache && $article && $parserOutput->getCacheTime() != -1 ) {
                        $parserCache = ParserCache::singleton();
-                       $parserCache->save( $parserOutput, $article, $wgUser );
+                       $parserCache->save( $parserOutput, $article, $popts);
                }
 
                $this->addParserOutput( $parserOutput );
@@ -591,17 +787,16 @@ class OutputPage {
         * @deprecated use addWikiTextTidy()
         */
        public function addSecondaryWikiText( $text, $linestart = true ) {
-               global $wgTitle;
                wfDeprecated( __METHOD__ );
-               $this->addWikiTextTitleTidy($text, $wgTitle, $linestart);
+               $this->addWikiTextTitleTidy($text, $this->getTitle(), $linestart);
        }
 
        /**
         * Add wikitext with tidy enabled
         */
        public function addWikiTextTidy(  $text, $linestart = true ) {
-               global $wgTitle;
-               $this->addWikiTextTitleTidy($text, $wgTitle, $linestart);
+               $title = $this->getTitle();
+               $this->addWikiTextTitleTidy($text, $title, $linestart);
        }
 
 
@@ -625,25 +820,43 @@ class OutputPage {
         * @param bool   $interface ??
         */
        public function parse( $text, $linestart = true, $interface = false ) {
-               global $wgParser, $wgTitle;
+               global $wgParser;
+               if( is_null( $this->getTitle() ) ) {
+                       throw new MWException( 'Empty $mTitle in ' . __METHOD__ );
+               }
                $popts = $this->parserOptions();
                if ( $interface) { $popts->setInterfaceMessage(true); }
-               $parserOutput = $wgParser->parse( $text, $wgTitle, $popts,
+               $parserOutput = $wgParser->parse( $text, $this->getTitle(), $popts,
                        $linestart, true, $this->mRevisionId );
                if ( $interface) { $popts->setInterfaceMessage(false); }
                return $parserOutput->getText();
        }
 
+       /** Parse wikitext, strip paragraphs, and return the HTML. */
+       public function parseInline( $text, $linestart = true, $interface = false ) {
+               $parsed = $this->parse( $text, $linestart, $interface );
+
+               $m = array();
+               if ( preg_match( '/^<p>(.*)\n?<\/p>\n?/sU', $parsed, $m ) ) {
+                       $parsed = $m[1];
+               }
+
+               return $parsed;
+       }
+
        /**
         * @param Article $article
         * @param User    $user
         *
+        * @deprecated
+        *
         * @return bool True if successful, else false.
         */
-       public function tryParserCache( &$article, $user ) {
-               $parserCache = ParserCache::singleton();
-               $parserOutput = $parserCache->get( $article, $user );
-               if ( $parserOutput !== false ) {
+       public function tryParserCache( &$article ) {
+               wfDeprecated( __METHOD__ );
+               $parserOutput = ParserCache::singleton()->get( $article, $article->getParserOptions() );
+
+               if ($parserOutput !== false) {
                        $this->addParserOutput( $parserOutput );
                        return true;
                } else {
@@ -694,7 +907,7 @@ class OutputPage {
         * If it does, it's very important that we don't allow public caching
         */
        function haveCacheVaryCookies() {
-               global $wgRequest, $wgCookiePrefix;
+               global $wgRequest;
                $cookieHeader = $wgRequest->getHeader( 'cookie' );
                if ( $cookieHeader === false ) {
                        return false;
@@ -713,7 +926,6 @@ class OutputPage {
 
        /** Get a complete X-Vary-Options header */
        public function getXVO() {
-               global $wgCookiePrefix;
                $cvCookies = $this->getCacheVaryCookies();
                $xvo = 'X-Vary-Options: Accept-Encoding;list-contains=gzip,Cookie;';
                $first = true;
@@ -729,7 +941,7 @@ class OutputPage {
        }
 
        public function sendCacheControl() {
-               global $wgUseSquid, $wgUseESI, $wgUseETag, $wgSquidMaxage, $wgRequest;
+               global $wgUseSquid, $wgUseESI, $wgUseETag, $wgSquidMaxage, $wgRequest, $wgUseXVO;
 
                $response = $wgRequest->response();
                if ($wgUseETag && $this->mETag)
@@ -739,8 +951,10 @@ class OutputPage {
                # maintain different caches for logged-in users and non-logged in ones
                $response->header( 'Vary: Accept-Encoding, Cookie' );
 
-               # Add an X-Vary-Options header for Squid with Wikimedia patches
-               $response->header( $this->getXVO() );
+               if ( $wgUseXVO ) {
+                       # Add an X-Vary-Options header for Squid with Wikimedia patches
+                       $response->header( $this->getXVO() );
+               }
 
                if( !$this->uncacheableBecauseRequestVars() && $this->mEnableClientCache ) {
                        if( $wgUseSquid && session_id() == '' &&
@@ -793,15 +1007,14 @@ class OutputPage {
        public function output() {
                global $wgUser, $wgOutputEncoding, $wgRequest;
                global $wgContLanguageCode, $wgDebugRedirects, $wgMimeType;
-               global $wgJsMimeType, $wgUseAjax, $wgAjaxWatch;
-               global $wgEnableMWSuggest;
+               global $wgUseAjax, $wgAjaxWatch;
+               global $wgEnableMWSuggest, $wgUniversalEditButton;
+               global $wgArticle;
 
                if( $this->mDoNothing ){
                        return;
                }
-
                wfProfileIn( __METHOD__ );
-
                if ( '' != $this->mRedirect ) {
                        # Standards require redirect URLs to be absolute
                        $this->mRedirect = wfExpandUrl( $this->mRedirect );
@@ -811,7 +1024,6 @@ class OutputPage {
                                }
                                $this->mLastModified = wfTimestamp( TS_RFC2822 );
                        }
-
                        $this->sendCacheControl();
 
                        $wgRequest->response()->header("Content-Type: text/html; charset=utf-8");
@@ -883,6 +1095,9 @@ class OutputPage {
 
                $sk = $wgUser->getSkin();
 
+               // add our core scripts to output
+               $this->addCoreScripts2Top();
+
                if ( $wgUseAjax ) {
                        $this->addScriptFile( 'ajax.js' );
 
@@ -891,17 +1106,34 @@ class OutputPage {
                        if( $wgAjaxWatch && $wgUser->isLoggedIn() ) {
                                $this->addScriptFile( 'ajaxwatch.js' );
                        }
-                       
+
                        if ( $wgEnableMWSuggest && !$wgUser->getOption( 'disablesuggest', false ) ){
                                $this->addScriptFile( 'mwsuggest.js' );
                        }
                }
-               
+
                if( $wgUser->getBoolOption( 'editsectiononrightclick' ) ) {
                        $this->addScriptFile( 'rightclickedit.js' );
                }
 
-               $this->mBodytext = StringUtils::cleanForCharset( $this->mBodytext, $wgOutputEncoding );
+               if( $wgUniversalEditButton ) {
+                       if( isset( $wgArticle ) && $this->getTitle() && $this->getTitle()->quickUserCan( 'edit' )
+                               && ( $this->getTitle()->exists() || $this->getTitle()->quickUserCan( 'create' ) ) ) {
+                               // Original UniversalEditButton
+                               $this->addLink( array(
+                                       'rel' => 'alternate',
+                                       'type' => 'application/x-wiki',
+                                       'title' => wfMsg( 'edit' ),
+                                       'href' => $this->getTitle()->getLocalURL( 'action=edit' )
+                               ) );
+                               // Alternate edit link
+                               $this->addLink( array(
+                                       'rel' => 'edit',
+                                       'title' => wfMsg( 'edit' ),
+                                       'href' => $this->getTitle()->getLocalURL( 'action=edit' )
+                               ) );
+                       }
+               }
 
                # Buffer output; final headers may depend on later processing
                ob_start();
@@ -927,8 +1159,9 @@ class OutputPage {
        }
 
        /**
-        * @todo document
-        * @param string $ins
+        * Actually output something with print(). Performs an iconv to the
+        * output encoding, if needed.
+        * @param string $ins The string to output
         */
        public function out( $ins ) {
                global $wgInputEncoding, $wgOutputEncoding, $wgContLang;
@@ -946,7 +1179,7 @@ class OutputPage {
         */
        public static function setEncodings() {
                global $wgInputEncoding, $wgOutputEncoding;
-               global $wgUser, $wgContLang;
+               global $wgContLang;
 
                $wgInputEncoding = strtolower( $wgInputEncoding );
 
@@ -975,7 +1208,7 @@ class OutputPage {
         * @return nothing
         */
        function blockedPage( $return = true ) {
-               global $wgUser, $wgContLang, $wgTitle, $wgLang;
+               global $wgUser, $wgContLang, $wgLang;
 
                $this->setPageTitle( wfMsg( 'blockedtitle' ) );
                $this->setRobotPolicy( 'noindex,nofollow' );
@@ -1025,7 +1258,7 @@ class OutputPage {
 
                # Don't auto-return to special pages
                if( $return ) {
-                       $return = $wgTitle->getNamespace() > -1 ? $wgTitle : NULL;
+                       $return = $this->getTitle()->getNamespace() > -1 ? $this->getTitle() : null;
                        $this->returnToMain( null, $return );
                }
        }
@@ -1038,9 +1271,8 @@ class OutputPage {
         * @param array $params Message parameters
         */
        public function showErrorPage( $title, $msg, $params = array() ) {
-               global $wgTitle;
-               if ( isset($wgTitle) ) {
-                       $this->mDebugtext .= 'Original title: ' . $wgTitle->getPrefixedText() . "\n";
+               if ( $this->getTitle() ) {
+                       $this->mDebugtext .= 'Original title: ' . $this->getTitle()->getPrefixedText() . "\n";
                }
                $this->setPageTitle( wfMsg( $title ) );
                $this->setHTMLTitle( wfMsg( 'errorpagetitle' ) );
@@ -1052,7 +1284,7 @@ class OutputPage {
 
                array_unshift( $params, 'parse' );
                array_unshift( $params, $msg );
-               $this->addHtml( call_user_func_array( 'wfMsgExt', $params ) );
+               $this->addHTML( call_user_func_array( 'wfMsgExt', $params ) );
 
                $this->returnToMain();
        }
@@ -1064,10 +1296,8 @@ class OutputPage {
         */
        public function showPermissionsErrorPage( $errors, $action = null )
        {
-               global $wgTitle;
-
                $this->mDebugtext .= 'Original title: ' .
-               $wgTitle->getPrefixedText() . "\n";
+               $this->getTitle()->getPrefixedText() . "\n";
                $this->setPageTitle( wfMsg( 'permissionserrors' ) );
                $this->setHTMLTitle( wfMsg( 'permissionserrors' ) );
                $this->setRobotPolicy( 'noindex,nofollow' );
@@ -1107,7 +1337,7 @@ class OutputPage {
         * @param string $permission key required
         */
        public function permissionRequired( $permission ) {
-               global $wgUser;
+               global $wgLang;
 
                $this->setPageTitle( wfMsg( 'badaccess' ) );
                $this->setHTMLTitle( wfMsg( 'errorpagetitle' ) );
@@ -1119,7 +1349,7 @@ class OutputPage {
                        User::getGroupsWithPermission( $permission ) );
                if( $groups ) {
                        $this->addWikiMsg( 'badaccess-groups',
-                               implode( ', ', $groups ),
+                               $wgLang->commaList( $groups ),
                                count( $groups) );
                } else {
                        $this->addWikiMsg( 'badaccess-group0' );
@@ -1147,7 +1377,7 @@ class OutputPage {
         * Produce the stock "please login to use the wiki" page
         */
        public function loginToUse() {
-               global $wgUser, $wgTitle, $wgContLang;
+               global $wgUser, $wgContLang;
 
                if( $wgUser->isLoggedIn() ) {
                        $this->permissionRequired( 'read' );
@@ -1162,9 +1392,15 @@ class OutputPage {
                $this->setArticleFlag( false );
 
                $loginTitle = SpecialPage::getTitleFor( 'Userlogin' );
-               $loginLink = $skin->makeKnownLinkObj( $loginTitle, wfMsgHtml( 'loginreqlink' ), 'returnto=' . $wgTitle->getPrefixedUrl() );
-               $this->addHtml( wfMsgWikiHtml( 'loginreqpagetext', $loginLink ) );
-               $this->addHtml( "\n<!--" . $wgTitle->getPrefixedUrl() . "-->" );
+               $loginLink = $skin->link(
+                       $loginTitle,
+                       wfMsgHtml( 'loginreqlink' ),
+                       array(),
+                       array( 'returnto' => $this->getTitle()->getPrefixedText() ),
+                       array( 'known', 'noclasses' )
+               );
+               $this->addHTML( wfMsgWikiHtml( 'loginreqpagetext', $loginLink ) );
+               $this->addHTML( "\n<!--" . $this->getTitle()->getPrefixedUrl() . "-->" );
 
                # Don't return to the main page if the user can't read it
                # otherwise we'll end up in a pointless loop
@@ -1187,8 +1423,7 @@ class OutputPage {
                        $text = wfMsgNoTrans( 'permissionserrorstext', count($errors)). "\n\n";
                } else {
                        global $wgLang;
-                       $action_desc = wfMsg( "right-$action" );
-                       $action_desc = $wgLang->lcfirst( $action_desc ); // FIXME: TERRIBLE HACK
+                       $action_desc = wfMsgNoTrans( "action-$action" );
                        $text = wfMsgNoTrans( 'permissionserrorstext-withaction', count($errors), $action_desc ) . "\n\n";
                }
 
@@ -1203,7 +1438,7 @@ class OutputPage {
                        }
                        $text .= '</ul>';
                } else {
-                       $text .= '<div class="permissions-errors">' . call_user_func_array( 'wfMsgNoTrans', reset( $errors ) ) . '</div>';
+                       $text .= "<div class=\"permissions-errors\">\n" . call_user_func_array( 'wfMsgNoTrans', reset( $errors ) ) . "\n</div>";
                }
 
                return $text;
@@ -1229,7 +1464,7 @@ class OutputPage {
         * @param array  $reasons   List of reasons for this error, as returned by Title::getUserPermissionsErrors().
         */
        public function readOnlyPage( $source = null, $protected = false, $reasons = array(), $action = null ) {
-               global $wgUser, $wgTitle;
+               global $wgUser;
                $skin = $wgUser->getSkin();
 
                $this->setRobotPolicy( 'noindex,nofollow' );
@@ -1245,7 +1480,18 @@ class OutputPage {
                        // Permissions error
                        if( $source ) {
                                $this->setPageTitle( wfMsg( 'viewsource' ) );
-                               $this->setSubtitle( wfMsg( 'viewsourcefor', $skin->makeKnownLinkObj( $wgTitle ) ) );
+                               $this->setSubtitle(
+                                       wfMsg(
+                                               'viewsourcefor',
+                                               $skin->link(
+                                                       $this->getTitle(),
+                                                       null,
+                                                       array(),
+                                                       array(),
+                                                       array( 'known', 'noclasses' )
+                                               )
+                                       )
+                               );
                        } else {
                                $this->setPageTitle( wfMsg( 'badaccess' ) );
                        }
@@ -1272,7 +1518,7 @@ class OutputPage {
 
                        // Show templates used by this article
                        $skin = $wgUser->getSkin();
-                       $article = new Article( $wgTitle );
+                       $article = new Article( $this->getTitle() );
                        $this->addHTML( "<div class='templatesUsed'>
 {$skin->formatTemplates( $article->getUsedTemplates() )}
 </div>
@@ -1282,8 +1528,8 @@ class OutputPage {
                # If the title doesn't exist, it's fairly pointless to print a return
                # link to it.  After all, you just tried editing it and couldn't, so
                # what's there to do there?
-               if( $wgTitle->exists() ) {
-                       $this->returnToMain( null, $wgTitle );
+               if( $this->getTitle()->exists() ) {
+                       $this->returnToMain( null, $this->getTitle() );
                }
        }
 
@@ -1356,11 +1602,14 @@ class OutputPage {
         * Add a "return to" link pointing to a specified title
         *
         * @param Title $title Title to link
+        * @param string $query Query string
         */
-       public function addReturnTo( $title ) {
+       public function addReturnTo( $title, $query = array() ) {
                global $wgUser;
-               $link = wfMsg( 'returnto', $wgUser->getSkin()->makeLinkObj( $title ) );
-               $this->addHtml( "<p>{$link}</p>\n" );
+               $this->addLink( array( 'rel' => 'next', 'href' => $title->getFullUrl() ) );
+               $link = wfMsgHtml( 'returnto', $wgUser->getSkin()->link(
+                       $title, null, array(), $query ) );
+               $this->addHTML( "<p id=\"mw-returnto\">{$link}</p>\n" );
        }
 
        /**
@@ -1370,13 +1619,17 @@ class OutputPage {
         * @param null $unused No longer used
         * @param Title $returnto Title to return to
         */
-       public function returnToMain( $unused = null, $returnto = NULL ) {
+       public function returnToMain( $unused = null, $returnto = null, $returntoquery = null ) {
                global $wgRequest;
 
-               if ( $returnto == NULL ) {
+               if ( $returnto == null ) {
                        $returnto = $wgRequest->getText( 'returnto' );
                }
 
+               if ( $returntoquery == null ) {
+                       $returntoquery = $wgRequest->getText( 'returntoquery' );
+               }
+
                if ( '' === $returnto ) {
                        $returnto = Title::newMainPage();
                }
@@ -1390,31 +1643,7 @@ class OutputPage {
                        $titleObj = Title::newMainPage();
                }
 
-               $this->addReturnTo( $titleObj );
-       }
-
-       /**
-        * This function takes the title (first item of mGoodLinks), categories, existing and broken links for the page
-        * and uses the first 10 of them for META keywords
-        *
-        * @param ParserOutput &$parserOutput
-        */
-       private function addKeywords( &$parserOutput ) {
-               global $wgTitle;
-               $this->addKeyword( $wgTitle->getPrefixedText() );
-               $count = 1;
-               $links2d =& $parserOutput->getLinks();
-               if ( !is_array( $links2d ) ) {
-                       return;
-               }
-               foreach ( $links2d as $dbkeys ) {
-                       foreach( $dbkeys as $dbkey => $unused ) {
-                               $this->addKeyword( $dbkey );
-                               if ( ++$count > 10 ) {
-                                       break 2;
-                               }
-                       }
-               }
+               $this->addReturnTo( $titleObj, $returntoquery );
        }
 
        /**
@@ -1423,53 +1652,75 @@ class OutputPage {
        public function headElement( Skin $sk ) {
                global $wgDocType, $wgDTD, $wgContLanguageCode, $wgOutputEncoding, $wgMimeType;
                global $wgXhtmlDefaultNamespace, $wgXhtmlNamespaces;
-               global $wgUser, $wgContLang, $wgUseTrackbacks, $wgTitle, $wgStyleVersion;
+               global $wgContLang, $wgUseTrackbacks, $wgStyleVersion, $wgEnableScriptLoader, $wgHtml5;
 
-               $this->addMeta( "http:Content-type", "$wgMimeType; charset={$wgOutputEncoding}" );
-               $this->addStyle( 'common/wikiprintable.css', 'print' );
+               $this->addMeta( "http:Content-Type", "$wgMimeType; charset={$wgOutputEncoding}" );
+               if ( $sk->commonPrintStylesheet() ) {
+                       $this->addStyle( 'common/wikiprintable.css', 'print' );
+               }
                $sk->setupUserCss( $this );
 
                $ret = '';
 
                if( $wgMimeType == 'text/xml' || $wgMimeType == 'application/xhtml+xml' || $wgMimeType == 'application/xml' ) {
-                       $ret .= "<?xml version=\"1.0\" encoding=\"$wgOutputEncoding\" ?>\n";
+                       $ret .= "<?xml version=\"1.0\" encoding=\"$wgOutputEncoding\" ?" . ">\n";
                }
 
-               $ret .= "<!DOCTYPE html PUBLIC \"$wgDocType\"\n        \"$wgDTD\">\n";
-
                if ( '' == $this->getHTMLTitle() ) {
                        $this->setHTMLTitle(  wfMsg( 'pagetitle', $this->getPageTitle() ));
                }
 
-               $rtl = $wgContLang->isRTL() ? " dir='RTL'" : '';
-               $ret .= "<html xmlns=\"{$wgXhtmlDefaultNamespace}\" ";
-               foreach($wgXhtmlNamespaces as $tag => $ns) {
-                       $ret .= "xmlns:{$tag}=\"{$ns}\" ";
+               $dir = $wgContLang->getDir();
+
+               if ( $wgHtml5 ) {
+                       $ret .= "<!doctype html>\n";
+                       $ret .= "<html lang=\"$wgContLanguageCode\" dir=\"$dir\">\n";
+               } else {
+                       $ret .= "<!DOCTYPE html PUBLIC \"$wgDocType\" \"$wgDTD\">\n";
+                       $ret .= "<html xmlns=\"{$wgXhtmlDefaultNamespace}\" ";
+                       foreach($wgXhtmlNamespaces as $tag => $ns) {
+                               $ret .= "xmlns:{$tag}=\"{$ns}\" ";
+                       }
+                       $ret .= "xml:lang=\"$wgContLanguageCode\" lang=\"$wgContLanguageCode\" dir=\"$dir\">\n";
                }
-               $ret .= "xml:lang=\"$wgContLanguageCode\" lang=\"$wgContLanguageCode\" $rtl>\n";
-               $ret .= "<head>\n<title>" . htmlspecialchars( $this->getHTMLTitle() ) . "</title>\n\t\t";
-               $ret .= implode( "\t\t", array(
+
+               $ret .= "<head>\n";
+               $ret .= "<title>" . htmlspecialchars( $this->getHTMLTitle() ) . "</title>\n";
+               $ret .= implode( "\n", array(
                        $this->getHeadLinks(),
                        $this->buildCssLinks(),
-                       $sk->getHeadScripts( $this->mAllowUserJs ),
-                       $this->mScripts,
+                       $sk->getHeadScripts( $this ),
                        $this->getHeadItems(),
                ));
                if( $sk->usercss ){
-                       $ret .= "<style type='text/css'>{$sk->usercss}</style>";
+                       $ret .= Html::inlineStyle( $sk->usercss );
                }
 
+               if( $wgEnableScriptLoader )
+                       $ret .= $this->getScriptLoaderJs();
+
                if ($wgUseTrackbacks && $this->isArticleRelated())
-                       $ret .= $wgTitle->trackbackRDF();
+                       $ret .= $this->getTitle()->trackbackRDF();
 
                $ret .= "</head>\n";
                return $ret;
        }
-       
+
        protected function addDefaultMeta() {
-               global $wgVersion;
-               $this->addMeta( "generator", "MediaWiki $wgVersion" );
-               
+               global $wgVersion, $wgHtml5;
+
+               static $called = false;
+               if ( $called ) {
+                       # Don't run this twice
+                       return;
+               }
+               $called = true;
+
+               if ( !$wgHtml5 ) {
+                       $this->addMeta( 'http:Content-Style-Type', 'text/css' ); //bug 15835
+               }
+               $this->addMeta( 'generator', "MediaWiki $wgVersion" );
+
                $p = "{$this->mIndexPolicy},{$this->mFollowPolicy}";
                if( $p !== 'index,follow' ) {
                        // http://www.robotstxt.org/wc/meta-user.html
@@ -1479,7 +1730,7 @@ class OutputPage {
 
                if ( count( $this->mKeywords ) > 0 ) {
                        $strip = array(
-                               "/<.*?>/" => '',
+                               "/<.*?" . ">/" => '',
                                "/_/" => ' '
                        );
                        $this->addMeta( 'keywords', preg_replace(array_keys($strip), array_values($strip),implode( ",", $this->mKeywords ) ) );
@@ -1491,12 +1742,12 @@ class OutputPage {
         */
        public function getHeadLinks() {
                global $wgRequest, $wgFeed;
-               
+
                // Ideally this should happen earlier, somewhere. :P
                $this->addDefaultMeta();
-               
+
                $tags = array();
-               
+
                foreach ( $this->mMetatags as $tag ) {
                        if ( 0 == strcasecmp( 'http:', substr( $tag[0], 0, 5 ) ) ) {
                                $a = 'http-equiv';
@@ -1514,7 +1765,6 @@ class OutputPage {
                }
 
                if( $wgFeed ) {
-                       global $wgTitle;
                        foreach( $this->getSyndicationLinks() as $format => $link ) {
                                # Use the page name for the title (accessed through $wgTitle since
                                # there's no other way).  In principle, this could lead to issues
@@ -1524,41 +1774,39 @@ class OutputPage {
                                $tags[] = $this->feedLink(
                                        $format,
                                        $link,
-                                       wfMsg( "page-{$format}-feed", $wgTitle->getPrefixedText() ) ); # Used messages: 'page-rss-feed' and 'page-atom-feed' (for an easier grep)
+                                       wfMsg( "page-{$format}-feed", $this->getTitle()->getPrefixedText() ) ); # Used messages: 'page-rss-feed' and 'page-atom-feed' (for an easier grep)
                        }
 
-                       # Recent changes feed should appear on every page (except recentchanges, 
-                       # that would be redundant). Put it after the per-page feed to avoid 
-                       # changing existing behavior. It's still available, probably via a 
+                       # Recent changes feed should appear on every page (except recentchanges,
+                       # that would be redundant). Put it after the per-page feed to avoid
+                       # changing existing behavior. It's still available, probably via a
                        # menu in your browser. Some sites might have a different feed they'd
                        # like to promote instead of the RC feed (maybe like a "Recent New Articles"
                        # or "Breaking news" one). For this, we see if $wgOverrideSiteFeed is defined.
                        # If so, use it instead.
-                       
-                       global $wgOverrideSiteFeed, $wgSitename;
+
+                       global $wgOverrideSiteFeed, $wgSitename, $wgFeedClasses;
                        $rctitle = SpecialPage::getTitleFor( 'Recentchanges' );
-                       
+
                        if ( $wgOverrideSiteFeed ) {
-                               foreach ( $wgOverrideSiteFeed as $type => $feedUrl ) { 
+                               foreach ( $wgOverrideSiteFeed as $type => $feedUrl ) {
                                        $tags[] = $this->feedLink (
                                                $type,
                                                htmlspecialchars( $feedUrl ),
                                                wfMsg( "site-{$type}-feed", $wgSitename ) );
                                }
                        }
-                       else if ( $wgTitle->getPrefixedText() != $rctitle->getPrefixedText() ) {
-                               $tags[] = $this->feedLink(
-                                       'rss',
-                                       $rctitle->getFullURL( 'feed=rss' ),
-                                       wfMsg( 'site-rss-feed', $wgSitename ) );
-                               $tags[] = $this->feedLink(
-                                       'atom',
-                                       $rctitle->getFullURL( 'feed=atom' ),
-                                       wfMsg( 'site-atom-feed', $wgSitename ) );
+                       else if ( $this->getTitle()->getPrefixedText() != $rctitle->getPrefixedText() ) {
+                               foreach( $wgFeedClasses as $format => $class ) {
+                                       $tags[] = $this->feedLink(
+                                               $format,
+                                               $rctitle->getLocalURL( "feed={$format}" ),
+                                               wfMsg( "site-{$format}-feed", $wgSitename ) ); # For grep: 'site-rss-feed', 'site-atom-feed'.
+                               }
                        }
                }
 
-               return implode( "\n\t\t", $tags ) . "\n";
+               return implode( "\n", $tags ) . "\n";
        }
 
        /**
@@ -1566,7 +1814,7 @@ class OutputPage {
         * @return array associating format keys with URLs
         */
        public function getSyndicationLinks() {
-               global $wgTitle, $wgFeedClasses;
+               global $wgFeedClasses;
                $links = array();
 
                if( $this->isSyndicated() ) {
@@ -1577,7 +1825,7 @@ class OutputPage {
                        }
 
                        foreach( $wgFeedClasses as $format => $class ) {
-                               $links[$format] = $wgTitle->getLocalUrl( "feed=$format{$appendQuery}" );
+                               $links[$format] = $this->getTitle()->getLocalUrl( "feed=$format{$appendQuery}" );
                        }
                }
                return $links;
@@ -1604,6 +1852,8 @@ class OutputPage {
         */
        public function addStyle( $style, $media='', $condition='', $dir='' ) {
                $options = array();
+               // Even though we expect the media type to be lowercase, but here we
+               // force it to lowercase to be safe.
                if( $media )
                        $options['media'] = $media;
                if( $condition )
@@ -1613,6 +1863,14 @@ class OutputPage {
                $this->styles[$style] = $options;
        }
 
+       /**
+        * Adds inline CSS styles
+        * @param $style_css Mixed: inline CSS
+        */
+       public function addInlineStyle( $style_css ){
+               $this->mScripts .= Html::inlineStyle( $style_css );
+       }
+
        /**
         * Build a set of <link>s for the stylesheets specified in the $this->styles array.
         * These will be applied to various media & IE conditionals.
@@ -1625,7 +1883,7 @@ class OutputPage {
                                $links[] = $link;
                }
 
-               return implode( "\n\t\t", $links );
+               return implode( "\n", $links );
        }
 
        protected function styleLink( $style, $options ) {
@@ -1633,7 +1891,7 @@ class OutputPage {
 
                if( isset( $options['dir'] ) ) {
                        global $wgContLang;
-                       $siteDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
+                       $siteDir = $wgContLang->getDir();
                        if( $siteDir != $options['dir'] )
                                return '';
                }
@@ -1644,7 +1902,7 @@ class OutputPage {
                                return '';
                        }
                } else {
-                       $media = '';
+                       $media = null;
                }
 
                if( substr( $style, 0, 1 ) == '/' ||
@@ -1656,15 +1914,7 @@ class OutputPage {
                        $url = $wgStylePath . '/' . $style . '?' . $wgStyleVersion;
                }
 
-               $attribs = array(
-                       'rel' => 'stylesheet',
-                       'href' => $url,
-                       'type' => 'text/css' );
-               if( $media ) {
-                       $attribs['media'] = $media;
-               }
-
-               $link = Xml::element( 'link', $attribs );
+               $link = Html::linkedStyle( $url, $media );
 
                if( isset( $options['condition'] ) ) {
                        $condition = htmlspecialchars( $options['condition'] );
@@ -1711,7 +1961,6 @@ class OutputPage {
         * for when rate limiting has triggered.
         */
        public function rateLimited() {
-               global $wgTitle;
 
                $this->setPageTitle(wfMsg('actionthrottled'));
                $this->setRobotPolicy( 'noindex,follow' );
@@ -1722,7 +1971,7 @@ class OutputPage {
                $this->setStatusCode(503);
                $this->addWikiMsg( 'actionthrottledtext' );
 
-               $this->returnToMain( null, $wgTitle );
+               $this->returnToMain( null, $this->getTitle() );
        }
 
        /**
@@ -1734,6 +1983,15 @@ class OutputPage {
                return $this->mNewSectionLink;
        }
 
+       /**
+       * Forcibly hide the new section link?
+       *
+       * @return bool
+       */
+       public function forceHideNewSectionLink() {
+               return $this->mHideNewSectionLink;
+       }
+
        /**
         * Show a warning about slave lag
         *
@@ -1750,7 +2008,7 @@ class OutputPage {
                                ? 'lag-warn-normal'
                                : 'lag-warn-high';
                        $warning = wfMsgExt( $message, 'parse', $lag );
-                       $this->addHtml( "<div class=\"mw-{$message}\">\n{$warning}\n</div>\n" );
+                       $this->addHTML( "<div class=\"mw-{$message}\">\n{$warning}\n</div>\n" );
                }
        }
 
@@ -1785,7 +2043,7 @@ class OutputPage {
         *
         * In the $wrap, $1 is replaced with the first message, $2 with the second, and so
         * on. The subsequent arguments may either be strings, in which case they are the
-        * message names, or an arrays, in which case the first element is the message name,
+        * message names, or arrays, in which case the first element is the message name,
         * and subsequent elements are the parameters to that message.
         *
         * The special named parameter 'options' in a message specification array is passed
@@ -1819,7 +2077,7 @@ class OutputPage {
                                $args = array();
                                $name = $spec;
                        }
-                       $s = str_replace( '$' . ($n+1), wfMsgExt( $name, $options, $args ), $s );
+                       $s = str_replace( '$' . ( $n + 1 ), wfMsgExt( $name, $options, $args ), $s );
                }
                $this->addHTML( $this->parse( $s, /*linestart*/true, /*uilang*/true ) );
        }