diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 504030e..d27dc05 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -399,6 +399,7 @@ function _theme_process_registry(&$cache, $name, $type, $theme, $path) {
   // Processor functions work in two distinct phases with the process
   // functions always being executed after the preprocess functions.
   $variable_process_phases = array(
+    'template suggestion functions' => 'template_suggestions',
     'preprocess functions' => 'preprocess',
     'process functions'    => 'process',
   );
@@ -831,18 +832,6 @@ function drupal_find_base_themes($themes, $key, $used_keys = array()) {
  * need to be fast, and calling the non-theme-hook-specific preprocess and
  * process functions for them would incur a noticeable performance penalty.
  *
- * There are two special variables that these preprocess and process functions
- * can set: 'theme_hook_suggestion' and 'theme_hook_suggestions'. These will be
- * merged together to form a list of 'suggested' alternate theme hooks to use,
- * in reverse order of priority. theme_hook_suggestion will always be a higher
- * priority than items in theme_hook_suggestions. theme() will use the
- * highest priority implementation that exists. If none exists, theme() will
- * use the implementation for the theme hook it was called with. These
- * suggestions are similar to and are used for similar reasons as calling
- * theme() with an array as the $hook parameter (see below). The difference
- * is whether the suggestions are determined by the code that calls theme() or
- * by a preprocess or process function.
- *
  * @param $hook
  *   The name of the theme hook to call. If the name contains a
  *   double-underscore ('__') and there isn't an implementation for the full
@@ -957,6 +946,28 @@ function theme($hook, $variables = array()) {
     $variables += array($info['render element'] => array());
   }
 
+  // Invoke template suggestion hooks.
+  $suggestions = array();
+  if (!empty($info['template suggestion functions'])) {
+    foreach ($info['template suggestion functions'] as $funcname) {
+      $suggestions += $funcname($variables);
+    }
+  }
+
+  // Check if each suggestion exists in the theme registry, and if so,
+  // use it instead of the hook that theme() was called with. This
+  // allows the preprocess/process step to route to a more specific
+  // theme hook. For example, a function may call theme('node', ...),
+  // but a a module can add 'node__article' as a suggestion via
+  // hook_template_suggestions_HOOK, enabling a theme to have an
+  // alternate template file for article nodes.
+  foreach (array_reverse($suggestions) as $suggestion) {
+    if (isset($hooks[$suggestion])) {
+      $info = $hooks[$suggestion];
+      break;
+    }
+  }
+
   // Invoke the variable processors, if any. The processors may specify
   // alternate suggestions for which hook's template/function to use. If the
   // hook is a suggestion of a base hook, invoke the variable processors of
@@ -973,13 +984,11 @@ function theme($hook, $variables = array()) {
       }
     }
     if (isset($base_hook_info['preprocess functions']) || isset($base_hook_info['process functions'])) {
-      $variables['theme_hook_suggestion'] = $hook;
       $hook = $base_hook;
       $info = $base_hook_info;
     }
   }
   if (isset($info['preprocess functions']) || isset($info['process functions'])) {
-    $variables['theme_hook_suggestions'] = array();
     foreach (array('preprocess functions', 'process functions') as $phase) {
       if (!empty($info[$phase])) {
         foreach ($info[$phase] as $processor_function) {
@@ -991,31 +1000,6 @@ function theme($hook, $variables = array()) {
         }
       }
     }
-    // If the preprocess/process functions specified hook suggestions, and the
-    // suggestion exists in the theme registry, use it instead of the hook that
-    // theme() was called with. This allows the preprocess/process step to
-    // route to a more specific theme hook. For example, a function may call
-    // theme('node', ...), but a preprocess function can add 'node__article' as
-    // a suggestion, enabling a theme to have an alternate template file for
-    // article nodes. Suggestions are checked in the following order:
-    // - The 'theme_hook_suggestion' variable is checked first. It overrides
-    //   all others.
-    // - The 'theme_hook_suggestions' variable is checked in FILO order, so the
-    //   last suggestion added to the array takes precedence over suggestions
-    //   added earlier.
-    $suggestions = array();
-    if (!empty($variables['theme_hook_suggestions'])) {
-      $suggestions = $variables['theme_hook_suggestions'];
-    }
-    if (!empty($variables['theme_hook_suggestion'])) {
-      $suggestions[] = $variables['theme_hook_suggestion'];
-    }
-    foreach (array_reverse($suggestions) as $suggestion) {
-      if (isset($hooks[$suggestion])) {
-        $info = $hooks[$suggestion];
-        break;
-      }
-    }
   }
 
   // Generate the output using either a function or a template.
@@ -2495,11 +2479,6 @@ function template_preprocess_html(&$variables) {
   foreach ($elements as $name => $element) {
     drupal_add_html_head($element, $name);
   }
-
-  // Populate the page template suggestions.
-  if ($suggestions = theme_get_suggestions(arg(), 'html')) {
-    $variables['theme_hook_suggestions'] = $suggestions;
-  }
 }
 
 /**
@@ -2557,11 +2536,6 @@ function template_preprocess_page(&$variables) {
   if ($node = menu_get_object()) {
     $variables['node'] = $node;
   }
-
-  // Populate the page template suggestions.
-  if ($suggestions = theme_get_suggestions(arg(), 'page')) {
-    $variables['theme_hook_suggestions'] = $suggestions;
-  }
 }
 
 /**
@@ -2619,8 +2593,8 @@ function template_process_html(&$variables) {
 /**
  * Generate an array of suggestions from path arguments.
  *
- * This is typically called for adding to the 'theme_hook_suggestions' or
- * 'attributes' class key variables from within preprocess functions, when
+ * This is typically called for adding to the 'hook_template_suggestions_HOOK'
+ * or 'attributes' class key variables from within preprocess functions, when
  * wanting to base the additional suggestions on the path of the current page.
  *
  * @param $args
@@ -2635,7 +2609,7 @@ function template_process_html(&$variables) {
  *
  * @return
  *   An array of suggestions, suitable for adding to
- *   $variables['theme_hook_suggestions'] within a preprocess function or to
+ *   hook_template_suggestions_HOOK or to
  *   $variables['attributes']['class'] if the suggestions represent extra CSS classes.
  */
 function theme_get_suggestions($args, $base, $delimiter = '__') {
@@ -2780,12 +2754,6 @@ function template_preprocess_maintenance_page(&$variables) {
     $variables['attributes']['class'][] = 'one-sidebar';
     $variables['attributes']['class'][] = 'sidebar-' . $variables['layout'];
   }
-
-  // Dead databases will show error messages so supplying this template will
-  // allow themers to override the page and the content completely.
-  if (isset($variables['db_is_active']) && !$variables['db_is_active']) {
-    $variables['theme_hook_suggestion'] = 'maintenance_page__offline';
-  }
 }
 
 /**
@@ -2817,5 +2785,4 @@ function template_preprocess_region(&$variables) {
   $variables['region'] = $variables['elements']['#region'];
 
   $variables['attributes']['class'][] = drupal_region_class($variables['region']);
-  $variables['theme_hook_suggestions'][] = 'region__' . $variables['region'];
 }
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 2592137..71e7f31 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -968,6 +968,28 @@ function block_rebuild() {
 }
 
 /**
+ * Implements hook_template_suggestions_HOOK().
+ */
+function block_template_suggestions_block($variables) {
+  $suggestions = array();
+  $block = $variables['elements']['#block'];
+
+  $suggestions[] = 'block__' . $block->region;
+  $suggestions[] = 'block__' . $block->module;
+  // Hyphens (-) and underscores (_) play a special role in theme suggestions.
+  // Theme suggestions should only contain underscores, because within
+  // drupal_find_theme_templates(), underscores are converted to hyphens to
+  // match template file names, and then converted back to underscores to match
+  // pre-processing and other function names. So if your theme suggestion
+  // contains a hyphen, it will end up as an underscore after this conversion,
+  // and your function names won't be recognized. So, we need to convert
+  // hyphens to underscores in block deltas for the theme suggestions.
+  $suggestions[] = 'block__' . $block->module . '__' . strtr($block->delta, '-', '_');
+
+  return $suggestions;
+}
+
+/**
  * Processes variables for block.tpl.php.
  *
  * Prepares the values passed to the theme_block function to be passed
@@ -1003,18 +1025,6 @@ function template_preprocess_block(&$variables) {
   // Add default class for block content.
   $variables['content_attributes']['class'][] = 'content';
 
-  $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->region;
-  $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->module;
-  // Hyphens (-) and underscores (_) play a special role in theme suggestions.
-  // Theme suggestions should only contain underscores, because within
-  // drupal_find_theme_templates(), underscores are converted to hyphens to
-  // match template file names, and then converted back to underscores to match
-  // pre-processing and other function names. So if your theme suggestion
-  // contains a hyphen, it will end up as an underscore after this conversion,
-  // and your function names won't be recognized. So, we need to convert
-  // hyphens to underscores in block deltas for the theme suggestions.
-  $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->module . '__' . strtr($variables['block']->delta, '-', '_');
-
   // Create a valid HTML ID and make sure it is unique.
   $variables['block_html_id'] = drupal_html_id('block-' . $variables['block']->module . '-' . $variables['block']->delta);
 }
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 8157971..af05631 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -1042,6 +1042,21 @@ function field_extract_bundle($entity_type, $bundle) {
 }
 
 /**
+ * Implements hook_template_suggestions_HOOK().
+ */
+function field_template_suggestions_field($variables) {
+  $element = $variables['element'];
+
+  // Add specific suggestions that can override the default implementation.
+  return array(
+    'field__' . $element['#field_type'],
+    'field__' . $element['#field_name'],
+    'field__' . $element['#bundle'],
+    'field__' . $element['#field_name'] . '__' . $element['#bundle'],
+  );
+}
+
+/**
  * Theme preprocess function for theme_field() and field.tpl.php.
  *
  * @see theme_field()
@@ -1087,14 +1102,6 @@ function template_preprocess_field(&$variables, $hook) {
   if ($element['#label_display'] == 'inline') {
     $variables['attributes']['class'][] = 'clearfix';
   }
-
-  // Add specific suggestions that can override the default implementation.
-  $variables['theme_hook_suggestions'] = array(
-    'field__' . $element['#field_type'],
-    'field__' . $element['#field_name'],
-    'field__' . $element['#bundle'],
-    'field__' . $element['#field_name'] . '__' . $element['#bundle'],
-  );
 }
 
 /**
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 30b1e6b..e1fed21 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -1036,6 +1036,31 @@ function forum_get_topics($tid, $sortby, $forum_per_page) {
 }
 
 /**
+ * Implements hook_template_suggestions_HOOK().
+ */
+function forum_template_suggestions_forums($variables) {
+  $suggestions = array();
+
+  // Provide separate template suggestions based on what's being output. Topic id is also accounted for.
+  // Check both variables to be safe then the inverse. Forums with topic ID's take precedence.
+  if ($variables['forums'] && !$variables['topics']) {
+    $suggestions[] = 'forums__containers';
+    $suggestions[] = 'forums__' . $variables['tid'];
+    $suggestions[] = 'forums__containers__' . $variables['tid'];
+  }
+  elseif (!$variables['forums'] && $variables['topics']) {
+    $suggestions[] = 'forums__topics';
+    $suggestions[] = 'forums__' . $variables['tid'];
+    $suggestions[] = 'forums__topics__' . $variables['tid'];
+  }
+  else {
+    $suggestions[] = 'forums__' . $variables['tid'];
+  }
+
+  return $suggestions;
+}
+
+/**
  * Implements hook_preprocess_HOOK() for block.tpl.php.
  */
 function forum_preprocess_block(&$variables) {
@@ -1106,23 +1131,6 @@ function template_preprocess_forums(&$variables) {
     else {
       $variables['topics'] = '';
     }
-
-    // Provide separate template suggestions based on what's being output. Topic id is also accounted for.
-    // Check both variables to be safe then the inverse. Forums with topic ID's take precedence.
-    if ($variables['forums'] && !$variables['topics']) {
-      $variables['theme_hook_suggestions'][] = 'forums__containers';
-      $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid'];
-      $variables['theme_hook_suggestions'][] = 'forums__containers__' . $variables['tid'];
-    }
-    elseif (!$variables['forums'] && $variables['topics']) {
-      $variables['theme_hook_suggestions'][] = 'forums__topics';
-      $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid'];
-      $variables['theme_hook_suggestions'][] = 'forums__topics__' . $variables['tid'];
-    }
-    else {
-      $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid'];
-    }
-
   }
   else {
     drupal_set_title(t('No forums defined'));
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 349aab5..0ee758f 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -281,6 +281,17 @@ function node_field_display_node_alter(&$display, $context) {
 }
 
 /**
+ * Implements hook_template_suggestions_HOOK().
+ */
+function node_template_suggestions_node($variables) {
+  return array(
+    'node__' . $variables['nid'],
+    'node__' . $variables['type'] . '__' . $variables['langcode'],
+    'node__' . $variables['type'],
+  );
+}
+
+/**
  * Entity uri callback.
  *
  * @param Drupal\node\Node $node
@@ -1383,10 +1394,6 @@ function template_preprocess_node(&$variables) {
   if (isset($variables['preview'])) {
     $variables['attributes']['class'][] = 'preview';
   }
-
-  // Clean up name so there are no underscores.
-  $variables['theme_hook_suggestions'][] = 'node__' . $node->type;
-  $variables['theme_hook_suggestions'][] = 'node__' . $node->nid;
 }
 
 /**
diff --git a/core/modules/poll/poll.module b/core/modules/poll/poll.module
index 4ab2cb8..5e5eec4 100644
--- a/core/modules/poll/poll.module
+++ b/core/modules/poll/poll.module
@@ -776,6 +776,20 @@ function poll_vote($form, &$form_state) {
 }
 
 /**
+ * Implements hook_template_suggestions_HOOK().
+ */
+function poll_template_suggestions_poll_vote($variables) {
+  $suggestions = array();
+
+  // When rendering as a block, suggest a separate template.
+  if ($variables['form']['#block']) {
+    $suggestions[] = 'poll_vote__block';
+  }
+
+  return $suggestions;
+}
+
+/**
  * Implements hook_preprocess_HOOK() for block.tpl.php.
  */
 function poll_preprocess_block(&$variables) {
@@ -796,9 +810,6 @@ function template_preprocess_poll_vote(&$variables) {
   $variables['vote'] = drupal_render($form['vote']);
   $variables['rest'] = drupal_render_children($form);
   $variables['block'] = $form['#block'];
-  if ($variables['block']) {
-    $variables['theme_hook_suggestions'][] = 'poll_vote__block';
-  }
 }
 
 /**
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index 7613465..2fb7c84 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -156,6 +156,20 @@ function search_block_view($delta = '') {
 }
 
 /**
+ * Implements hook_template_suggestions_HOOK().
+ */
+function search_template_suggestions_search_result($variables) {
+  return array('search_result__' . $variables['module']);
+}
+
+/**
+ * Implements hook_template_suggestions_HOOK().
+ */
+function search_template_suggestions_search_results($variables) {
+  return array('search_results__' . $variables['module']);
+}
+
+/**
  * Implements hook_preprocess_HOOK() for block.tpl.php.
  */
 function search_preprocess_block(&$variables) {
diff --git a/core/modules/search/search.pages.inc b/core/modules/search/search.pages.inc
index 337fe85..ae5ca73 100644
--- a/core/modules/search/search.pages.inc
+++ b/core/modules/search/search.pages.inc
@@ -91,7 +91,6 @@ function template_preprocess_search_results(&$variables) {
     $variables['search_results'] .= theme('search_result', array('result' => $result, 'module' => $variables['module']));
   }
   $variables['pager'] = theme('pager', array('tags' => NULL));
-  $variables['theme_hook_suggestions'][] = 'search_results__' . $variables['module'];
 }
 
 /**
@@ -132,7 +131,6 @@ function template_preprocess_search_result(&$variables) {
   // Provide separated and grouped meta information..
   $variables['info_split'] = $info;
   $variables['info'] = implode(' - ', $info);
-  $variables['theme_hook_suggestions'][] = 'search_result__' . $variables['module'];
 }
 
 /**
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index de7241f..6982556 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -1162,6 +1162,38 @@ function system_menu() {
 }
 
 /**
+ * Implements hook_template_suggestions_HOOK().
+ */
+function system_template_suggestions_html($variables) {
+  return theme_get_suggestions(arg(), 'html');
+}
+
+/**
+ * Implements hook_template_suggestions_HOOK().
+ */
+function system_template_suggestions_page($variables) {
+  return theme_get_suggestions(arg(), 'page');
+}
+
+/**
+ * Implements hook_template_suggestions_HOOK().
+ */
+function system_template_suggestions_maintenance_page($variables) {
+  // Dead databases will show error messages so supplying this template will
+  // allow themers to override the page and the content completely.
+  if (isset($variables['db_is_active']) && !$variables['db_is_active']) {
+    return array('maintenance_page__offline');
+  }
+}
+
+/**
+ * Implements hook_template_suggestions_HOOK().
+ */
+function system_template_suggestions_region($variables) {
+  return array('region__' . $variables['region']);
+}
+
+/**
  * Page callback; Execute cron tasks.
  *
  * @see system_cron_access().
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index a772e02..3b01155 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -622,6 +622,19 @@ function taxonomy_term_view(Term $term, $view_mode = 'full', $langcode = NULL) {
 }
 
 /**
+ * Implements hook_template_suggestions_HOOK().
+ */
+function taxonomy_template_suggestions_taxonomy_term($variables) {
+  $suggestions = array();
+  $term = $variables['term'];
+
+  $suggestions[] = 'taxonomy_term__' . $term->vocabulary_machine_name;
+  $suggestions[] = 'taxonomy_term__' . $term->tid;
+
+  return $suggestions;
+}
+
+/**
  * Process variables for taxonomy-term.tpl.php.
  */
 function template_preprocess_taxonomy_term(&$variables) {
@@ -652,9 +665,6 @@ function template_preprocess_taxonomy_term(&$variables) {
   // Gather classes, and clean up name so there are no underscores.
   $vocabulary_name_css = str_replace('_', '-', $term->vocabulary_machine_name);
   $variables['attributes']['class'][] = 'vocabulary-' . $vocabulary_name_css;
-
-  $variables['theme_hook_suggestions'][] = 'taxonomy_term__' . $term->vocabulary_machine_name;
-  $variables['theme_hook_suggestions'][] = 'taxonomy_term__' . $term->tid;
 }
 
 /**
