diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 009bb33..e7aaca2 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1156,7 +1156,7 @@ function template_preprocess_maintenance_task_list(&$variables) {
  * See the @link themeable Default theme implementations topic @endlink for
  * details.
  */
-function template_preprocess(&$variables, $hook, $info) {
+function template_preprocess(&$variables, $context) {
   // Tell all templates where they are located.
   $variables['directory'] = \Drupal::theme()->getActiveTheme()->getPath();
 
@@ -1175,8 +1175,8 @@ function template_preprocess(&$variables, $hook, $info) {
 
   // When theming a render element, merge its #attributes into
   // $variables['attributes'].
-  if (isset($info['render element'])) {
-    $key = $info['render element'];
+  if (isset($context['info']['render element'])) {
+    $key = $context['info']['render element'];
     if (isset($variables[$key]['#attributes'])) {
       $variables['attributes'] = NestedArray::mergeDeep($variables['attributes'], $variables[$key]['#attributes']);
     }
@@ -1307,7 +1307,7 @@ function template_preprocess_html(&$variables) {
  * inside "modules/system/page.html.twig". Look in there for the full list of
  * variables.
  */
-function template_preprocess_page(&$variables) {
+function template_preprocess_page(&$variables, $context) {
   $language_interface = \Drupal::languageManager()->getCurrentLanguage();
   $site_config = \Drupal::config('system.site');
 
@@ -1349,6 +1349,11 @@ function template_preprocess_page(&$variables) {
   if ($node = \Drupal::routeMatch()->getParameter('node')) {
     $variables['node'] = $node;
   }
+
+  // @todo Remove once https://drupal.org/node/939462 is fixed.
+  if ($context['suggestion'] !== 'page' && function_exists($function = 'template_preprocess_' . $context['suggestion'])) {
+    $function($variables, $context);
+  }
 }
 
 /**
@@ -1432,9 +1437,7 @@ function theme_get_suggestions($args, $base, $delimiter = '__') {
  *
  * @see system_page_attachments()
  */
-function template_preprocess_maintenance_page(&$variables) {
-  // @todo Rename the templates to page--maintenance + page--install.
-  template_preprocess_page($variables);
+function template_preprocess_page__maintenance(&$variables) {
 
   // @see system_page_attachments()
   $variables['#attached']['library'][] = 'system/maintenance';
@@ -1449,10 +1452,9 @@ function template_preprocess_maintenance_page(&$variables) {
  *   An associative array containing:
  *   - content - An array of page content.
  *
- * @see template_preprocess_maintenance_page()
+ * @see template_preprocess_page__maintenance()
  */
-function template_preprocess_install_page(&$variables) {
-  template_preprocess_maintenance_page($variables);
+function template_preprocess_page__maintenance__install(&$variables) {
 
   // Override the site name that is displayed on the page, since Drupal is
   // still in the process of being installed.
@@ -1492,7 +1494,7 @@ function template_preprocess_region(&$variables) {
  *   - content_attributes: A string containing the attributes for the content's
  *     div.
  */
-function template_preprocess_field(&$variables, $hook) {
+function template_preprocess_field(&$variables) {
   $element = $variables['element'];
 
   // Creating variables for the template.
@@ -1675,6 +1677,16 @@ function drupal_common_theme() {
     'page' => array(
       'render element' => 'page',
     ),
+    'page__maintenance' => array(
+      'base hook' => 'page',
+      'render element' => 'page',
+      'template' => 'page--maintenance',
+    ),
+    'page__maintenance__install' => array(
+      'base hook' => 'page',
+      'render element' => 'page',
+      'template' => 'page--maintenance--install',
+    ),
     'region' => array(
       'render element' => 'elements',
     ),
@@ -1737,12 +1749,6 @@ function drupal_common_theme() {
       'function' => 'theme_indentation',
     ),
     // From theme.maintenance.inc.
-    'maintenance_page' => array(
-      'render element' => 'page',
-    ),
-    'install_page' => array(
-      'render element' => 'page',
-    ),
     'maintenance_task_list' => array(
       'variables' => array('items' => NULL, 'active' => NULL,  'variant' => NULL),
     ),
diff --git a/core/lib/Drupal/Core/Render/theme.api.php b/core/lib/Drupal/Core/Render/theme.api.php
index 689dce0..c2fb0f0 100644
--- a/core/lib/Drupal/Core/Render/theme.api.php
+++ b/core/lib/Drupal/Core/Render/theme.api.php
@@ -463,29 +463,32 @@ function hook_form_system_theme_settings_alter(&$form, \Drupal\Core\Form\FormSta
  *
  * @param $variables
  *   The variables array (modify in place).
- * @param $hook
- *   The name of the theme hook.
- */
-function hook_preprocess(&$variables, $hook) {
- static $hooks;
-
+ * @param $context
+ *   The context for which this preprocess function is running. This includes:
+ *   - hook:
+ *     @todo Document this after figuring out what it is. It's not necessarily
+ *       the same as what was passed to _theme() as $hook, nor is it
+ *       necessarily the same as 'suggestion'. What is it?
+ *   - suggestions: The array of theme hook suggestions that were searched in
+ *     order to find one that was implemented via a template or function.
+ *   - suggestion: The theme hook suggestion that was found and therefore
+ *     will be rendered after variable processing is complete.
+ *   - info: The theme registry information for the matching hook/suggestion.
+*/
+function hook_preprocess(&$variables, $context) {
   // Add contextual links to the variables, if the user has permission.
 
   if (!\Drupal::currentUser()->hasPermission('access contextual links')) {
     return;
   }
 
-  if (!isset($hooks)) {
-    $hooks = theme_get_registry();
-  }
-
   // Determine the primary theme function argument.
-  if (isset($hooks[$hook]['variables'])) {
-    $keys = array_keys($hooks[$hook]['variables']);
+  if (isset($context['info']['variables'])) {
+    $keys = array_keys($context['info']['variables']);
     $key = $keys[0];
   }
   else {
-    $key = $hooks[$hook]['render element'];
+    $key = $context['info']['render element'];
   }
 
   if (isset($variables[$key])) {
diff --git a/core/lib/Drupal/Core/Theme/ThemeManager.php b/core/lib/Drupal/Core/Theme/ThemeManager.php
index f6c5714..25df4a7 100644
--- a/core/lib/Drupal/Core/Theme/ThemeManager.php
+++ b/core/lib/Drupal/Core/Theme/ThemeManager.php
@@ -289,9 +289,15 @@ public function render($hook, array $variables) {
       }
     }
     if (isset($info['preprocess functions'])) {
+      $context = array(
+        'hook' => $hook,
+        'suggestions' => $suggestions,
+        'suggestion' => isset($suggestion) ? $suggestion : NULL,
+        'info' => $info,
+      );
       foreach ($info['preprocess functions'] as $preprocessor_function) {
         if (function_exists($preprocessor_function)) {
-          $preprocessor_function($variables, $hook, $info);
+          $preprocessor_function($variables, $context);
         }
       }
       // Allow theme preprocess functions to set $variables['#attached'] and
@@ -355,7 +361,13 @@ public function render($hook, array $variables) {
       // adding some new variable to track that.
       if (!isset($variables['directory'])) {
         $default_template_variables = array();
-        template_preprocess($default_template_variables, $hook, $info);
+        $context = array(
+          'hook' => $hook,
+          'suggestions' => $suggestions,
+          'suggestion' => isset($suggestion) ? $suggestion : NULL,
+          'info' => $info,
+        );
+        template_preprocess($default_template_variables, $context);
         $variables += $default_template_variables;
       }
       if (!isset($default_attributes)) {
diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module
index a672e4e..088b998 100644
--- a/core/modules/contextual/contextual.module
+++ b/core/modules/contextual/contextual.module
@@ -101,14 +101,14 @@ function contextual_help($route_name, RouteMatchInterface $route_match) {
  * @see contextual_page_attachments()
  * @see \Drupal\contextual\ContextualController::render()
  */
-function contextual_preprocess(&$variables, $hook, $info) {
+function contextual_preprocess(&$variables, $context) {
   // Determine the primary theme function argument.
-  if (!empty($info['variables'])) {
-    $keys = array_keys($info['variables']);
+  if (!empty($context['info']['variables'])) {
+    $keys = array_keys($context['info']['variables']);
     $key = $keys[0];
   }
-  elseif (!empty($info['render element'])) {
-    $key = $info['render element'];
+  elseif (!empty($context['info']['render element'])) {
+    $key = $context['info']['render element'];
   }
   if (!empty($key) && isset($variables[$key])) {
     $element = $variables[$key];
diff --git a/core/modules/system/templates/install-page.html.twig b/core/modules/system/templates/page--maintenance--install.html.twig
similarity index 100%
rename from core/modules/system/templates/install-page.html.twig
rename to core/modules/system/templates/page--maintenance--install.html.twig
diff --git a/core/modules/system/templates/maintenance-page.html.twig b/core/modules/system/templates/page--maintenance.html.twig
similarity index 100%
rename from core/modules/system/templates/maintenance-page.html.twig
rename to core/modules/system/templates/page--maintenance.html.twig
diff --git a/core/themes/bartik/bartik.theme b/core/themes/bartik/bartik.theme
index 4f83a9e..6daad13 100644
--- a/core/themes/bartik/bartik.theme
+++ b/core/themes/bartik/bartik.theme
@@ -40,7 +40,7 @@ function bartik_preprocess_html(&$variables) {
 /**
  * Implements hook_preprocess_HOOK() for page templates.
  */
-function bartik_preprocess_page(&$variables) {
+function bartik_preprocess_page(&$variables, $context) {
   // Set the options that apply to both page and maintenance page.
   _bartik_process_page($variables);
 
@@ -60,12 +60,17 @@ function bartik_preprocess_page(&$variables) {
     // Make sure the shortcut link is the first item in title_suffix.
     $variables['title_suffix']['add_or_remove_shortcut']['#weight'] = -100;
   }
+
+  // @todo Remove once https://drupal.org/node/939462 is fixed.
+  if ($context['suggestion'] !== 'page' && function_exists($function = 'bartik_preprocess_' . $context['suggestion'])) {
+    $function($variables, $context);
+  }
 }
 
 /**
- * Implements hook_preprocess_HOOK() for maintenance-page.html.twig.
+ * Implements hook_preprocess_HOOK().
  */
-function bartik_preprocess_maintenance_page(&$variables) {
+function bartik_preprocess_page__maintenance(&$variables) {
   // By default, site_name is set to Drupal if no db connection is available
   // or during site installation. Setting site_name to an empty string makes
   // the site and update pages look cleaner.
diff --git a/core/themes/bartik/templates/maintenance-page.html.twig b/core/themes/bartik/templates/page--maintenance.html.twig
similarity index 100%
rename from core/themes/bartik/templates/maintenance-page.html.twig
rename to core/themes/bartik/templates/page--maintenance.html.twig
diff --git a/core/themes/seven/seven.theme b/core/themes/seven/seven.theme
index b508d94..34943a2 100644
--- a/core/themes/seven/seven.theme
+++ b/core/themes/seven/seven.theme
@@ -27,13 +27,18 @@ function seven_preprocess_html(&$variables) {
 /**
  * Implements hook_preprocess_HOOK() for page templates.
  */
-function seven_preprocess_page(&$variables) {
+function seven_preprocess_page(&$variables, $context) {
   $variables['primary_local_tasks'] = $variables['tabs'];
   unset($variables['primary_local_tasks']['#secondary']);
   $variables['secondary_local_tasks'] = array(
     '#theme' => 'menu_local_tasks',
     '#secondary' => isset($variables['tabs']['#secondary']) ? $variables['tabs']['#secondary'] : '',
   );
+
+  // @todo Remove once https://drupal.org/node/939462 is fixed.
+  if ($context['suggestion'] !== 'page' && function_exists($function = 'seven_preprocess_' . $context['suggestion'])) {
+    $function($variables, $context);
+  }
 }
 
 /**
@@ -141,17 +146,17 @@ function seven_element_info_alter(&$type) {
 }
 
 /**
- * Implements hook_preprocess_install_page().
+ * Implements hook_preprocess_HOOK().
  */
-function seven_preprocess_install_page(&$variables) {
+function seven_preprocess_page__maintenance__install(&$variables) {
   // Seven has custom styling for the install page.
   $variables['#attached']['library'][] = 'seven/install-page';
 }
 
 /**
- * Implements hook_preprocess_maintenance_page().
+ * Implements hook_preprocess_HOOK().
  */
-function seven_preprocess_maintenance_page(&$variables) {
+function seven_preprocess_page__maintenance(&$variables) {
   // Seven has custom styling for the maintenance page.
   $variables['#attached']['library'][] = 'seven/maintenance-page';
 }
diff --git a/core/themes/seven/templates/install-page.html.twig b/core/themes/seven/templates/page--maintenance--install.html.twig
similarity index 100%
rename from core/themes/seven/templates/install-page.html.twig
rename to core/themes/seven/templates/page--maintenance--install.html.twig
diff --git a/core/themes/seven/templates/maintenance-page.html.twig b/core/themes/seven/templates/page--maintenance.html.twig
similarity index 100%
rename from core/themes/seven/templates/maintenance-page.html.twig
rename to core/themes/seven/templates/page--maintenance.html.twig
