Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1022
diff -u -p -r1.1022 common.inc
--- includes/common.inc	16 Oct 2009 13:18:30 -0000	1.1022
+++ includes/common.inc	16 Oct 2009 13:48:53 -0000
@@ -3292,7 +3292,7 @@ function drupal_clear_css_cache() {
  * @return
  *   The cleaned identifier.
  */
-function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_' => '-', '[' => '-', ']' => '')) {
+function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_' => '-', '/' => '-', '[' => '-', ']' => '')) {
   // By default, we filter using Drupal's coding standards.
   $identifier = strtr($identifier, $filter);
 
Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.353
diff -u -p -r1.353 menu.inc
--- includes/menu.inc	15 Oct 2009 14:07:26 -0000	1.353
+++ includes/menu.inc	16 Oct 2009 15:45:48 -0000
@@ -1578,6 +1578,9 @@ function menu_navigation_links($menu_nam
  *
  * @param $level
  *   The level of tasks you ask for. Primary tasks are 0, secondary are 1.
+ * @param $path
+ *   (optional) A menu link path to collect tabs and actions for.
+ *
  * @return
  *   An array containing
  *   - tabs: Local tasks for the requested level:
@@ -1589,25 +1592,28 @@ function menu_navigation_links($menu_nam
  *   - root_path: The router path for the current page. If the current page is
  *     a default local task, then this corresponds to the parent tab.
  */
-function menu_local_tasks($level = 0) {
+function menu_local_tasks($level = 0, $path = NULL) {
   $data = &drupal_static(__FUNCTION__);
-  $root_path = &drupal_static(__FUNCTION__ . ':root_path', '');
+
   $empty = array(
     'tabs' => array('count' => 0, 'output' => array()),
     'actions' => array('count' => 0, 'output' => array()),
-    'root_path' => &$root_path,
+    'root_path' => '',
   );
 
-  if (!isset($data)) {
-    $data = array();
+  $router_item = menu_get_item($path);
+  if (!$router_item || !$router_item['access']) {
+    return $empty;
+  }
+  $root_path = $router_item['path'];
+  $empty['root_path'] = $root_path;
+
+  if (!isset($data[$root_path])) {
+    $data[$root_path] = $empty;
     // Set defaults in case there are no actions or tabs.
     $actions = $empty['actions'];
     $tabs = array();
 
-    $router_item = menu_get_item();
-    if (!$router_item || !$router_item['access']) {
-      return $empty;
-    }
     // Get all tabs and the root page.
     $result = db_select('menu_router', NULL, array('fetch' => PDO::FETCH_ASSOC))
       ->fields('menu_router')
@@ -1615,10 +1621,9 @@ function menu_local_tasks($level = 0) {
       ->orderBy('weight')
       ->orderBy('title')
       ->execute();
-    $map = arg();
+    $map = $router_item['original_map'];
     $children = array();
     $tasks = array();
-    $root_path = $router_item['path'];
 
     foreach ($result as $item) {
       _menu_translate($item, $map, TRUE);
@@ -1682,7 +1687,7 @@ function menu_local_tasks($level = 0) {
       $actions['output'] = $actions_current;
       $depth++;
     }
-    $data['actions'] = $actions;
+    $data[$root_path]['actions'] = $actions;
     // Find all tabs at the same level or above the current one.
     $parent = $router_item['tab_parent'];
     $path = $router_item['path'];
@@ -1706,7 +1711,7 @@ function menu_local_tasks($level = 0) {
             // Use the path of the parent instead.
             $link['href'] = $tasks[$p]['href'];
             if ($item['path'] == $router_item['path']) {
-              $root_path = $tasks[$p]['path'];
+              $data[$root_path]['root_path'] = $tasks[$p]['path'];
             }
           }
           // We check for the active tab.
@@ -1739,23 +1744,80 @@ function menu_local_tasks($level = 0) {
     ksort($tabs);
     // Remove the depth, we are interested only in their relative placement.
     $tabs = array_values($tabs);
-    $data['tabs'] = $tabs;
+    $data[$root_path]['tabs'] = $tabs;
 
     // Allow modules to alter local tasks or dynamically append further tasks.
-    drupal_alter('menu_local_tasks', $data, $router_item, $root_path);
+    drupal_alter('menu_local_tasks', $data[$root_path], $router_item, $root_path);
   }
 
-  if (isset($data['tabs'][$level])) {
+  if (isset($data[$root_path]['tabs'][$level])) {
     return array(
-      'tabs' => $data['tabs'][$level],
-      'actions' => $data['actions'],
-      'root_path' => $root_path,
+      'tabs' => $data[$root_path]['tabs'][$level],
+      'actions' => $data[$root_path]['actions'],
+      'root_path' => $data[$root_path]['root_path'],
     );
   }
   return $empty;
 }
 
 /**
+ * Retrieve contextual links for a system object based on registered local tasks.
+ *
+ * This leverages menu_local_tasks() to retrieve the first layer of registered
+ * local tasks in the menu system for a given system object. For example, if the
+ * path "node/123" is passed and considering the following menu router items
+ * using the 'type' MENU_LOCAL_TASK or MENU_DEFAULT_LOCAL_TASK:
+ * - node/%node/view (default local task)
+ * - node/%node/edit
+ * - node/%node/load
+ * - node/%node/render
+ * Then this function will return the 'edit', 'load', and 'render' as contextual
+ * links.
+ * menu_local_tasks() will translate the given link path into the corresponding
+ * menu router item (here: "node/%") and only retrieve and prepare the local
+ * tasks once during a single page request.
+ *
+ * @param $path
+ *   The menu router path of the object to retrieve local tasks for, for example
+ *   "node/123" or "admin/structure/menu/manage/[menu_name]".
+ *
+ * @return
+ *   A renderable array containing menu router items that are local tasks for
+ *   the passed in path.
+ *
+ * @see system_preprocess()
+ *
+ * @todo menu_local_tasks() translates the local tasks too early, so the
+ *   normalized node/% always points to the first node path that was passed in.
+ *   Either we need to change menu_local_tasks() or implement a custom version
+ *   of it here (more likely, because we can filter out non-contextual links
+ *   in the query already then).
+ */
+function menu_context_links($path) {
+  // @todo This access check really doesn't belong into an API function. But as
+  //   of now, there is no better location to entirely prevent the loading and
+  //   building of contextual links in case they should not be rendered at all.
+  if (!user_access('view contextual links')) {
+    return array();
+  }
+  $local_tasks = menu_local_tasks(0, $path);
+  $root_length = drupal_strlen($local_tasks['root_path']);
+  $tasks = $local_tasks['tabs']['output'];
+  foreach ($tasks as $key => $task) {
+    // Always unset the default, because it is not a real task.
+    if ($task['#link']['type'] == MENU_DEFAULT_LOCAL_TASK) {
+      unset($tasks[$key]);
+      continue;
+    }
+    unset($tasks[$key]['#theme']);
+    // Extract the link "type" from the path.
+    $tasks[$key]['#link_type'] = drupal_substr($task['#link']['path'], $root_length + 1);
+  }
+
+  return $tasks;
+}
+
+/**
  * Returns the rendered local tasks at the top level.
  */
 function menu_primary_local_tasks() {
@@ -2827,6 +2889,10 @@ function _menu_router_build($callbacks) 
       $item['tab_parent'] = '';
       $item['tab_root'] = $path;
     }
+    // If not specified, assign the default tab type for tabs.
+    elseif (!isset($item['tab_type'])) {
+      $item['tab_type'] = 'view';
+    }
     for ($i = $item['_number_parts'] - 1; $i; $i--) {
       $parent_path = implode('/', array_slice($item['_parts'], 0, $i));
       if (isset($menu[$parent_path])) {
@@ -2899,6 +2965,7 @@ function _menu_router_build($callbacks) 
       'theme callback' => '',
       'description' => '',
       'position' => '',
+      'tab_type' => '',
       'tab_parent' => '',
       'tab_root' => $path,
       'path' => $path,
@@ -2942,6 +3009,7 @@ function _menu_router_save($menu, $masks
       'delivery_callback',
       'fit',
       'number_parts',
+      'tab_type',
       'tab_parent',
       'tab_root',
       'title',
@@ -2970,6 +3038,7 @@ function _menu_router_save($menu, $masks
       'delivery_callback' => $item['delivery callback'],
       'fit' => $item['_fit'],
       'number_parts' => $item['_number_parts'],
+      'tab_type' => $item['tab_type'],
       'tab_parent' => $item['tab_parent'],
       'tab_root' => $item['tab_root'],
       'title' => $item['title'],
Index: misc/attached_links.css
===================================================================
RCS file: misc/attached_links.css
diff -N misc/attached_links.css
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ misc/attached_links.css	16 Oct 2009 14:53:59 -0000
@@ -0,0 +1,38 @@
+/* $Id$ */
+
+/**
+ * Attached links regions.
+ */
+.attached-links-region {
+  outline: none;
+  position: relative;
+}
+.attached-links-region-active {
+  outline: #000 dashed 1px;
+}
+
+/**
+ * Attached links.
+ */
+ul.attached-links {
+  float: right;
+  font-size: 90%;
+  margin: 0;
+  padding: 0;
+}
+ul.attached-links li {
+  border-left: 1px solid #ccc;
+  display: inline;
+  line-height: 100%;
+  list-style: none;
+  margin: 0 0 0 0.3em;
+  padding: 0 0 0 0.6em;
+}
+ul.attached-links li.first {
+  border-left: 0;
+  margin: 0;
+  padding: 0;
+}
+ul.attached-links li a {
+  text-decoration: none;
+}
Index: misc/attached_links.js
===================================================================
RCS file: misc/attached_links.js
diff -N misc/attached_links.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ misc/attached_links.js	16 Oct 2009 14:55:55 -0000
@@ -0,0 +1,33 @@
+// $Id$
+(function ($) {
+
+Drupal.attachedLinks = Drupal.attachedLinks || {};
+
+/**
+ * Attach outline behavior for regions associated with attached links.
+ */
+Drupal.behaviors.attachedLinks = {
+  attach: function (context) {
+    $('ul.attached-links', context).once('attached-links', function () {
+      $(this).hover(Drupal.attachedLinks.hover, Drupal.attachedLinks.hoverOut);
+    });
+  }
+};
+
+/**
+ * Enables outline for the region an attached link is associated with.
+ */
+Drupal.attachedLinks.hover = function () {
+  $(this).addClass('attached-links-link-active')
+    .parents('.attached-links-region').eq(0).addClass('attached-links-region-active');
+};
+
+/**
+ * Disables outline for the region an attached link is associated with.
+ */
+Drupal.attachedLinks.hoverOut = function () {
+  $(this).removeClass('attached-links-link-active')
+    .parents('.attached-links-region').eq(0).removeClass('attached-links-region-active');
+};
+
+})(jQuery);
Index: modules/block/block.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.api.php,v
retrieving revision 1.8
diff -u -p -r1.8 block.api.php
--- modules/block/block.api.php	31 Aug 2009 17:06:08 -0000	1.8
+++ modules/block/block.api.php	16 Oct 2009 14:04:27 -0000
@@ -140,6 +140,7 @@ function hook_block_view($delta = '') {
         'content' => mymodule_display_block_exciting(),
       );
       break;
+
     case 'amazing':
       $block = array(
         'subject' => t('Default title of the amazing block'),
@@ -151,6 +152,79 @@ function hook_block_view($delta = '') {
 }
 
 /**
+ * Perform alterations to the content of a block.
+ *
+ * This hook allows you to modify any data returned by hook_block_view().
+ *
+ * Note that instead of hook_block_view_alter(), which is called for all
+ * blocks, you can also use hook_block_view_MODULE_DELTA_alter() to alter a
+ * specific block.
+ *
+ * @param $data
+ *   An array of data, as returned from the hook_block_view() implementation of
+ *   the module that defined the block:
+ *   - subject: The localized title of the block.
+ *   - content: Either a string or a renderable array representing the content
+ *     of the block. You should check that the content is an array before trying
+ *     to modify parts of the renderable structure.
+ * @param $block
+ *   The block object, as loaded from the database, having the main properties:
+ *   - module: The name of the module that defined the block.
+ *   - delta: The identifier for the block within that module, as defined within
+ *     hook_block_info().
+ *
+ * @see hook_block_view_alter()
+ * @see hook_block_view()
+ */
+function hook_block_view_MODULE_DELTA_alter(&$data, $block) {
+  // Remove the contextual links attached to all blocks that provide them.
+  if (is_array($data['content']) && isset($data['content']['#attached_links'])) {
+    unset($data['content']['#attached_links']);
+  }
+  // Add a theme wrapper function defined by the current module to all blocks
+  // provided by the "somemodule" module.
+  if (is_array($data['content']) && $block->module == 'somemodule') {
+    $data['content']['#theme_wrappers'][] = 'mymodule_special_block';
+  }
+}
+
+/**
+ * Perform alterations to a specific block.
+ *
+ * Modules can implement hook_block_view_MODULE_DELTA_alter() to modify a
+ * specific block, rather than implementing hook_block_view_alter().
+ *
+ * Note that this hook fires before hook_block_view_alter(). Therefore, all
+ * implementations of hook_block_view_MODULE_DELTA_alter() will run before all
+ * implementations of hook_block_view_alter(), regardless of the module order.
+ *
+ * @param $data
+ *   An array of data, as returned from the hook_block_view() implementation of
+ *   the module that defined the block:
+ *   - subject: The localized title of the block.
+ *   - content: Either a string or a renderable array representing the content
+ *     of the block. You should check that the content is an array before trying
+ *     to modify parts of the renderable structure.
+ * @param $block
+ *   The block object, as loaded from the database, having the main properties:
+ *   - module: The name of the module that defined the block.
+ *   - delta: The identifier for the block within that module, as defined within
+ *     hook_block_info().
+ *
+ * @see hook_block_view_alter()
+ * @see hook_block_view()
+ */
+function hook_block_view_MODULE_DELTA_alter(&$data, $block) {
+  // This code will only run for a specific block. For example, if MODULE_DELTA
+  // in the function definition above is set to "mymodule_somedelta", the code
+  // will only run on the "somedelta" block provided by the "mymodule" module.
+
+  // Change the title of the "somedelta" block provided by the "mymodule"
+  // module.
+  $data['subject'] = t('New title of the block');
+}
+
+/**
  * Act on blocks prior to rendering.
  *
  * This hook allows you to add, remove or modify blocks in the block list. The
Index: modules/block/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.module,v
retrieving revision 1.389
diff -u -p -r1.389 block.module
--- modules/block/block.module	14 Oct 2009 02:13:14 -0000	1.389
+++ modules/block/block.module	16 Oct 2009 14:00:43 -0000
@@ -280,6 +280,22 @@ function _block_get_renderable_array($li
   foreach ($list as $key => $block) {
     $build[$key] = $block->content;
     unset($block->content);
+
+    // Attach links for this block; skip this for the system main block.
+    if ($key != 'system_main') {
+      // Add local task to configure the block.
+      // @todo Implement proper path/subject/verb URL pattern for blocks.
+      $router_item = menu_get_item("admin/structure/block/configure/{$block->module}/{$block->delta}");
+      $build[$key]['#attached_links'][][] = array(
+        '#link_type' => 'configure',
+        '#link' => array(
+          'title' => $router_item['title'],
+          'href' => "admin/structure/block/configure/{$block->module}/{$block->delta}",
+          'options' => (isset($router_item['localized_options']) ? $router_item['localized_options'] : array()),
+        ),
+      );
+    }
+
     $build[$key] += array(
       '#block' => $block,
       '#weight' => ++$weight,
@@ -752,6 +768,12 @@ function _block_render_blocks($region_bl
       }
       else {
         $array = module_invoke($block->module, 'block_view', $block->delta);
+
+        // Allow modules to modify the block before it is viewed, via either
+        // hook_block_view_MODULE_DELTA_alter() or hook_block_view_alter().
+        drupal_alter("block_view_{$block->module}_{$block->delta}", $array, $block);
+        drupal_alter('block_view', $array, $block);
+
         if (isset($cid)) {
           cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);
         }
Index: modules/block/block.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.tpl.php,v
retrieving revision 1.4
diff -u -p -r1.4 block.tpl.php
--- modules/block/block.tpl.php	11 Sep 2009 06:48:02 -0000	1.4
+++ modules/block/block.tpl.php	16 Oct 2009 06:38:27 -0000
@@ -11,6 +11,7 @@
  * - $block->module: Module that generated the block.
  * - $block->delta: An ID for the block, unique within each module.
  * - $block->region: The block region embedding the current block.
+ * - $attached_links (array): An array of links that are attached to the block.
  * - $classes: String of classes that can be used to style contextually through
  *   CSS. It can be manipulated through the variable $classes_array from
  *   preprocess functions. The default values can be one or more of the following:
@@ -36,6 +37,11 @@
  */
 ?>
 <div id="block-<?php print $block->module . '-' . $block->delta; ?>" class="<?php print $classes; ?>"<?php print $attributes; ?>>
+ 
+<?php if ($attached_links): ?>
+  <?php print render($attached_links); ?>
+<?php endif; ?>
+ 
 <?php if ($block->subject): ?>
   <h2<?php print $title_attributes; ?>><?php print $block->subject ?></h2>
 <?php endif;?>
Index: modules/locale/locale.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.test,v
retrieving revision 1.46
diff -u -p -r1.46 locale.test
--- modules/locale/locale.test	16 Oct 2009 02:04:42 -0000	1.46
+++ modules/locale/locale.test	16 Oct 2009 03:35:50 -0000
@@ -1089,7 +1089,7 @@ class LanguageSwitchingFunctionalTest ex
     $this->assertText(t('Languages'), t('Language switcher block found.'));
 
     // Assert that only the current language is marked as active.
-    list($language_switcher) = $this->xpath('//div[@id="block-locale-language"]');
+    list($language_switcher) = $this->xpath('//div[@id="block-locale-language"]/div[@class="content"]');
     $links = array(
       'active' => array(),
       'inactive' => array(),
@@ -1098,7 +1098,7 @@ class LanguageSwitchingFunctionalTest ex
       'active' => array(),
       'inactive' => array(),
     );
-    foreach ($language_switcher->div->ul->li as $link) {
+    foreach ($language_switcher->ul->li as $link) {
       $classes = explode(" ", (string) $link['class']);
       list($language) = array_intersect($classes, array('en', 'fr'));
       if (in_array('active', $classes)) {
Index: modules/menu/menu.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.api.php,v
retrieving revision 1.17
diff -u -p -r1.17 menu.api.php
--- modules/menu/menu.api.php	15 Oct 2009 14:07:29 -0000	1.17
+++ modules/menu/menu.api.php	16 Oct 2009 15:25:37 -0000
@@ -199,6 +199,18 @@
  *     this alone; the default alphabetical order is usually best.
  *   - "menu_name": Optional. Set this to a custom menu if you don't want your
  *     item to be placed in Navigation.
+ *   - "tab_type": (optional) Defines the type of a tab to control its placement
+ *     depending on the requested context. By default, all tabs are only
+ *     displayed as local tasks when being rendered in a page context. All tabs
+ *     that should be accessible as contextual links in page region containers
+ *     outside of the parent menu item's primary page context, should be
+ *     registered using one of the following types:
+ *     - view: (default) The tab is displayed as local task for the page context
+ *       only.
+ *     - task: The tab is displayed both in the page context and as contextual
+ *       link.
+ *     - context: The tab is displayed as contextual link outside of the primary
+ *       page context only.
  *   - "tab_parent": For local task menu items, the path of the task's parent
  *     item; defaults to the same path without the last component (e.g., the
  *     default parent for 'admin/people/create' is 'admin/people').
Index: modules/menu/menu.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.module,v
retrieving revision 1.209
diff -u -p -r1.209 menu.module
--- modules/menu/menu.module	13 Oct 2009 01:24:07 -0000	1.209
+++ modules/menu/menu.module	16 Oct 2009 01:00:59 -0000
@@ -431,10 +431,27 @@ function menu_block_view($delta = '') {
   $menus = menu_get_menus(FALSE);
   $data['subject'] = check_plain($menus[$delta]);
   $data['content'] = menu_tree($delta);
+  // Attach local tasks for this block.
+  if (!empty($data['content'])) {
+    $data['content']['#attached_links'][] = menu_context_links('admin/structure/menu/manage/' . $delta);
+  }
   return $data;
 }
 
 /**
+ * Implement hook_block_view_alter().
+ */
+function menu_block_view_alter(&$data, $module, $delta) {
+  // Attach links to system menu blocks.
+  if ($module == 'system' && !empty($data['content'])) {
+    $system_menus = menu_list_system_menus();
+    if (isset($system_menus[$delta])) {
+      $data['content']['#attached_links'][] = menu_context_links('admin/structure/menu/manage/' . $delta);
+    }
+  }
+}
+
+/**
  * Implement hook_node_insert().
  */
 function menu_node_insert($node) {
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1148
diff -u -p -r1.1148 node.module
--- modules/node/node.module	16 Oct 2009 03:21:23 -0000	1.1148
+++ modules/node/node.module	16 Oct 2009 03:35:51 -0000
@@ -1111,6 +1111,9 @@ function node_build($node, $build_mode =
     '#node' => $node,
     '#build_mode' => $build_mode,
   );
+  // Attach local tasks for this node.
+  $build['#attached_links'][] = menu_context_links('node/' . $node->nid);
+
   return $build;
 }
 
Index: modules/node/node.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.tpl.php,v
retrieving revision 1.24
diff -u -p -r1.24 node.tpl.php
--- modules/node/node.tpl.php	11 Oct 2009 06:43:33 -0000	1.24
+++ modules/node/node.tpl.php	16 Oct 2009 06:38:05 -0000
@@ -18,6 +18,7 @@
  * - $node_url: Direct url of the current node.
  * - $terms: the themed list of taxonomy term links output from theme_links().
  * - $display_submitted: whether submission information should be displayed.
+ * - $attached_links (array): An array of links that are attached to the node.
  * - $classes: String of classes that can be used to style contextually through
  *   CSS. It can be manipulated through the variable $classes_array from
  *   preprocess functions. The default values can be one or more of the following:
@@ -74,6 +75,10 @@
 
   <?php print $user_picture; ?>
 
+  <?php if (!$page && $attached_links): ?>
+    <?php print render($attached_links); ?>
+  <?php endif; ?>
+
   <?php if (!$page): ?>
     <h2<?php print $title_attributes; ?>><a href="<?php print $node_url; ?>"><?php print $node_title; ?></a></h2>
   <?php endif; ?>
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.401
diff -u -p -r1.401 system.install
--- modules/system/system.install	15 Oct 2009 17:53:34 -0000	1.401
+++ modules/system/system.install	16 Oct 2009 15:31:09 -0000
@@ -1025,6 +1025,13 @@ function system_schema() {
         'default' => 0,
         'size' => 'small',
       ),
+      'tab_type' => array(
+        'description' => 'Only for local tasks (tabs) - the type of the local task to control its contextual placement.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
       'tab_parent' => array(
         'description' => 'Only for local tasks (tabs) - the router path of the parent page (which may also be a local task).',
         'type' => 'varchar',
@@ -2755,6 +2762,19 @@ function system_update_7042() {
 }
 
 /**
+ * Add a 'tab_type' field to {menu_router} to control contextual placement of local tasks.
+ */
+function system_update_7043() {
+  db_add_field('menu_router', 'tab_type', array(
+    'description' => 'Only for local tasks (tabs) - the type of the local task to control its contextual placement.',
+    'type' => 'varchar',
+    'length' => 255,
+    'not null' => TRUE,
+    'default' => '',
+  ));
+}
+
+/**
  * @} End of "defgroup updates-6.x-to-7.x"
  * The next series of updates should start at 8000.
  */
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.817
diff -u -p -r1.817 system.module
--- modules/system/system.module	16 Oct 2009 03:01:54 -0000	1.817
+++ modules/system/system.module	16 Oct 2009 06:30:04 -0000
@@ -255,6 +255,10 @@ function system_permission() {
       'title' => t('Block IP addresses'),
       'description' => t('Block IP addresses from accessing your site.'),
     ),
+    'view contextual links' => array(
+      'title' => t('View contextual links'),
+      'description' => t('Access contextual links associated with items on the page.'),
+    ),
   );
 }
 
@@ -3482,3 +3486,51 @@ function system_archiver_info() {
 function theme_confirm_form($variables) {
   return drupal_render_children($variables['form']);
 }
+
+/**
+ * Template variable preprocessor for attached links.
+ */
+function system_preprocess(&$variables, $hook) {
+  static $hooks;
+
+  if (!isset($hooks)) {
+    $hooks = theme_get_registry();
+  }
+
+  // Initialize attached links.
+  $variables['attached_links'] = array();
+
+  // Determine the primary theme function argument.
+  $keys = array_keys($hooks[$hook]['arguments']);
+  $key = $keys[0];
+  if (isset($variables[$key])) {
+    $element = $variables[$key];
+  }
+
+  if (isset($element) && is_array($element) && isset($element['#attached_links'])) {
+    // Transform contextual links into parameters suitable for theme_link().
+    $items = call_user_func_array('array_merge_recursive', $element['#attached_links']);
+    $links = array();
+    foreach ($items as $item) {
+      $class = drupal_html_class($item['#link_type']);
+      $links[$class] = array(
+        'title' => $item['#link']['title'],
+        'href' => $item['#link']['href'],
+        'options' => (isset($item['#link']['localized_options']) ? $item['#link']['localized_options'] : array()),
+      );
+    }
+    if ($links) {
+      $variables['attached_links'] = array(
+        '#theme' => 'links',
+        '#links' => $links,
+        '#attributes' => array('class' => array('attached-links')),
+        '#attached' => array(
+          'js' => array('misc/attached_links.js'),
+          'css' => array('misc/attached_links.css'),
+        ),
+      );
+      $variables['classes_array'][] = 'attached-links-region';
+    }
+  }
+}
+
Index: themes/garland/block.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/themes/garland/block.tpl.php,v
retrieving revision 1.9
diff -u -p -r1.9 block.tpl.php
--- themes/garland/block.tpl.php	11 Sep 2009 06:48:03 -0000	1.9
+++ themes/garland/block.tpl.php	10 Oct 2009 17:39:51 -0000
@@ -3,6 +3,10 @@
 ?>
 <div id="block-<?php print $block->module . '-' . $block->delta; ?>" class="<?php print $classes; ?> clearfix"<?php print $attributes; ?>>
 
+<?php if ($attached_links): ?>
+  <?php print render($attached_links); ?>
+<?php endif; ?>
+
 <?php if (!empty($block->subject)): ?>
   <h2 class="title"<?php print $title_attributes; ?>><?php print $block->subject ?></h2>
 <?php endif;?>
Index: themes/garland/comment.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/themes/garland/comment.tpl.php,v
retrieving revision 1.17
diff -u -p -r1.17 comment.tpl.php
--- themes/garland/comment.tpl.php	10 Oct 2009 13:37:11 -0000	1.17
+++ themes/garland/comment.tpl.php	16 Oct 2009 04:48:54 -0000
@@ -5,6 +5,10 @@
 
   <div class="clearfix">
 
+  <?php if ($attached_links): ?>
+    <?php print render($attached_links); ?>
+  <?php endif; ?>
+
     <span class="submitted"><?php print $created; ?> — <?php print $author; ?></span>
 
   <?php if ($new) : ?>
Index: themes/garland/node.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/themes/garland/node.tpl.php,v
retrieving revision 1.17
diff -u -p -r1.17 node.tpl.php
--- themes/garland/node.tpl.php	11 Oct 2009 03:07:21 -0000	1.17
+++ themes/garland/node.tpl.php	11 Oct 2009 05:05:50 -0000
@@ -3,6 +3,10 @@
 ?>
 <div id="node-<?php print $node->nid; ?>" class="<?php print $classes; ?>"<?php print $attributes; ?>>
 
+  <?php if (!$page && $attached_links): ?>
+    <?php print render($attached_links); ?>
+  <?php endif; ?>
+
   <?php print $user_picture; ?>
 
   <?php if (!$page): ?>
Index: themes/garland/style.css
===================================================================
RCS file: /cvs/drupal/drupal/themes/garland/style.css,v
retrieving revision 1.65
diff -u -p -r1.65 style.css
--- themes/garland/style.css	5 Oct 2009 02:43:01 -0000	1.65
+++ themes/garland/style.css	16 Oct 2009 06:34:26 -0000
@@ -650,8 +650,8 @@ ul.secondary li.active a {
  */
 .node {
   border-bottom: 1px solid #e9eff3;
-  margin: 0 -26px 1.5em;
-  padding: 1.5em 26px;
+  margin: 0 -16px 1.5em;
+  padding: 1.5em 16px;
 }
 
 ul.links li, ul.inline li {
@@ -809,6 +809,17 @@ tr.even td.menu-disabled {
 }
 
 /**
+ * Attached links.
+ */
+.attached-links-region-active {
+  outline: #027AC6 dashed 1px;
+}
+.block ul.attached-links {
+  margin: 0;
+  padding: 0;
+}
+
+/**
  * Collapsible fieldsets
  */
 fieldset {
