Index: modules/block/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.module,v
retrieving revision 1.308
diff -u -p -r1.308 block.module
--- modules/block/block.module	17 Jul 2008 21:10:39 -0000	1.308
+++ modules/block/block.module	2 Aug 2008 03:34:49 -0000
@@ -207,7 +207,7 @@ function block_block($op = 'list', $delt
 
     case 'view':
       $block = db_fetch_object(db_query('SELECT body, format FROM {boxes} WHERE bid = %d', $delta));
-      $data['content'] = check_markup($block->body, $block->format, FALSE);
+      $data['content'] = check_markup($block->body, $block->format, 'block', $block, FALSE);
       return $data;
   }
 }
Index: modules/book/book.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.test,v
retrieving revision 1.3
diff -u -p -r1.3 book.test
--- modules/book/book.test	15 May 2008 21:19:24 -0000	1.3
+++ modules/book/book.test	2 Aug 2008 04:33:42 -0000
@@ -108,7 +108,7 @@ class BookTestCase extends DrupalWebTest
     $this->drupalGet('book/export/html/' . $node->nid);
     $this->assertText($node->title, t('Printer friendly title found.'));
     $node->body = str_replace('<!--break-->', '', $node->body);
-    $this->assertRaw(check_markup($node->body, $node->format), t('Printer friendly body found.'));
+    $this->assertRaw(check_markup($node->body, $node->format, 'node', $node), t('Printer friendly body found.'));
 
     $number++;
   }
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.643
diff -u -p -r1.643 comment.module
--- modules/comment/comment.module	16 Jul 2008 21:59:26 -0000	1.643
+++ modules/comment/comment.module	2 Aug 2008 03:37:44 -0000
@@ -583,7 +583,7 @@ function comment_nodeapi(&$node, $op, $a
       $text = '';
       $comments = db_query('SELECT subject, comment, format FROM {comments} WHERE nid = %d AND status = %d', $node->nid, COMMENT_PUBLISHED);
       while ($comment = db_fetch_object($comments)) {
-        $text .= '<h2>' . check_plain($comment->subject) . '</h2>' . check_markup($comment->comment, $comment->format, FALSE);
+        $text .= '<h2>' . check_plain($comment->subject) . '</h2>' . check_markup($comment->comment, $comment->format, 'comment', $comment, FALSE);
       }
       return $text;
 
@@ -1522,7 +1522,7 @@ function _comment_form_submit(&$comment_
     // 2) Strip out all HTML tags
     // 3) Convert entities back to plain-text.
     // Note: format is checked by check_markup().
-    $comment_values['subject'] = trim(truncate_utf8(decode_entities(strip_tags(check_markup($comment_values['comment'], $comment_values['format']))), 29, TRUE));
+    $comment_values['subject'] = trim(truncate_utf8(decode_entities(strip_tags(check_markup($comment_values['comment'], $comment_values['format'], 'comment', (object)$comment_values))), 29, TRUE));
     // Edge cases where the comment body is populated only by HTML tags will
     // require a default subject.
     if ($comment_values['subject'] == '') {
@@ -1574,7 +1574,7 @@ function theme_comment_view($comment, $n
 
   // Switch to folded/unfolded view of the comment.
   if ($visible) {
-    $comment->comment = check_markup($comment->comment, $comment->format, FALSE);
+    $comment->comment = check_markup($comment->comment, $comment->format, 'comment', $comment, FALSE);
     // Comment API hook.
     comment_invoke_comment($comment, 'view');
     $output .= theme('comment', $comment, $node, $links);
Index: modules/filter/filter.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v
retrieving revision 1.217
diff -u -p -r1.217 filter.module
--- modules/filter/filter.module	24 Jul 2008 16:25:17 -0000	1.217
+++ modules/filter/filter.module	2 Aug 2008 03:42:11 -0000
@@ -406,20 +406,33 @@ function filter_list_format($format) {
  * @param $format
  *    The format of the text to be filtered. Specify FILTER_FORMAT_DEFAULT for
  *    the default format.
+ * @param $type
+ *    The type of context in which this filter is being applied - for example,
+ *    for nodes this will be 'node'; for comments this will be 'comment'.
+ * @param $context
+ *    An arbitrary value that represents the context in which the filter is
+ *    being applied. For example, for node filtering, it will contain the node
+ *    object; for comment filtering, the comment object; for box filtering, the
+ *    block object. Defaults to NULL if not specified.
  * @param $check
- *    Whether to check the $format with filter_access() first. Defaults to TRUE.
- *    Note that this will check the permissions of the current user, so you
- *    should specify $check = FALSE when viewing other people's content. When
- *    showing content that is not (yet) stored in the database (eg. upon preview),
- *    set to TRUE so the user's permissions are checked.
+ *    Whether to check the $format with filter_access() first. Defaults to
+ *    TRUE. Note that this will check the permissions of the current user, so
+ *    you should specify $check = FALSE when viewing other people's content.
+ *    When showing content that is not (yet) stored in the database (eg. upon
+ *    preview), set to TRUE so the user's permissions are checked.
+ * @return
+ *    The filtered text, run through the specified format with the specified
+ *    context, or t('n/a') on a failed access check or NULL text.
  */
-function check_markup($text, $format = FILTER_FORMAT_DEFAULT, $check = TRUE) {
+function check_markup($text, $format = FILTER_FORMAT_DEFAULT, $type = '', $context = NULL, $check = TRUE) {
   // When $check = TRUE, do an access check on $format.
   if (isset($text) && (!$check || filter_access($format))) {
     $format = filter_resolve_format($format);
 
     // Check for a cached version of this piece of text.
-    $cache_id = $format . ':' . md5($text);
+    // This may change with the context, so invalidate this if the context
+    // or type changes as well.
+    $cache_id = $format . ':' . md5($text) . ':' . md5($type) . ':' . md5(serialize($context));
     if ($cached = cache_get($cache_id, 'cache_filter')) {
       return $cached->data;
     }
@@ -436,12 +449,12 @@ function check_markup($text, $format = F
 
     // Give filters the chance to escape HTML-like data such as code or formulas.
     foreach ($filters as $filter) {
-      $text = module_invoke($filter->module, 'filter', 'prepare', $filter->delta, $format, $text, $cache_id);
+      $text = module_invoke($filter->module, 'filter', 'prepare', $filter->delta, $format, $text, $type, $context, $cache_id);
     }
 
     // Perform filtering.
     foreach ($filters as $filter) {
-      $text = module_invoke($filter->module, 'filter', 'process', $filter->delta, $format, $text, $cache_id);
+      $text = module_invoke($filter->module, 'filter', 'process', $filter->delta, $format, $text, $type, $context, $cache_id);
     }
 
     // Store in cache with a minimum expiration time of 1 day.
@@ -596,7 +609,7 @@ function theme_filter_tips_more_info() {
  * - URL and e-mail address filter:
  *     Converts newlines into paragraph and break tags.
  */
-function filter_filter($op, $delta = 0, $format = -1, $text = '') {
+function filter_filter($op, $delta = 0, $format = -1, $text = '', $type = '', $context = NULL) {
   switch ($op) {
     case 'list':
       return array(0 => t('Limit allowed HTML tags'), 1 => t('Convert line breaks'), 2 => t('Convert URLs into links'), 3 => t('Correct broken HTML'), 4 => t('Escape all HTML'));
Index: modules/filter/filter.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/filter/filter.test,v
retrieving revision 1.5
diff -u -p -r1.5 filter.test
--- modules/filter/filter.test	6 Jun 2008 10:36:43 -0000	1.5
+++ modules/filter/filter.test	2 Aug 2008 05:11:47 -0000
@@ -352,3 +352,119 @@ END;
     }
   }
 }
+
+class FilterContextTestCase extends DrupalWebTestCase {
+
+  /**
+   * Implementation of getInfo().
+   */
+  function getInfo() {
+    return array(
+      'name' => t('Filter contexts'),
+      'description' => t('Checks that the filter context system works as expected, and filters are able to use the system properly.'),
+      'group' => t('Filter'),
+    );
+  }
+
+  /**
+   * Set up our test filter.
+   */
+  function setUp() {
+    // @TODO - once SimpleTest can create fake modules, remove this slightly
+    // hackish code.
+    parent::setUp();
+    db_query('DELETE FROM {filters} WHERE format = %d', filter_resolve_format(FILTER_FORMAT_DEFAULT));
+    db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, 'testing_hook', 0, 0)", filter_resolve_format(FILTER_FORMAT_DEFAULT));
+  }
+
+  /**
+   * Prepare a series of test cases for filter contexts.
+   */
+  function prepareCases() {
+    $cases = array();
+
+    $cases[] = array(
+      'text' => 'Test 1',
+      'expected' => 'Test 1',
+    );
+
+    $cases[] = array(
+      'text' => 'Test 2',
+      'type' => 'test_2',
+      'expected' => 'Test 2Test 2',
+    );
+
+    $cases[] = array(
+      'text' => 'Test 3',
+      'type' => 'test_3',
+      'expected' => 'Test !3!',
+    );
+
+    $cases[] = array(
+      'text' => 'Test 4',
+      'type' => 'test_4',
+      'context' => 'Context 4',
+      'expected' => 'Context 4',
+    );
+
+    $cases[] = array(
+      'text' => 'Test 5',
+      'type' => 'test_5',
+      'context' => array('Test 5' => 'Awesome 5'),
+      'expected' => 'Awesome 5',
+    );
+
+    return $cases;
+  }
+
+  /**
+   * Test the filter context API.
+   */
+  function testFilterContexts() {
+    $cases = $this->prepareCases();
+    foreach ($cases as $case) {
+      $case += array('type' => '', 'context' => NULL);
+      $result = check_markup($case['text'], FILTER_FORMAT_DEFAULT, $case['type'], $case['context'], FALSE);
+      $this->assertEqual($result, $case['expected'], t('Expected @expected, got @actual.', array('@actual' => $result, '@expected' => $case['expected'])));
+      $cache = cache_get(filter_resolve_format(FILTER_FORMAT_DEFAULT) . ':' . md5($case['text']) . ':' . md5($case['type']) . ':' . md5(serialize($case['context'])), 'cache_filter');
+      $this->assertEqual($cache->data, $result, t('Expected @result to be cached, cached value was @cached.', array('@result' => $result, '@cached' => $cache->data)));
+    }
+  }
+}
+
+/**
+ * Helper function for FilterContextTestCase. Pretends to implement
+ * hook_filter() for testing purposes.
+ */
+function testing_hook_filter($op, $delta = 0, $format = -1, $text = '', $type = '', $context = NULL) {
+  switch ($op) {
+    case 'list':
+      return array(0 => t('Test filter'));
+
+    case 'no cache':
+      // We want caching, as we will test that too.
+      return FALSE;
+
+    case 'description':
+      return t('This filter is a test filter that only exists for the purposes of testing.');
+
+    case 'process':
+      switch ($type) {
+        case 'test_2':
+          return $text . $text;
+
+        case 'test_3':
+          return str_replace('3', '!3!', $text);
+
+        case 'test_4':
+          return $context;
+
+        case 'test_5':
+          return $context[$text];
+      }
+      // Deliberate fall-through.
+
+    default:
+      return $text;
+  }
+}
\ No newline at end of file
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.969
diff -u -p -r1.969 node.module
--- modules/node/node.module	24 Jul 2008 16:25:18 -0000	1.969
+++ modules/node/node.module	2 Aug 2008 03:38:57 -0000
@@ -1067,10 +1067,10 @@ function node_prepare($node, $teaser = F
   $node->readmore = (strlen($node->teaser) < strlen($node->body));
 
   if ($teaser == FALSE) {
-    $node->body = check_markup($node->body, $node->format, FALSE);
+    $node->body = check_markup($node->body, $node->format, 'node', $node, FALSE);
   }
   else {
-    $node->teaser = check_markup($node->teaser, $node->format, FALSE);
+    $node->teaser = check_markup($node->teaser, $node->format, 'node', $node, FALSE);
   }
 
   $node->content['body'] = array(
Index: modules/php/php.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/php/php.module,v
retrieving revision 1.10
diff -u -p -r1.10 php.module
--- modules/php/php.module	14 Apr 2008 17:48:41 -0000	1.10
+++ modules/php/php.module	2 Aug 2008 03:41:46 -0000
@@ -69,7 +69,7 @@ else {
  *
  * Executes PHP code. Use with care.
  */
-function php_filter($op, $delta = 0, $format = -1, $text = '') {
+function php_filter($op, $delta = 0, $format = -1, $text = '', $type = '', $context = NULL) {
   switch ($op) {
     case 'list':
       return array(0 => t('PHP evaluator'));
Index: modules/profile/profile.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/profile/profile.module,v
retrieving revision 1.243
diff -u -p -r1.243 profile.module
--- modules/profile/profile.module	24 Jul 2008 16:25:18 -0000	1.243
+++ modules/profile/profile.module	2 Aug 2008 03:40:00 -0000
@@ -252,7 +252,7 @@ function profile_view_field($user, $fiel
   if (isset($user->{$field->name}) && $value = $user->{$field->name}) {
     switch ($field->type) {
       case 'textarea':
-        return check_markup($value);
+        return check_markup($value, FILTER_FORMAT_DEFAULT, 'user', $user);
       case 'textfield':
       case 'selection':
         return $browse ? l($value, 'profile/' . $field->name . '/' . $value) : check_plain($value);
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.912
diff -u -p -r1.912 user.module
--- modules/user/user.module	16 Jul 2008 21:59:29 -0000	1.912
+++ modules/user/user.module	2 Aug 2008 03:40:39 -0000
@@ -1990,7 +1990,7 @@ function user_comment(&$comment, $op) {
   // Validate signature.
   if ($op == 'view') {
     if (variable_get('user_signatures', 0) && !empty($comment->signature)) {
-      $comment->signature = check_markup($comment->signature, $comment->format);
+      $comment->signature = check_markup($comment->signature, $comment->format, 'comment', $comment);
     }
     else {
       $comment->signature = '';
