diff --git a/core/includes/common.inc b/core/includes/common.inc
index c9913a2..92c8c77 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -6604,9 +6604,6 @@ function drupal_common_theme() {
     'install_page' => array(
       'variables' => array('content' => NULL),
     ),
-    'task_list' => array(
-      'variables' => array('items' => NULL, 'active' => NULL),
-    ),
     'authorize_message' => array(
       'variables' => array('message' => NULL, 'success' => TRUE),
     ),
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 3b69ce3..5efe0f8 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -720,7 +720,7 @@ function install_tasks($install_state) {
  * Returns a list of tasks that should be displayed to the end user.
  *
  * The output of this function is a list suitable for sending to
- * theme_task_list().
+ * theme_item_list__tasks().
  *
  * @param $install_state
  *   An array of information about the current installation state.
@@ -729,7 +729,8 @@ function install_tasks($install_state) {
  *   A list of tasks, with keys equal to the machine-readable task name and
  *   values equal to the name that should be displayed.
  *
- * @see theme_task_list()
+ * @see template_preprocess_item_list()
+ * @see theme_item_list()
  */
 function install_tasks_to_display($install_state) {
   $displayed_tasks = array();
@@ -810,7 +811,10 @@ function install_display_output($output, $install_state) {
     // Let the theming function know when every step of the installation has
     // been completed.
     $active_task = $install_state['installation_finished'] ? NULL : $install_state['active_task'];
-    drupal_add_region_content('sidebar_first', theme('task_list', array('items' => install_tasks_to_display($install_state), 'active' => $active_task)));
+    drupal_add_region_content('sidebar_first', theme('item_list__tasks', array(
+      'items' => install_tasks_to_display($install_state),
+      'active_task' => $active_task,
+    )));
   }
   print theme('install_page', array('content' => $output));
   exit;
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 2d1d375..f1aa13f 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -889,6 +889,7 @@ function theme($hook, $variables = array()) {
     }
     $hook = $candidate;
   }
+  $original_hook = $hook;
 
   // If there's no implementation, check for more generic fallbacks. If there's
   // still no implementation, log an error and return an empty string.
@@ -924,15 +925,16 @@ function theme($hook, $variables = array()) {
     }
   }
 
-  // If a renderable array is passed as $variables, then set $variables to
-  // the arguments expected by the theme function.
   if (isset($variables['#theme']) || isset($variables['#theme_wrappers'])) {
     $element = $variables;
     $variables = array();
+    // If a renderable array is passed as $variables, then convert all element
+    // #properties into variables that may be expected as arguments by the theme
+    // function or preprocess functions.
     if (isset($info['variables'])) {
-      foreach (array_keys($info['variables']) as $name) {
-        if (isset($element["#$name"])) {
-          $variables[$name] = $element["#$name"];
+      foreach (array_keys($element) as $key) {
+        if ($key[0] === '#') {
+          $variables[substr($key, 1)] = $element[$key];
         }
       }
     }
@@ -971,6 +973,9 @@ function theme($hook, $variables = array()) {
     }
   }
   if (isset($info['preprocess functions']) || isset($info['process functions'])) {
+    // Provide the originally passed in theme hook name to process functions.
+    $variables['theme_hook_original'] = $original_hook;
+
     $variables['theme_hook_suggestions'] = array();
     foreach (array('preprocess functions', 'process functions') as $phase) {
       if (!empty($info[$phase])) {
@@ -2083,6 +2088,29 @@ function theme_item_list($variables) {
   return $output;
 }
 
+// @todo template_preprocess_item_list__tasks() does not work (yet).
+function template_preprocess_item_list(&$variables) {
+  if ($variables['theme_hook_original'] === 'item_list__tasks') {
+    $t = get_t();
+    $active = isset($variables['active_task']) ? $variables['active_task'] : key($variables['items']);
+    $done = isset($variables['items'][$active]);
+    foreach ($variables['items'] as $key => &$item) {
+      if (!is_array($item)) {
+        $item = array('data' => $item);
+      }
+      if ($key === $active) {
+        $item['class'][] = 'active';
+        $item['data'] .= '<span class="element-invisible">(' . $t('active') . ')</span>';
+        $done = FALSE;
+      }
+      elseif ($done) {
+        $item['class'][] = 'done';
+        $item['data'] .= '<span class="element-invisible">(' . $t('done') . ')</span>';
+      }
+    }
+  }
+}
+
 /**
  * Returns HTML for a "more help" link.
  *
diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc
index 4b3e80c..c0e9d7f 100644
--- a/core/includes/theme.maintenance.inc
+++ b/core/includes/theme.maintenance.inc
@@ -96,45 +96,6 @@ function _theme_load_offline_registry($theme, $base_theme = NULL, $theme_engine
 }
 
 /**
- * Returns HTML for a list of maintenance tasks to perform.
- *
- * @param $variables
- *   An associative array containing:
- *   - items: An associative array of maintenance tasks.
- *   - active: The key for the currently active maintenance task.
- *
- * @ingroup themeable
- */
-function theme_task_list($variables) {
-  $t = get_t();
-  $items = $variables['items'];
-  $active = $variables['active'];
-
-  $done = isset($items[$active]) || $active == NULL;
-  $output = '<h2 class="element-invisible">Installation tasks</h2>';
-  $output .= '<ol class="task-list">';
-
-  foreach ($items as $k => $item) {
-    if ($active == $k) {
-      $class = 'active';
-      $status = '(' . $t('active') . ')';
-      $done = FALSE;
-    }
-    else {
-      $class = $done ? 'done' : '';
-      $status = $done ? '(' . $t('done') . ')' : '';
-    }
-    $output .= '<li';
-    $output .= ($class ? ' class="' . $class . '"' : '') . '>';
-    $output .= $item;
-    $output .= ($status ? '<span class="element-invisible">' . $status . '</span>' : '');
-    $output .= '</li>';
-  }
-  $output .= '</ol>';
-  return $output;
-}
-
-/**
  * Returns HTML for the installation page.
  *
  * Note: this function is not themeable.
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 7add837..92362f4 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -2578,6 +2578,21 @@ function node_page_default() {
       '#suffix' => '</div>',
     );
   }
+  $build['tasks'] = array(
+    '#theme' => 'item_list__tasks',
+    '#title' => 'Installation tasks',
+    '#type' => 'ol',
+    '#items' => array(
+      'foo' => 'Fool me',
+      'bar' => 'Bar me',
+      'baz' => 'Buzz me',
+    ),
+    '#active_task' => 'bar',
+    '#attributes' => array('class' => array('task-list')),
+    '#attached' => array(
+      'css' => array(drupal_get_path('module', 'system') . '/system.maintenance.css'),
+    ),
+  );
   return $build;
 }
 
diff --git a/core/update.php b/core/update.php
index b8a726d..2b28044 100644
--- a/core/update.php
+++ b/core/update.php
@@ -325,7 +325,10 @@ function update_task_list($active = NULL) {
     'finished' => 'Review log',
   );
 
-  drupal_add_region_content('sidebar_first', theme('task_list', array('items' => $tasks, 'active' => $active)));
+  drupal_add_region_content('sidebar_first', theme('item_list__tasks', array(
+    'items' => $tasks,
+    'active_task' => $active,
+  )));
 }
 
 /**
