Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.396
diff -u -p -r1.396 menu.inc
--- includes/menu.inc	16 Jun 2010 05:07:16 -0000	1.396
+++ includes/menu.inc	25 Jun 2010 13:17:47 -0000
@@ -1045,6 +1045,9 @@ function menu_tree_all_data($menu_name, 
         $links[] = $item;
       }
       $data['tree'] = menu_tree_data($links, $parents);
+      // Allow the tree to be altered before any access checks. Since this
+      // tree is not menu item-specific, just pass a null item.
+      drupal_alter('menu_tree', $data['tree']);
       $data['node_links'] = array();
       menu_tree_collect_node_links($data['tree'], $data['node_links']);
       // Cache the data, if it is not already in the cache.
@@ -1085,7 +1088,19 @@ function menu_tree_page_data($menu_name,
   $tree = &drupal_static(__FUNCTION__, array());
 
   // Load the menu item corresponding to the current page.
-  if ($item = menu_get_item()) {
+  $active_trail = menu_get_active_trail();
+  // Find the deepest item in the active trail that is in the menu system.
+  do {
+    $item = array_pop($active_trail);
+  } while (isset($item) && $item['type'] == 0);
+  if (!empty($item['href'])) {
+    $item = menu_get_item($item['href']);
+  }
+  // If we didn't find a menu item, load the menu item corresponding to the current page.
+  if (!$item) {
+    $item = menu_get_item();
+  }
+  if ($item) {
     if (isset($max_depth)) {
       $max_depth = min($max_depth, MENU_MAX_DEPTH);
     }
@@ -1165,8 +1180,8 @@ function menu_tree_page_data($menu_name,
                 ->condition('mlid', $args, 'NOT IN')
                 ->execute();
               $num_rows = FALSE;
-              foreach ($result as $item) {
-                $args[] = $item['mlid'];
+              foreach ($result as $link) {
+                $args[] = $link['mlid'];
                 $num_rows = TRUE;
               }
             } while ($num_rows);
@@ -1210,10 +1225,12 @@ function menu_tree_page_data($menu_name,
         }
         // Build an ordered array of links using the query result object.
         $links = array();
-        foreach ($query->execute() as $item) {
-          $links[] = $item;
+        foreach ($query->execute() as $link) {
+          $links[] = $link;
         }
         $data['tree'] = menu_tree_data($links, $parents);
+        // Allow the tree to be altered before any access checks.
+        drupal_alter('menu_tree', $data['tree'], $item);
         $data['node_links'] = array();
         menu_tree_collect_node_links($data['tree'], $data['node_links']);
         // Cache the data, if it is not already in the cache.
@@ -2168,20 +2185,18 @@ function menu_get_active_breadcrumb() {
     return $breadcrumb;
   }
 
-  $item = menu_get_item();
-  if ($item && $item['access']) {
-    $active_trail = menu_get_active_trail();
-
-    foreach ($active_trail as $parent) {
-      $breadcrumb[] = l($parent['title'], $parent['href'], $parent['localized_options']);
-    }
-    $end = end($active_trail);
-
-    // Don't show a link to the current page in the breadcrumb trail.
-    if ($item['href'] == $end['href'] || ($item['type'] == MENU_DEFAULT_LOCAL_TASK && $end['href'] != '<front>')) {
-      array_pop($breadcrumb);
-    }
+  $active_trail = menu_get_active_trail();
+
+  foreach ($active_trail as $parent) {
+    $breadcrumb[] = l($parent['title'], $parent['href'], $parent['localized_options']);
   }
+  $end = end($active_trail);
+
+  // Don't show a link to the current page in the breadcrumb trail.
+  if ($end['href'] != '<front>') {
+    array_pop($breadcrumb);
+  }
+
   return $breadcrumb;
 }
 
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.170
diff -u -p -r1.170 system.api.php
--- modules/system/system.api.php	23 Jun 2010 02:45:35 -0000	1.170
+++ modules/system/system.api.php	25 Jun 2010 13:17:51 -0000
@@ -1225,6 +1225,30 @@ function hook_menu_contextual_links_alte
 }
 
 /**
+ * Alter menu trees before they are rendered.
+ *
+ * This hook is invoked by menu_tree_page_data() and menu_tree_all_data(). The
+ * tree is passed through menu_tree_check_access() after this hook is called, so
+ * implementations will not need to worry about node access for any altered
+ * items.
+ *
+ * @param $tree
+ *   The menu tree to be altered.
+ * @param $router_item
+ *   For trees generated by menu_tree_page_data(), this will be the router item
+ *   used to calculate the active trail within the tree. NULL for trees
+ *   generated by menu_tree_all_data().
+ */
+function hook_menu_tree_alter(&$tree, $router_item = NULL) {
+  // Add a "primary" class to each of the first-level links.
+  foreach ($data['tree'] as $mlid => &$item) {
+    $options = unserialize($item['link']['options']);
+    $options['attributes']['class'][] = 'primary';
+    $item['link']['options'] = serialize($options);
+  }
+}
+
+/**
  * Perform alterations before a page is rendered.
  *
  * Use this hook when you want to remove or alter elements at the page
