Index: includes/batch.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/batch.inc,v
retrieving revision 1.20
diff -u -r1.20 batch.inc
--- includes/batch.inc	24 Jun 2008 21:51:02 -0000	1.20
+++ includes/batch.inc	23 Aug 2008 15:56:23 -0000
@@ -76,10 +76,10 @@
 function _batch_progress_page_js() {
   $batch = batch_get();
 
-  // The first batch set gets to set the page title
-  // and the initialization and error messages.
+  // The first batch set gets to set the page title and the initialization and
+  // error messages. Only safe strings should be passed in to batch_set().
   $current_set = _batch_current_set();
-  drupal_set_title($current_set['title']);
+  drupal_set_title($current_set['title'], ALREADY_SANITIZED);
   drupal_add_js('misc/progress.js', 'core', 'header', FALSE, FALSE);
 
   $url = url($batch['url'], array('query' => array('id' => $batch['id'])));
@@ -122,7 +122,7 @@
   $batch =& batch_get();
   $current_set = _batch_current_set();
 
-  drupal_set_title($current_set['title']);
+  drupal_set_title($current_set['title'], ALREADY_SANITIZED);
 
   $new_op = 'do_nojs';
 
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.788
diff -u -r1.788 common.inc
--- includes/common.inc	21 Aug 2008 19:36:36 -0000	1.788
+++ includes/common.inc	23 Aug 2008 15:56:23 -0000
@@ -713,7 +713,7 @@
  *   to escape HTML characters. Use this for any output that's displayed within
  *   a Drupal page.
  *   @code
- *     drupal_set_title($title = t("@name's blog", array('@name' => $account->name)));
+ *     drupal_set_title($title = t("@name's blog", array('@name' => $account->name)), ALREADY_SANITIZED);
  *   @endcode
  *
  * - %variable, which indicates that the string should be HTML escaped and
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.281
diff -u -r1.281 form.inc
--- includes/form.inc	17 Aug 2008 11:08:23 -0000	1.281
+++ includes/form.inc	23 Aug 2008 15:56:24 -0000
@@ -2288,6 +2288,11 @@
  * batch_process();
  * @endcode
  *
+ * Note - if the batch 'title', 'init_message', 'progress_message',
+ * or 'error_message' could contain any user input, it is the responsibility of
+ * the code calling batch_set() to sanitize them first with a function like
+ * check_plain() or filter_xss().
+ *
  * Sample batch operations:
  * @code
  * // Simple and artificial: load a node of a given type for a given user
Index: includes/path.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/path.inc,v
retrieving revision 1.24
diff -u -r1.24 path.inc
--- includes/path.inc	24 Jun 2008 22:12:15 -0000	1.24
+++ includes/path.inc	23 Aug 2008 15:56:24 -0000
@@ -10,6 +10,28 @@
  * executing "drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH);".
  */
 
+
+/**
+ * @name Title filtering flags
+ * @{
+ * Flags for use in drupal_set_title().
+ */
+
+/**
+ * Flag for drupal_set_title(), when title has not been sanitized for xss.
+ */
+define('NOT_SANITIZED', 0);
+
+/**
+ * Flag for drupal_set_title(), when title has already been sanitized.
+ */
+define('ALREADY_SANITIZED', 1);
+
+/**
+ * @} End of "Title filtering flags".
+ */
+
+
 /**
  * Initialize the $_GET['q'] variable to the proper normal path.
  */
@@ -196,15 +218,20 @@
  * @param $title
  *   Optional string value to assign to the page title; or if set to NULL
  *   (default), leaves the current title unchanged.
+ * @param $input
+ *   Optional flag - normally should be left as NOT_SANITIZED. Only set to
+ *   ALREADY_SANITIZED if $title contains HTML and if you have removed any
+ *   possibly dangerous code from $title using a function like check_plain() or
+ *   filter_xss().
  *
  * @return
  *   The updated title of the current page.
  */
-function drupal_set_title($title = NULL) {
+function drupal_set_title($title = NULL, $input = NOT_SANITIZED) {
   static $stored_title;
 
   if (isset($title)) {
-    $stored_title = $title;
+    $stored_title = ($input == ALREADY_SANITIZED) ? $title : check_plain($title);
   }
   return $stored_title;
 }
Index: modules/aggregator/aggregator.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.pages.inc,v
retrieving revision 1.16
diff -u -r1.16 aggregator.pages.inc
--- modules/aggregator/aggregator.pages.inc	16 Aug 2008 21:13:32 -0000	1.16
+++ modules/aggregator/aggregator.pages.inc	23 Aug 2008 15:56:24 -0000
@@ -37,7 +37,7 @@
   // $arg1 is $form_state and $arg2 is $feed. Otherwise, $arg1 is $feed.
   $feed = is_array($arg2) ? $arg2 : $arg1;
   $feed = (object)$feed;
-  drupal_set_title(check_plain($feed->title));
+  drupal_set_title($feed->title);
   $feed_source = theme('aggregator_feed_source', $feed);
 
   // It is safe to include the fid in the query because it's loaded from the
Index: modules/block/block.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.admin.inc,v
retrieving revision 1.19
diff -u -r1.19 block.admin.inc
--- modules/block/block.admin.inc	16 Jul 2008 21:59:25 -0000	1.19
+++ modules/block/block.admin.inc	23 Aug 2008 15:56:24 -0000
@@ -174,7 +174,7 @@
   // Get the block subject for the page title.
   $info = module_invoke($module, 'block', 'list');
   if (isset($info[$delta])) {
-    drupal_set_title(t("'%name' block", array('%name' => $info[$delta]['info'])));
+    drupal_set_title(t("'%name' block", array('%name' => $info[$delta]['info'])), ALREADY_SANITIZED);
   }
 
   // Standard block configurations.
Index: modules/blog/blog.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/blog/blog.pages.inc,v
retrieving revision 1.10
diff -u -r1.10 blog.pages.inc
--- modules/blog/blog.pages.inc	22 May 2008 19:27:13 -0000	1.10
+++ modules/blog/blog.pages.inc	23 Aug 2008 15:56:24 -0000
@@ -12,7 +12,7 @@
 function blog_page_user($account) {
   global $user;
 
-  drupal_set_title($title = t("@name's blog", array('@name' => $account->name)));
+  drupal_set_title($title = t("@name's blog", array('@name' => $account->name)), ALREADY_SANITIZED);
 
   $items = array();
 
Index: modules/book/book.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.admin.inc,v
retrieving revision 1.12
diff -u -r1.12 book.admin.inc
--- modules/book/book.admin.inc	5 Jul 2008 06:00:51 -0000	1.12
+++ modules/book/book.admin.inc	23 Aug 2008 15:56:24 -0000
@@ -70,7 +70,7 @@
  * @ingroup forms.
  */
 function book_admin_edit($form_state, $node) {
-  drupal_set_title(check_plain($node->title));
+  drupal_set_title($node->title);
   $form = array();
   $form['#node'] = $node;
   _book_admin_table($node, $form);
Index: modules/book/book.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.pages.inc,v
retrieving revision 1.7
diff -u -r1.7 book.pages.inc
--- modules/book/book.pages.inc	15 May 2008 21:19:24 -0000	1.7
+++ modules/book/book.pages.inc	23 Aug 2008 15:56:24 -0000
@@ -90,7 +90,7 @@
  * Menu callback; show the outline form for a single node.
  */
 function book_outline($node) {
-  drupal_set_title(check_plain($node->title));
+  drupal_set_title($node->title);
   return drupal_get_form('book_outline_form', $node);
 }
 
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.647
diff -u -r1.647 comment.module
--- modules/comment/comment.module	21 Aug 2008 19:36:36 -0000	1.647
+++ modules/comment/comment.module	23 Aug 2008 15:56:24 -0000
@@ -1419,7 +1419,7 @@
 function comment_form_add_preview($form, &$form_state) {
   global $user;
   $edit = $form_state['values'];
-  drupal_set_title(t('Preview comment'));
+  drupal_set_title(t('Preview comment'), ALREADY_SANITIZED);
   $output = '';
   $node = node_load($edit['nid']);
 
Index: modules/contact/contact.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/contact/contact.pages.inc,v
retrieving revision 1.11
diff -u -r1.11 contact.pages.inc
--- modules/contact/contact.pages.inc	16 Jul 2008 21:59:26 -0000	1.11
+++ modules/contact/contact.pages.inc	23 Aug 2008 15:56:24 -0000
@@ -164,7 +164,7 @@
     $output = t("You cannot send more than %number messages per hour. Please try again later.", array('%number' => variable_get('contact_hourly_threshold', 3)));
   }
   else {
-    drupal_set_title(check_plain($account->name));
+    drupal_set_title($account->name);
     $output = drupal_get_form('contact_mail_user', $account);
   }
 
Index: modules/filter/filter.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/filter/filter.admin.inc,v
retrieving revision 1.13
diff -u -r1.13 filter.admin.inc
--- modules/filter/filter.admin.inc	24 Jul 2008 16:27:51 -0000	1.13
+++ modules/filter/filter.admin.inc	23 Aug 2008 15:56:24 -0000
@@ -94,7 +94,7 @@
  */
 function filter_admin_format_page($format = NULL) {
   if (!isset($format->name)) {
-    drupal_set_title(t("Add input format"));
+    drupal_set_title(t('Add input format'), ALREADY_SANITIZED);
     $format = (object)array('name' => '', 'roles' => '', 'format' => '');
   }
   return drupal_get_form('filter_admin_format_form', $format);
@@ -302,7 +302,7 @@
  * Menu callback; display settings defined by a format's filters.
  */
 function filter_admin_configure_page($format) {
-  drupal_set_title(t("Configure %format", array('%format' => $format->name)));
+  drupal_set_title(t("Configure %format", array('%format' => $format->name)), ALREADY_SANITIZED);
   return drupal_get_form('filter_admin_configure', $format);
 }
 
@@ -343,7 +343,7 @@
  * Menu callback; display form for ordering filters for a format.
  */
 function filter_admin_order_page($format) {
-  drupal_set_title(t("Rearrange %format", array('%format' => $format->name)));
+  drupal_set_title(t("Rearrange %format", array('%format' => $format->name)), ALREADY_SANITIZED);
   return drupal_get_form('filter_admin_order', $format);
 }
 
Index: modules/forum/forum.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v
retrieving revision 1.461
diff -u -r1.461 forum.module
--- modules/forum/forum.module	11 Aug 2008 21:57:40 -0000	1.461
+++ modules/forum/forum.module	23 Aug 2008 15:56:24 -0000
@@ -656,7 +656,7 @@
     }
   }
   drupal_set_breadcrumb($breadcrumb);
-  drupal_set_title(check_plain($title));
+  drupal_set_title($title);
 
   if ($variables['forums_defined'] = count($variables['forums']) || count($variables['parents'])) {
     // Format the "post new content" links listing.
@@ -717,7 +717,7 @@
 
   }
   else {
-    drupal_set_title(t('No forums defined'));
+    drupal_set_title(t('No forums defined'), ALREADY_SANITIZED);
     $variables['links'] = array();
     $variables['forums'] = '';
     $variables['topics'] = '';
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.972
diff -u -r1.972 node.module
--- modules/node/node.module	21 Aug 2008 19:36:37 -0000	1.972
+++ modules/node/node.module	23 Aug 2008 15:56:24 -0000
@@ -1134,7 +1134,7 @@
  */
 function node_show($node, $cid, $message = FALSE) {
   if ($message) {
-    drupal_set_title(t('Revision of %title from %date', array('%title' => $node->title, '%date' => format_date($node->revision_timestamp))));
+    drupal_set_title(t('Revision of %title from %date', array('%title' => $node->title, '%date' => format_date($node->revision_timestamp))), ALREADY_SANITIZED);
   }
   $output = node_view($node, FALSE, TRUE);
 
@@ -1829,7 +1829,7 @@
  * Menu callback; view a single node.
  */
 function node_page_view($node, $cid = NULL) {
-  drupal_set_title(check_plain($node->title));
+  drupal_set_title($node->title);
   return node_show($node, $cid);
 }
 
Index: modules/node/node.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.pages.inc,v
retrieving revision 1.33
diff -u -r1.33 node.pages.inc
--- modules/node/node.pages.inc	22 Aug 2008 12:38:02 -0000	1.33
+++ modules/node/node.pages.inc	23 Aug 2008 15:56:24 -0000
@@ -59,7 +59,7 @@
     // Initialize settings:
     $node = array('uid' => $user->uid, 'name' => (isset($user->name) ? $user->name : ''), 'type' => $type, 'language' => '');
 
-    drupal_set_title(t('Create @name', array('@name' => $types[$type]->name)));
+    drupal_set_title(t('Create @name', array('@name' => $types[$type]->name)), ALREADY_SANITIZED);
     $output = drupal_get_form($type . '_node_form', $node);
   }
 
@@ -380,7 +380,7 @@
       $cloned_node->build_mode = NODE_BUILD_PREVIEW;
       $output = theme('node_preview', $cloned_node);
     }
-    drupal_set_title(t('Preview'));
+    drupal_set_title(t('Preview'), ALREADY_SANITIZED);
 
     return $output;
   }
@@ -503,7 +503,7 @@
  * Generate an overview table of older revisions of a node.
  */
 function node_revision_overview($node) {
-  drupal_set_title(t('Revisions for %title', array('%title' => $node->title)));
+  drupal_set_title(t('Revisions for %title', array('%title' => $node->title)), ALREADY_SANITIZED);
 
   $header = array(t('Revision'), array('data' => t('Operations'), 'colspan' => 2));
 
Index: modules/openid/openid.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/openid/openid.pages.inc,v
retrieving revision 1.6
diff -u -r1.6 openid.pages.inc
--- modules/openid/openid.pages.inc	14 Apr 2008 17:48:38 -0000	1.6
+++ modules/openid/openid.pages.inc	23 Aug 2008 15:56:24 -0000
@@ -28,7 +28,7 @@
  * Menu callback; Manage OpenID identities for the specified user.
  */
 function openid_user_identities($account) {
-  drupal_set_title(check_plain($account->name));
+  drupal_set_title($account->name);
   drupal_add_css(drupal_get_path('module', 'openid') . '/openid.css', 'module');
 
   // Check to see if we got a response
Index: modules/path/path.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/path/path.admin.inc,v
retrieving revision 1.10
diff -u -r1.10 path.admin.inc
--- modules/path/path.admin.inc	21 Aug 2008 19:36:37 -0000	1.10
+++ modules/path/path.admin.inc	23 Aug 2008 15:56:24 -0000
@@ -69,7 +69,7 @@
 function path_admin_edit($pid = 0) {
   if ($pid) {
     $alias = path_load($pid);
-    drupal_set_title(check_plain($alias['dst']));
+    drupal_set_title($alias['dst']);
     $output = drupal_get_form('path_admin_form', $alias);
   }
   else {
Index: modules/poll/poll.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.pages.inc,v
retrieving revision 1.6
diff -u -r1.6 poll.pages.inc
--- modules/poll/poll.pages.inc	15 May 2008 20:55:58 -0000	1.6
+++ modules/poll/poll.pages.inc	23 Aug 2008 15:56:24 -0000
@@ -28,7 +28,7 @@
  * Callback for the 'votes' tab for polls you can see other votes on
  */
 function poll_votes($node) {
-  drupal_set_title(check_plain($node->title));
+  drupal_set_title($node->title);
   $output = t('This table lists all the recorded votes for this poll. If anonymous users are allowed to vote, they will be identified by the IP address of the computer they used when they voted.');
 
   $header[] = array('data' => t('Visitor'), 'field' => 'u.name');
@@ -51,7 +51,7 @@
  * Callback for the 'results' tab for polls you can vote on
  */
 function poll_results($node) {
-  drupal_set_title(check_plain($node->title));
+  drupal_set_title($node->title);
   $node->show_results = TRUE;
   return node_show($node, 0);
 }
Index: modules/profile/profile.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/profile/profile.admin.inc,v
retrieving revision 1.12
diff -u -r1.12 profile.admin.inc
--- modules/profile/profile.admin.inc	21 Aug 2008 19:36:38 -0000	1.12
+++ modules/profile/profile.admin.inc	23 Aug 2008 15:56:24 -0000
@@ -175,7 +175,7 @@
         drupal_not_found();
         return;
       }
-      drupal_set_title(t('edit %title', array('%title' => $edit['title'])));
+      drupal_set_title(t('edit %title', array('%title' => $edit['title'])), ALREADY_SANITIZED);
       $form['fid'] = array('#type' => 'value',
         '#value' => $fid,
       );
@@ -193,7 +193,7 @@
       return;
     }
     $type = $arg;
-    drupal_set_title(t('add new %type', array('%type' => $types[$type])));
+    drupal_set_title(t('add new %type', array('%type' => $types[$type])), ALREADY_SANITIZED);
     $edit = array('name' => 'profile_');
     $form['type'] = array('#type' => 'value', '#value' => $type);
   }
Index: modules/profile/profile.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/profile/profile.pages.inc,v
retrieving revision 1.3
diff -u -r1.3 profile.pages.inc
--- modules/profile/profile.pages.inc	21 Aug 2008 19:36:38 -0000	1.3
+++ modules/profile/profile.pages.inc	23 Aug 2008 15:56:24 -0000
@@ -73,7 +73,7 @@
       $title = check_plain($field->page);
     }
 
-    drupal_set_title($title);
+    drupal_set_title($title, ALREADY_SANITIZED);
     return $output;
   }
   else if ($name && !$field->fid) {
@@ -99,7 +99,7 @@
     $output = theme('profile_wrapper', $content);
     $output .= theme('pager', NULL, 20);
 
-    drupal_set_title(t('User list'));
+    drupal_set_title(t('User list'), ALREADY_SANITIZED);
     return $output;
   }
 }
Index: modules/statistics/statistics.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/statistics/statistics.admin.inc,v
retrieving revision 1.9
diff -u -r1.9 statistics.admin.inc
--- modules/statistics/statistics.admin.inc	21 Aug 2008 19:36:38 -0000	1.9
+++ modules/statistics/statistics.admin.inc	23 Aug 2008 16:00:39 -0000
@@ -64,7 +64,7 @@
     $rows[] = array(array('data' => t('No statistics available.'), 'colspan' => 4));
   }
 
-  drupal_set_title(t('Top pages in the past %interval', array('%interval' => format_interval(variable_get('statistics_flush_accesslog_timer', 259200)))));
+  drupal_set_title(t('Top pages in the past %interval', array('%interval' => format_interval(variable_get('statistics_flush_accesslog_timer', 259200)))), ALREADY_SANITIZED);
   $output = theme('table', $header, $rows);
   $output .= theme('pager', NULL, 30, 0);
   return $output;
@@ -97,7 +97,7 @@
     $rows[] = array(array('data' => t('No statistics available.'), 'colspan' => 4));
   }
 
-  drupal_set_title(t('Top visitors in the past %interval', array('%interval' => format_interval(variable_get('statistics_flush_accesslog_timer', 259200)))));
+  drupal_set_title(t('Top visitors in the past %interval', array('%interval' => format_interval(variable_get('statistics_flush_accesslog_timer', 259200)))), ALREADY_SANITIZED);
   $output = theme('table', $header, $rows);
   $output .= theme('pager', NULL, 30, 0);
   return $output;
@@ -109,7 +109,7 @@
 function statistics_top_referrers() {
   $query = "SELECT url, COUNT(url) AS hits, MAX(timestamp) AS last FROM {accesslog} WHERE url NOT LIKE :host AND url <> '' GROUP BY url";
   $query_cnt = "SELECT COUNT(DISTINCT(url)) FROM {accesslog} WHERE url <> '' AND url NOT LIKE :host";
-  drupal_set_title(t('Top referrers in the past %interval', array('%interval' => format_interval(variable_get('statistics_flush_accesslog_timer', 259200)))));
+  drupal_set_title(t('Top referrers in the past %interval', array('%interval' => format_interval(variable_get('statistics_flush_accesslog_timer', 259200)))), ALREADY_SANITIZED);
 
   $header = array(
     array('data' => t('Hits'), 'field' => 'hits', 'sort' => 'desc'),
Index: modules/statistics/statistics.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/statistics/statistics.pages.inc,v
retrieving revision 1.3
diff -u -r1.3 statistics.pages.inc
--- modules/statistics/statistics.pages.inc	14 Apr 2008 17:48:41 -0000	1.3
+++ modules/statistics/statistics.pages.inc	23 Aug 2008 15:56:24 -0000
@@ -29,7 +29,7 @@
       $rows[] = array(array('data' => t('No statistics available.'), 'colspan' => 4));
     }
 
-    drupal_set_title(check_plain($node->title));
+    drupal_set_title($node->title);
     $output = theme('table', $header, $rows);
     $output .= theme('pager', NULL, 30, 0);
     return $output;
@@ -60,7 +60,7 @@
       $rows[] = array(array('data' => t('No statistics available.'), 'colspan' => 3));
     }
 
-    drupal_set_title(check_plain($account->name));
+    drupal_set_title($account->name);
     $output = theme('table', $header, $rows);
     $output .= theme('pager', NULL, 30, 0);
     return $output;
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.613
diff -u -r1.613 system.module
--- modules/system/system.module	21 Aug 2008 19:36:38 -0000	1.613
+++ modules/system/system.module	23 Aug 2008 15:56:24 -0000
@@ -1270,6 +1270,11 @@
  * confirmed the action. You should never directly inspect $_POST to see if an
  * action was confirmed.
  *
+ * Note - if the parameters $question, $description, $yes, or $no could contain
+ * any user input (such as node titles or taxonomy terms), it is the
+ * responsibility of the code calling confirm_form() to sanitize them first with
+ * a function like check_plain() or filter_xss().
+ *
  * @ingroup forms
  * @param $form
  *   Additional elements to inject into the form, for example hidden elements.
@@ -1303,7 +1308,7 @@
   }
   $cancel = l($no ? $no : t('Cancel'), $path, array('query' => $query, 'fragment' => $fragment));
 
-  drupal_set_title($question);
+  drupal_set_title($question, ALREADY_SANITIZED);
 
   // Confirm form fails duplication check, as the form values rarely change -- so skip it.
   $form['#skip_duplicate_check'] = TRUE;
Index: modules/system/system.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.test,v
retrieving revision 1.10
diff -u -r1.10 system.test
--- modules/system/system.test	22 Aug 2008 12:35:55 -0000	1.10
+++ modules/system/system.test	23 Aug 2008 15:56:24 -0000
@@ -320,3 +320,68 @@
     $this->assertRaw($string, t('Fingerprinting meta tag generated correctly.'), t('System'));
   }
 }
+
+class PageTitleFiltering extends DrupalWebTestCase {
+  protected $content_user;
+  protected $saved_title;
+
+  /**
+   * Implementation of getInfo().
+   */
+  function getInfo() {
+    return array(
+      'name' => t('HTML in page titles'),
+      'description' => t('Tests correct handling or conversion by drupal_set_title() and drupal_get_title().'),
+      'group' => t('System')
+    );
+  }
+
+  /**
+   * Implementation of setUp().
+   */
+  function setUp() {
+    parent::setUp();
+
+    $this->content_user = $this->drupalCreateUser(array('create page content', 'access content'));
+    $this->drupalLogin($this->content_user);
+    $this->saved_title = drupal_get_title();
+  }
+
+  /**
+   * Reset page title.
+   */
+  function tearDown() {
+    // Restore the page title.
+    drupal_set_title($this->saved_title, ALREADY_SANITIZED);
+
+    parent::tearDown();
+  }
+
+  /**
+   * Tests the handling of HTML by drupal_set_title() and drupal_get_title()
+   */
+  function testTitleTags() {
+     $title = "string with <em>HTML</em>";
+     // drupal_set_title's $filter is NOT_SANITIZED by default, so the title should be
+     // returned with check_plain().
+     drupal_set_title($title);
+     $this->assertTrue(strpos(drupal_get_title(), '<em>') === FALSE, t('Tags in title converted to entities when $input is NOT_SANITIZED.'));
+     // drupal_set_title's $filter is passed as ALREADY_SANITIZED, so the title should be
+     // returned with HTML.
+     drupal_set_title($title, ALREADY_SANITIZED);
+     $this->assertTrue(strpos(drupal_get_title(), '<em>') !== FALSE, t('Tags in title are not converted to entities when $input is ALREADY_SANITIZED.'));
+     // Generate node content.
+     $edit = array(
+       'title' => '!SimpleTest! ' . $title . $this->randomName(20),
+       'body' => '!SimpleTest! test body' . $this->randomName(200),
+     );
+    // Create the node with HTML in the title.
+    $this->drupalPost('node/add/page', $edit, t('Save'));
+
+    $node = node_load(array('title' => $edit['title']));
+    $this->assertNotNull($node, 'Node created and found in database');
+    $this->drupalGet("node/" . $node->nid);
+    $this->assertText(check_plain($edit['title']), 'Check to make sure tags in the node title are converted.');
+  }
+}
+
Index: modules/taxonomy/taxonomy.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.admin.inc,v
retrieving revision 1.27
diff -u -r1.27 taxonomy.admin.inc
--- modules/taxonomy/taxonomy.admin.inc	16 Jul 2008 21:59:28 -0000	1.27
+++ modules/taxonomy/taxonomy.admin.inc	23 Aug 2008 15:56:24 -0000
@@ -258,7 +258,7 @@
     return taxonomy_vocabulary_confirm_reset_alphabetical($form_state, $vocabulary->vid);
   }
 
-  drupal_set_title(t('Terms in %vocabulary', array('%vocabulary' => $vocabulary->name)));
+  drupal_set_title(t('Terms in %vocabulary', array('%vocabulary' => $vocabulary->name)), ALREADY_SANITIZED);
   $form = array(
     '#vocabulary' => (array)$vocabulary,
     '#tree' => TRUE,
@@ -628,7 +628,7 @@
  * Menu callback; return the edit form for a new term after setting the title.
  */
 function taxonomy_add_term_page($vocabulary) {
-  drupal_set_title(t('Add term to %vocabulary', array('%vocabulary' => $vocabulary->name)));
+  drupal_set_title(t('Add term to %vocabulary', array('%vocabulary' => $vocabulary->name)), ALREADY_SANITIZED);
   return drupal_get_form('taxonomy_form_term' , $vocabulary);
 }
 
Index: modules/taxonomy/taxonomy.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.pages.inc,v
retrieving revision 1.12
diff -u -r1.12 taxonomy.pages.inc
--- modules/taxonomy/taxonomy.pages.inc	21 Aug 2008 19:36:38 -0000	1.12
+++ modules/taxonomy/taxonomy.pages.inc	23 Aug 2008 15:56:24 -0000
@@ -26,7 +26,7 @@
 
     if ($names) {
       $title = check_plain(implode(', ', $names));
-      drupal_set_title($title);
+      drupal_set_title($title, ALREADY_SANITIZED);
 
       switch ($op) {
         case 'page':
Index: modules/tracker/tracker.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/tracker/tracker.pages.inc,v
retrieving revision 1.6
diff -u -r1.6 tracker.pages.inc
--- modules/tracker/tracker.pages.inc	14 Apr 2008 17:48:42 -0000	1.6
+++ modules/tracker/tracker.pages.inc	23 Aug 2008 15:56:24 -0000
@@ -19,7 +19,7 @@
       // When viewed from user/%user/track, display the name of the user
       // as page title -- the tab title remains Track so this needs to be done
       // here and not in the menu definiton.
-      drupal_set_title(check_plain($account->name));
+      drupal_set_title($account->name);
     }
   // TODO: These queries are very expensive, see http://drupal.org/node/105639
     $sql = 'SELECT DISTINCT(n.nid), n.title, n.type, n.changed, n.uid, u.name, GREATEST(n.changed, l.last_comment_timestamp) AS last_updated, l.comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {users} u ON n.uid = u.uid LEFT JOIN {comments} c ON n.nid = c.nid AND (c.status = %d OR c.status IS NULL) WHERE n.status = 1 AND (n.uid = %d OR c.uid = %d) ORDER BY last_updated DESC';
Index: modules/translation/translation.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/translation/translation.pages.inc,v
retrieving revision 1.3
diff -u -r1.3 translation.pages.inc
--- modules/translation/translation.pages.inc	14 Apr 2008 17:48:42 -0000	1.3
+++ modules/translation/translation.pages.inc	23 Aug 2008 15:56:24 -0000
@@ -54,6 +54,6 @@
     $rows[] = array($language_name, $title, $status, implode(" | ", $options));
   }
 
-  drupal_set_title(t('Translations of %title', array('%title' => $node->title)));
+  drupal_set_title(t('Translations of %title', array('%title' => $node->title)), ALREADY_SANITIZED);
   return theme('table', $header, $rows);
 }
Index: modules/user/user.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.pages.inc,v
retrieving revision 1.15
diff -u -r1.15 user.pages.inc
--- modules/user/user.pages.inc	21 Aug 2008 19:36:39 -0000	1.15
+++ modules/user/user.pages.inc	23 Aug 2008 15:56:24 -0000
@@ -147,7 +147,7 @@
  * Menu callback; Displays a user or user profile page.
  */
 function user_view($account) {
-  drupal_set_title(check_plain($account->name));
+  drupal_set_title($account->name);
   // Retrieve all profile fields and attach to $account->content.
   user_build_content($account);
 
@@ -218,7 +218,7 @@
  * @see user_edit_submit()
  */
 function user_edit($account, $category = 'account') {
-  drupal_set_title(check_plain($account->name));
+  drupal_set_title($account->name);
   return drupal_get_form('user_profile_form', $account, $category);
 }
 
