diff --git includes/common.inc includes/common.inc
index c2215a6..aab3cd2 100644
--- includes/common.inc
+++ includes/common.inc
@@ -3571,6 +3571,10 @@ function _drupal_bootstrap_full() {
   module_load_all();
   // Make sure all stream wrappers are registered.
   file_get_stream_wrappers();
+  // Set a custom theme for the current page, if there is one. We need to run
+  // this before invoking hook_init(), since any modules which initialize the
+  // theme system will prevent a custom theme from being correctly set later.
+  menu_set_custom_theme();
   // Let all modules take action before menu system handles the request
   // We do not want this while running update.php.
   if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
diff --git includes/menu.inc includes/menu.inc
index 1daee3c..e289c2a 100644
--- includes/menu.inc
+++ includes/menu.inc
@@ -346,8 +346,9 @@ function menu_set_item($path, $router_item) {
  *   The router item, an associate array corresponding to one row in the
  *   menu_router table. The value of key map holds the loaded objects. The
  *   value of key access is TRUE if the current user can access this page.
- *   The values for key title, page_arguments, access_arguments will be
- *   filled in based on the database values and the objects loaded.
+ *   The values for key title, page_arguments, access_arguments, and
+ *   theme_arguments will be filled in based on the database values and the
+ *   objects loaded.
  */
 function menu_get_item($path = NULL, $router_item = NULL) {
   $router_items = &drupal_static(__FUNCTION__);
@@ -377,6 +378,7 @@ function menu_get_item($path = NULL, $router_item = NULL) {
       if ($router_item['access']) {
         $router_item['map'] = $map;
         $router_item['page_arguments'] = array_merge(menu_unserialize($router_item['page_arguments'], $map), array_slice($map, $router_item['number_parts']));
+        $router_item['theme_arguments'] = array_merge(menu_unserialize($router_item['theme_arguments'], $map), array_slice($map, $router_item['number_parts']));
       }
     }
     $router_items[$path] = $router_item;
@@ -878,6 +880,8 @@ function menu_tree_all_data($menu_name, $item = NULL) {
         'title',
         'title_callback',
         'title_arguments',
+        'theme_callback',
+        'theme_arguments',
         'type',
         'description',
       ));
@@ -1048,6 +1052,8 @@ function menu_tree_page_data($menu_name) {
           'title',
           'title_callback',
           'title_arguments',
+          'theme_callback',
+          'theme_arguments',
           'type',
           'description',
         ));
@@ -1343,6 +1349,29 @@ function menu_get_active_help() {
 }
 
 /**
+ * Get the custom theme for the current page, if there is one.
+ */
+function menu_get_custom_theme($set = FALSE) {
+  $custom_theme = &drupal_static(__FUNCTION__);
+  // Skip this if the site is offline or being installed or updated, since the
+  // menu system may not be correctly initialized then.
+  if ($set && !variable_get('site_offline', FALSE) && (!defined('MAINTENANCE_MODE') || (MAINTENANCE_MODE != 'update' && MAINTENANCE_MODE != 'install'))) {
+    $router_item = menu_get_item();
+    if (!empty($router_item['access']) && !empty($router_item['theme_callback']) && drupal_function_exists($router_item['theme_callback'])) {
+      $custom_theme = call_user_func_array($router_item['theme_callback'], $router_item['theme_arguments']);
+    }
+  }
+  return $custom_theme;
+}
+
+/**
+ * Set a custom theme for the current page, if there is one.
+ */
+function menu_set_custom_theme() {
+  menu_get_custom_theme(TRUE);
+}
+
+/**
  * Build a list of named menus.
  */
 function menu_get_names() {
@@ -2636,6 +2665,13 @@ function _menu_router_build($callbacks) {
             $item['page arguments'] = $parent['page arguments'];
           }
         }
+        // Same for theme callbacks.
+        if (!isset($item['theme callback']) && isset($parent['theme callback'])) {
+          $item['theme callback'] = $parent['theme callback'];
+          if (!isset($item['theme arguments']) && isset($parent['theme arguments'])) {
+            $item['theme arguments'] = $parent['theme arguments'];
+          }
+        }
       }
     }
     if (!isset($item['access callback']) && isset($item['access arguments'])) {
@@ -2657,6 +2693,8 @@ function _menu_router_build($callbacks) {
       'block callback' => '',
       'title arguments' => array(),
       'title callback' => 't',
+      'theme arguments' => array(),
+      'theme callback' => '',
       'description' => '',
       'position' => '',
       'tab_parent' => '',
@@ -2696,6 +2734,8 @@ function _menu_router_save($menu, $masks) {
       'title',
       'title_callback',
       'title_arguments',
+      'theme_callback',
+      'theme_arguments',
       'type',
       'block_callback',
       'description',
@@ -2720,6 +2760,8 @@ function _menu_router_save($menu, $masks) {
       'title' => $item['title'],
       'title_callback' => $item['title callback'],
       'title_arguments' => ($item['title arguments'] ? serialize($item['title arguments']) : ''),
+      'theme_callback' => $item['theme callback'],
+      'theme_arguments' => serialize($item['theme arguments']),
       'type' => $item['type'],
       'block_callback' => $item['block callback'],
       'description' => $item['description'],
diff --git includes/theme.inc includes/theme.inc
index b466f0d..55a302a 100644
--- includes/theme.inc
+++ includes/theme.inc
@@ -57,7 +57,8 @@ function drupal_theme_initialize() {
 
   // Allow modules to override the present theme... only select custom theme
   // if it is available in the list of installed themes.
-  $theme = $custom_theme && $themes[$custom_theme] ? $custom_theme : $theme;
+  $custom_theme = menu_get_custom_theme();
+  $theme = $custom_theme && isset($themes[$custom_theme]) ? $custom_theme : $theme;
 
   // Store the identifier for retrieving theme settings with.
   $theme_key = $theme;
diff --git modules/block/block.admin.inc modules/block/block.admin.inc
index 1fc4a6a..4fe9d77 100644
--- modules/block/block.admin.inc
+++ modules/block/block.admin.inc
@@ -34,11 +34,6 @@ function block_form_system_performance_settings_alter(&$form, &$form_state) {
  * Menu callback for admin/structure/block.
  */
 function block_admin_display($theme = NULL) {
-  global $custom_theme;
-
-  // If non-default theme configuration has been selected, set the custom theme.
-  $custom_theme = isset($theme) ? $theme : variable_get('theme_default', 'garland');
-
   // Fetch and sort blocks.
   $blocks = _block_rehash();
   usort($blocks, '_block_compare');
@@ -50,14 +45,10 @@ function block_admin_display($theme = NULL) {
  * Generate main blocks administration form.
  */
 function block_admin_display_form(&$form_state, $blocks, $theme = NULL) {
-  global $theme_key, $custom_theme;
+  global $theme_key;
 
   drupal_add_css(drupal_get_path('module', 'block') . '/block.css', array('preprocess' => FALSE));
 
-  // If non-default theme configuration has been selected, set the custom theme.
-  $custom_theme = isset($theme) ? $theme : variable_get('theme_default', 'garland');
-  drupal_theme_initialize();
-
   $block_regions = system_region_list($theme_key, REGIONS_VISIBLE) + array(BLOCK_REGION_NONE => '<' . t('none') . '>');
 
   // Weights range from -delta to +delta, so delta should be at least half
diff --git modules/block/block.module modules/block/block.module
index 0211131..c0a92ee 100644
--- modules/block/block.module
+++ modules/block/block.module
@@ -125,6 +125,7 @@ function block_menu() {
     'description' => 'Configure what block content appears in your site\'s sidebars and other regions.',
     'page callback' => 'block_admin_display',
     'access arguments' => array('administer blocks'),
+    'theme callback' => '_block_custom_theme',
   );
   $items['admin/structure/block/list'] = array(
     'title' => 'List',
@@ -167,6 +168,8 @@ function block_menu() {
       'weight' => $key == $default ? -10 : 0,
       'access callback' => '_block_themes_access',
       'access arguments' => array($theme),
+      'theme callback' => '_block_custom_theme',
+      'theme arguments' => array($key),
     );
   }
   return $items;
@@ -181,6 +184,17 @@ function _block_themes_access($theme) {
 }
 
 /**
+ * Theme callback for the block configuration pages.
+ *
+ * If a particular theme's blocks are being configured, that theme will be used
+ * for the page. Otherwise, we return nothing and fall back on the default
+ * theme of the site.
+ */
+function _block_custom_theme($theme = NULL) {
+  return $theme;
+}
+
+/**
  * Implement hook_block_list().
  */
 function block_block_list() {
diff --git modules/book/book.module modules/book/book.module
index bbd900c..164802a 100644
--- modules/book/book.module
+++ modules/book/book.module
@@ -1178,7 +1178,7 @@ function book_menu_subtree_data($item) {
       $menu_router_alias = $query->join('menu_router', 'm', 'm.path = ml.router_path');
       $book_alias = $query->join('book', 'b', 'ml.mlid = b.mlid');
       $query->fields($book_alias);
-      $query->fields($menu_router_alias, array('load_functions', 'to_arg_functions', 'access_callback', 'access_arguments', 'page_callback', 'page_arguments', 'title', 'title_callback', 'title_arguments', 'type'));
+      $query->fields($menu_router_alias, array('load_functions', 'to_arg_functions', 'access_callback', 'access_arguments', 'page_callback', 'page_arguments', 'title', 'title_callback', 'title_arguments', 'theme_callback', 'theme_arguments', 'type'));
       $query->fields('ml');
       $query->condition('menu_name', $item['menu_name']);
       for ($i = 1; $i <= MENU_MAX_DEPTH && $item["p$i"]; ++$i) {
diff --git modules/menu/menu.admin.inc modules/menu/menu.admin.inc
index 4640990..c8b5321 100644
--- modules/menu/menu.admin.inc
+++ modules/menu/menu.admin.inc
@@ -43,7 +43,7 @@ function theme_menu_admin_overview($title, $name, $description) {
 function menu_overview_form(&$form_state, $menu) {
   global $menu_admin;
   $sql = "
-    SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, m.description, ml.*
+    SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.theme_callback, m.theme_arguments, m.type, m.description, ml.*
     FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path
     WHERE ml.menu_name = :menu
     ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC";
diff --git modules/menu/menu.api.php modules/menu/menu.api.php
index 8fc9151..ff494ef 100644
--- modules/menu/menu.api.php
+++ modules/menu/menu.api.php
@@ -40,6 +40,13 @@
  *     user_access() unless a value is inherited from a parent menu item.
  *   - "access arguments": An array of arguments to pass to the access callback
  *     function. Integer values pass the corresponding URL component.
+ *   - "theme callback": Optional. A function returning the machine-readable
+ *     name of the theme that will be used to render the page. If the function
+ *     returns nothing, the main site theme will be used. If no function is
+ *     provided, the main site theme will also be used, unless a value is
+ *     inherited from a parent menu item.
+ *   - "theme arguments": An array of arguments to pass to the theme callback
+ *     function. Integer values pass the corresponding URL component.
  *   - "file": A file that will be included before the callbacks are accessed;
  *     this allows callback functions to be in separate files. The file should
  *     be relative to the implementing module's directory unless otherwise
diff --git modules/node/node.module modules/node/node.module
index b99f958..2f67f93 100644
--- modules/node/node.module
+++ modules/node/node.module
@@ -1728,6 +1728,7 @@ function node_menu() {
     'access callback' => '_node_add_access',
     'weight' => 1,
     'menu_name' => 'management',
+    'theme callback' => '_node_custom_theme',
   );
   $items['rss.xml'] = array(
     'title' => 'RSS feed',
@@ -1784,6 +1785,7 @@ function node_menu() {
     'page arguments' => array(1),
     'access callback' => 'node_access',
     'access arguments' => array('update', 1),
+    'theme callback' => '_node_custom_theme',
     'weight' => 1,
     'type' => MENU_LOCAL_TASK,
   );
@@ -1842,6 +1844,17 @@ function node_page_title($node) {
 }
 
 /**
+ * Theme callback for creating and editing nodes.
+ */
+function _node_custom_theme() {
+  // Use the administration theme if the site is configured to use it for node
+  // editing.
+  if (variable_get('node_admin_theme')) {
+    return system_admin_theme();
+  }
+}
+
+/**
  * Implement hook_init().
  */
 function node_init() {
diff --git modules/system/system.install modules/system/system.install
index db1e3f5..166373b 100644
--- modules/system/system.install
+++ modules/system/system.install
@@ -905,6 +905,20 @@ function system_schema() {
         'not null' => TRUE,
         'default' => '',
       ),
+      'theme_callback' => array(
+        'description' => 'A function which returns the name of the theme that will be used to render this page. If left empty, the default theme will be used.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'theme_arguments' => array(
+        'description' => 'A serialized array of arguments for the theme callback.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
       'type' => array(
         'description' => 'Numeric representation of the type of the menu item, like MENU_LOCAL_TASK.',
         'type' => 'int',
@@ -2411,6 +2425,17 @@ function system_update_7035() {
 }
 
 /**
+ * Add fields to the {menu_router} table to allow custom themes to be set per
+ * page.
+ */
+function system_update_7036() {
+  $ret = array();
+  db_add_field($ret, 'menu_router', 'theme_callback', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''));
+  db_add_field($ret, 'menu_router', 'theme_arguments', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''));
+  return $ret;
+}
+
+/**
  * @} End of "defgroup updates-6.x-to-7.x"
  * The next series of updates should start at 8000.
  */
diff --git modules/system/system.module modules/system/system.module
index 2ff7237..e2e185f 100644
--- modules/system/system.module
+++ modules/system/system.module
@@ -502,6 +502,7 @@ function system_menu() {
     'page callback' => 'system_main_admin_page',
     'weight' => 9,
     'menu_name' => 'management',
+    'theme callback' => 'system_admin_theme',
   );
   $items['admin/compact'] = array(
     'title' => 'Compact mode',
@@ -1222,6 +1223,13 @@ function system_admin_menu_block_access($path, $permission) {
 }
 
 /**
+ * Theme callback for site administration pages.
+ */
+function system_admin_theme() {
+  return variable_get('admin_theme');
+}
+
+/**
  * Implement hook_filetransfer_backends().
  */
 function system_filetransfer_backends() {
@@ -1334,14 +1342,10 @@ function _system_filetransfer_backend_form_common() {
  * Implement hook_init().
  */
 function system_init() {
-  // Use the administrative theme if the user is looking at a page in the admin/* path.
+  // Add the CSS for this module.
   if (arg(0) == 'admin' || (variable_get('node_admin_theme', '0') && arg(0) == 'node' && (arg(1) == 'add' || arg(2) == 'edit'))) {
-    global $custom_theme;
-    $custom_theme = variable_get('admin_theme', 0);
     drupal_add_css(drupal_get_path('module', 'system') . '/admin.css', array('weight' => CSS_SYSTEM));
   }
-
-  // Add the CSS for this module.
   drupal_add_css(drupal_get_path('module', 'system') . '/defaults.css', array('weight' => CSS_SYSTEM));
   drupal_add_css(drupal_get_path('module', 'system') . '/system.css', array('weight' => CSS_SYSTEM));
   drupal_add_css(drupal_get_path('module', 'system') . '/system-menus.css', array('weight' => CSS_SYSTEM));
@@ -1572,7 +1576,7 @@ function system_admin_menu_block($item) {
 
   $content = array();
   $result = db_query("
-    SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, m.description, ml.*
+    SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.theme_callback, m.theme_arguments, m.type, m.description, ml.*
     FROM {menu_links} ml
     LEFT JOIN {menu_router} m ON ml.router_path = m.path
     WHERE ml.plid = :plid AND ml.menu_name = :name AND hidden = 0", array(':plid' => $item['mlid'], ':name' => $item['menu_name']), array('fetch' => PDO::FETCH_ASSOC));
@@ -2206,7 +2210,7 @@ function system_get_module_admin_tasks($module) {
 
   if (empty($items)) {
     $result = db_query("
-       SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, ml.*
+       SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.theme_callback, m.theme_arguments, m.type, ml.*
        FROM {menu_links} ml INNER JOIN {menu_router} m ON ml.router_path = m.path WHERE ml.link_path LIKE 'admin/%' AND hidden >= 0 AND module = 'system' AND m.number_parts > 2", array(), array('fetch' => PDO::FETCH_ASSOC));
     foreach ($result as $item) {
       _menu_link_translate($item);
