Index: wp-content/themes/twentyeleven/functions.php
===================================================================
--- wp-content/themes/twentyeleven/functions.php (revision 20343)
+++ wp-content/themes/twentyeleven/functions.php (working copy)
@@ -108,6 +108,9 @@
 	// This theme uses Featured Images (also known as post thumbnails) for per-post/per-page Custom Header images
 	add_theme_support( 'post-thumbnails' );
 
+	// Add support for new title system
+	add_theme_support( 'title-tag', array( 'sep' => ' | ' ) );
+
 	// Add support for custom headers.
 	add_theme_support( 'custom-header', array(
 		// The default header text color.
Index: wp-content/themes/twentyeleven/header.php
===================================================================
--- wp-content/themes/twentyeleven/header.php (revision 20343)
+++ wp-content/themes/twentyeleven/header.php (working copy)
@@ -24,27 +24,6 @@
 <head>
 <meta charset="<?php bloginfo( 'charset' ); ?>" />
 <meta name="viewport" content="width=device-width" />
-<title><?php
-	/*
-	 * Print the <title> tag based on what is being viewed.
-	 */
-	global $page, $paged;
-
-	wp_title( '|', true, 'right' );
-
-	// Add the blog name.
-	bloginfo( 'name' );
-
-	// Add the blog description for the home/front page.
-	$site_description = get_bloginfo( 'description', 'display' );
-	if ( $site_description && ( is_home() || is_front_page() ) )
-		echo " | $site_description";
-
-	// Add a page number if necessary:
-	if ( $paged >= 2 || $page >= 2 )
-		echo ' | ' . sprintf( __( 'Page %s', 'twentyeleven' ), max( $paged, $page ) );
-
-	?></title>
 <link rel="profile" href="http://gmpg.org/xfn/11" />
 <link rel="stylesheet" type="text/css" media="all" href="<?php bloginfo( 'stylesheet_url' ); ?>" />
 <link rel="pingback" href="<?php bloginfo( 'pingback_url' ); ?>" />
Index: wp-content/themes/twentyten/functions.php
===================================================================
--- wp-content/themes/twentyten/functions.php (revision 20343)
+++ wp-content/themes/twentyten/functions.php (working copy)
@@ -84,6 +84,9 @@
 	// Add default posts and comments RSS feed links to head
 	add_theme_support( 'automatic-feed-links' );
 
+	// Add support for new title system
+	add_theme_support( 'title-tag', array( 'sep' => ' | ' ) );
+
 	// Make theme available for translation
 	// Translations can be filed in the /languages/ directory
 	load_theme_textdomain( 'twentyten', get_template_directory() . '/languages' );
Index: wp-content/themes/twentyten/header.php
===================================================================
--- wp-content/themes/twentyten/header.php (revision 20343)
+++ wp-content/themes/twentyten/header.php (working copy)
@@ -12,27 +12,6 @@
 <html <?php language_attributes(); ?>>
 <head>
 <meta charset="<?php bloginfo( 'charset' ); ?>" />
-<title><?php
-	/*
-	 * Print the <title> tag based on what is being viewed.
-	 */
-	global $page, $paged;
-
-	wp_title( '|', true, 'right' );
-
-	// Add the blog name.
-	bloginfo( 'name' );
-
-	// Add the blog description for the home/front page.
-	$site_description = get_bloginfo( 'description', 'display' );
-	if ( $site_description && ( is_home() || is_front_page() ) )
-		echo " | $site_description";
-
-	// Add a page number if necessary:
-	if ( $paged >= 2 || $page >= 2 )
-		echo ' | ' . sprintf( __( 'Page %s', 'twentyten' ), max( $paged, $page ) );
-
-	?></title>
 <link rel="profile" href="http://gmpg.org/xfn/11" />
 <link rel="stylesheet" type="text/css" media="all" href="<?php bloginfo( 'stylesheet_url' ); ?>" />
 <link rel="pingback" href="<?php bloginfo( 'pingback_url' ); ?>" />
Index: wp-includes/general-template.php
===================================================================
--- wp-includes/general-template.php (revision 20343)
+++ wp-includes/general-template.php (working copy)
@@ -617,6 +617,200 @@
 }
 
 /**
+ * Display <title> tag with contents
+ *
+ * When using add_theme_support to add support for this function, one can set a few variables:
+ * 'sep' string, for the separator used in title tags
+ * 'home-title' string, for the homepage title. Only used for the blog page when the blog page is also the frontpage.
+ * 'frontpage-title' string, for the front page when it's not the posts page.
+ * 'show-sitename' boolean, defaults to true. When set to false the site name will not be added to titles.
+ *
+ * @since 3.4.0
+ * @access private
+ */
+function _wp_render_title_tag() {
+	if ( ! current_theme_supports( 'title-tag' ) )
+		return;
+
+	// This can only work internally on wp_head.
+	if ( ! did_action( 'wp_head' ) && ( ! function_exists( 'doing_action' ) || ! doing_action( 'wp_head' ) ) )
+		return;
+
+	// If wp_title() has fired, don't do anything.
+	if ( did_action( 'wp_title' ) )
+		return;
+
+	// Allow early filtering, if this returns a title skip all the logic below
+	// and print it.
+	if ( null !== $pre = apply_filters( 'pre_wp_title_tag', null ) ) {
+		echo "<title>$pre</title>\n";
+		return;
+	}
+
+	global $page, $paged;
+
+	// Retrieve the options set by the add_theme_support call. The
+	// title_tag_options_filter filter allows any interested code to modify the
+	// set options easily.
+	$options = get_theme_support( 'title-tag' );
+	$options = apply_filters( 'title_tag_options_filter', $options[0] );
+
+	// ltr language sites tend to use title that has the most-specific part of
+	// the title on the left. rtl language sites tend to reverse this. The auto
+	// option automatically selects the appropriate direction based upon whether
+	// the site is set to use a text direction of ltr or rtl. This can be
+	// directly overridden by supplying ltr or rtl.
+	if ( 'auto' == $options['direction'] || ! in_array( $options['direction'], array( 'ltr', 'rtl' ) ) )
+		$options['direction'] = is_rtl() ? 'rtl' : 'ltr';
+
+	// Set up the PAGING variable.
+	if ( empty( $options['variables']['PAGING'] ) )
+		$options['variables']['PAGING'] = '%1$d';
+	if ( $paged >= 2 || $page >= 2 )
+		$options['variables']['PAGING'] = sprintf( $options['variables']['PAGING'], max( $paged, $page ) );
+	else
+		$options['variables']['PAGING'] = '';
+
+	// Allow for a filter to set the title
+	if ( null !== $title = apply_filters( 'title_tag_filter', null ) ) {
+		if ( is_array($title) )
+			list( $title, $view ) = $title;
+		else
+			$view = 'filter';
+	// Standard home page view
+	} elseif ( is_home() && is_front_page() ) {
+		$title = get_bloginfo('name');
+		$view = 'home';
+	// Static Posts Page
+	} elseif ( is_home() && !is_front_page() ) {
+		$_post = get_post( get_option('page_for_posts') );
+		$title = $_post->post_title;
+		$view = array( 'blog', 'page' );
+	// Static Front Page
+	} elseif ( !is_home() && is_front_page() ) {
+		$title = single_post_title( '', false );
+		$view = array( 'frontpage', 'page' );
+	// Post, Page, Attachment
+	} else if ( is_singular() ) {
+		$title = single_post_title( '', false );
+		$_post = get_queried_object();
+		$view = array( $_post->post_type . '-singular', 'singular' );
+	}
+	// Category or Tag Archive
+	elseif ( is_category() || is_tag() ) {
+		$title = single_term_title( '', false );
+		$term = get_queried_object();
+		$view = array( $term->taxonomy . '-taxonomy-archive', 'taxonomy-archive', 'archive' );
+	}
+	// Taxonomy Archive
+	elseif ( is_tax() ) {
+		$term = get_queried_object();
+		$tax = get_taxonomy( $term->taxonomy );
+		$title = single_term_title( $tax->labels->name, false );
+		$view = array( $term->taxonomy . '-taxonomy-archive', 'taxonomy-archive', 'archive' );
+	}
+	// Author Archive
+	elseif ( is_author() ) {
+		$author = get_queried_object();
+		$title = $author->display_name;
+		$view = array( 'author-archive', 'archive' );
+	}
+	// Post Type Archive
+	elseif ( is_post_type_archive() ) {
+		$title = post_type_archive_title( '', false );
+		$_post = get_queried_object();
+		$view = array( $_post->query_var . '-post-type-archive', 'post-type-archive', 'archive' );
+	}
+	// Date Archive
+	elseif ( is_date() ) {
+		if ( is_year() ) {
+			if ( is_month() )
+				$title = is_day() ? get_the_date() : trim( single_month_title( ' ', false ) );
+			else
+				$title = get_query_var( 'year' );
+		} elseif ( is_month() && !is_day() ) {
+			$title = single_month_title( '', false );
+		}
+		$view = array( 'date-archive', 'archive' );
+	}
+	// Search Results
+	elseif ( is_search() ) {
+		$title = strip_tags( get_query_var('s') );
+		$view = 'search';
+	}
+	// Page Not Found
+	elseif ( is_404() ) {
+		$view = '404';
+	}
+
+	// If a view is set, loop through the views to find the relevant title and
+	// title format.
+	if ( !empty( $view ) ) {
+		foreach ( (array) $view as $cur_view ) {
+			if ( empty( $title_format ) && isset( $options["title_format-$cur_view"] ) )
+				$title_format = $options["title_format-$cur_view"];
+			if ( empty( $options_title ) && isset( $options["title-$cur_view"] ) )
+				$options_title = $options["title-$cur_view"];
+		}
+	}
+
+	// Set the default value if a match was not found.
+	if ( empty( $title_format ) )
+		$title_format = $options['title_format'];
+	if ( empty( $options_title ) )
+		$options_title = $options['title'];
+
+	// If the title from the options contains a printf variable, replace it with
+	// the title generated above and use the result as the new title.
+	$use_options_title = false;
+	if ( ! is_array( $options_title ) )
+		$options_title = array( $options_title );
+	foreach ( $options_title as $index => $options_title_part ) {
+		if ( false !== strpos( $options_title_part, '%1$s' ) ) {
+			$options_title[$index] = sprintf( $options_title_part, $title );
+			$use_options_title = true;
+		}
+	}
+	if ( $use_options_title )
+		$title = $options_title;
+
+	// Flip the title and title format if the direction is rtl.
+	if ( 'rtl' == $options['direction'] ) {
+		if ( is_array($title) )
+			$title = array_reverse( $title );
+		if ( is_array($title_format) )
+			$title_format = array_reverse( $title_format );
+	}
+
+	// Loop through the title parts stored in the title format array. For each
+	// title part, replace any found variables with their relevant value. If any
+	// title part is empty, skip it to prevent duplicate seperators in the final
+	// title.
+	$new_title_format = array();
+	foreach ( (array) $title_format as $title_part ) {
+		foreach ( $options['variables'] as $var => $val )
+			$title_part = str_replace( "%%$var%%", $val, $title_part );
+		if ( !empty( $title_part ) )
+			$new_title_format[] = $title_part;
+	}
+
+	// Merge the title format array with the seperator seperating each title
+	// part.
+	$title_format = implode( $options['sep'], $new_title_format );
+
+	// If the title is an array, merge the parts with the seperator seperating
+	// each part. This allows for complex multi-part view-specific titles.
+	if ( is_array($title) )
+		$title = implode( $options['sep'], $title );
+
+	// Construct the finished title by replacing the %%TITLE%% placeholder
+	// variable with the view-specific title.
+	$title = str_replace( '%%TITLE%%', $title, $title_format );
+
+	echo "<title>" . $title . "</title>\n";
+}
+
+/**
  * Display or retrieve page title for post.
  *
  * This is optimized for single.php template file for displaying the post title.
Index: wp-includes/theme.php
===================================================================
--- wp-includes/theme.php (revision 20343)
+++ wp-includes/theme.php (working copy)
@@ -1328,19 +1328,76 @@
 				define( 'BACKGROUND_IMAGE', $args[0]['default-image'] );
 
 			break;
+
+		case 'title-tag' :
+			if ( ! is_array( $args ) )
+				$args = array( 0 => array() );
+
+			$jit = isset( $args[0]['__jit'] );
+			unset( $args[0]['__jit'] );
+
+			// Merge in data from previous add_theme_support() calls. The first value registered wins.
+			if ( isset( $_wp_theme_features['title-tag'] ) ) {
+				// It is annoying that array_merge_recursive can't work here to allow for new sub-array entries
+				// to replace existing ones rather than create an array structure. It would be nice if the
+				// wp_parse_args function could handle such nested arrays.
+				if ( isset( $_wp_theme_features['title-tag'][0]['variables'] ) && isset( $args[0]['variables'] ) )
+					$title_tag_variables = array_merge( $args[0]['variables'], $_wp_theme_features['title-tag'][0]['variables'] );
+				$args[0] = wp_parse_args( $_wp_theme_features['title-tag'][0], $args[0] );
+				if ( isset( $title_tag_variables ) )
+					$args[0]['variables'] = $title_tag_variables;
+			}
+
+			if ( $jit ) {
+				$defaults = array(
+					'sep'                     => ' - ',
+					'direction'               => 'auto', // auto, rtl, ltr
+					// %%TITLE%% is a special variable but can still be overridden if desired.
+					'title_format'            => array( '%%TITLE%%', '%%PAGING%%', '%%BLOGNAME%%' ),
+					'title_format-home'       => array( '%%BLOGNAME%%', '%%PAGING%%', '%%DESCRIPTION%%' ),
+					'title'                   => '',
+					'title-archive'           => __( 'Archive' ),
+					/* translators: 1: author name */
+					'title-author-archive'    => __( 'Author Archive for %1$s' ),
+					/* translators: 1: date description */
+					'title-date-archive'      => __( 'Archive for %1$s' ),
+					/* translators: 1: post type archive title */
+					'title-post-type-archive' => __( 'Archive for %1$s' ),
+					/* translators: 1: search phrase */
+					'title-search'            => __( 'Search Results for "%1$s"' ),
+					/* translators: 1: taxonomy name */
+					'title-taxonomy-archive'  => __( 'Archive for %1$s' ),
+					'title-404'               => __( 'Page not found' ),
+					'variables'               => array(
+						/* translators: 1: page number */
+						'PAGING'      => __( 'Page %1$d' ),
+						'BLOGNAME'    => get_bloginfo( 'name' ),
+						'DESCRIPTION' => get_bloginfo( 'description' ),
+					),
+				);
+
+				if ( isset( $args[0]['variables'] ) )
+					$title_tag_variables = array_merge( $args[0]['variables'], $defaults['variables'] );
+				$args[0] = wp_parse_args( $args[0], $defaults );
+				if ( isset( $title_tag_variables ) )
+					$args[0]['variables'] = $title_tag_variables;
+			}
+
+			break;
+
 	}
 
 	$_wp_theme_features[ $feature ] = $args;
 }
 
 /**
- * Registers the internal custom header and background routines.
+ * Registers the internal theme feature routines.
  *
  * @since 3.4.0
  * @access private
  */
-function _custom_header_background_just_in_time() {
-	global $custom_image_header, $custom_background;
+function _custom_theme_features_just_in_time() {
+	global $custom_image_header, $custom_background, $title_tag;
 
 	if ( current_theme_supports( 'custom-header' ) ) {
 		// In case any constants were defined after an add_custom_image_header() call, re-run.
@@ -1368,8 +1425,14 @@
 			$custom_background = new Custom_Background( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
 		}
 	}
+
+	if ( current_theme_supports( 'title-tag' ) ) {
+		add_theme_support( 'title-tag', array( '__jit' => true ) );
+
+		add_action( 'wp_head', '_wp_render_title_tag', 0, 0 );
+	}
 }
-add_action( 'wp_loaded', '_custom_header_background_just_in_time' );
+add_action( 'wp_loaded', '_custom_theme_features_just_in_time' );
 
 /**
  * Gets the theme support arguments passed when registering that support
