diff --git a/core/core.services.yml b/core/core.services.yml
index d10db5b..4cd9124 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -268,6 +268,10 @@ services:
     class: Symfony\Component\Routing\RequestContext
     calls:
       - [fromRequest, ['@request']]
+  router.admin_context:
+    class: Drupal\Core\Routing\AdminContext
+    calls:
+      - [setRequest, ['@?request=']]
   router.route_provider:
     class: Drupal\Core\Routing\RouteProvider
     arguments: ['@database', '@router.builder']
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 46449da..57b4ad4 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -2157,8 +2157,8 @@ function _drupal_add_js($data = NULL, $options = NULL) {
           $current_path = current_path();
           $current_path_is_admin = FALSE;
           // The function path_is_admin() is not available on update.php pages.
-          if (!(defined('MAINTENANCE_MODE') && MAINTENANCE_MODE === 'update')) {
-            $current_path_is_admin = path_is_admin($current_path);
+          if (!(defined('MAINTENANCE_MODE'))) {
+            $current_path_is_admin = \Drupal::service('router.admin_context')->isAdminRoute();
           }
           $path = array(
             'basePath' => base_path(),
diff --git a/core/includes/path.inc b/core/includes/path.inc
index 533dcd9..d9f0d5a 100644
--- a/core/includes/path.inc
+++ b/core/includes/path.inc
@@ -5,7 +5,9 @@
  * Functions to handle paths in Drupal.
  */
 
+use Drupal\Core\ParamConverter\ParamNotConvertedException;
 use Drupal\Core\Routing\RequestHelper;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -130,54 +132,18 @@ function path_load($conditions) {
  * @return
  *   TRUE if the path is administrative, FALSE otherwise.
  *
- * @see path_get_admin_paths()
- * @see hook_admin_paths()
- * @see hook_admin_paths_alter()
+ * @deprecated Use \Drupal::service('router.admin_context')->isAdminRoute()
+ *   service instead.
  */
 function path_is_admin($path) {
-  $path_map = &drupal_static(__FUNCTION__);
-  if (!isset($path_map['admin'][$path])) {
-    $patterns = path_get_admin_paths();
-    $path_map['admin'][$path] = drupal_match_path($path, $patterns['admin']);
-    $path_map['non_admin'][$path] = drupal_match_path($path, $patterns['non_admin']);
+  try {
+    $parameters = \Drupal::service('router')->match('/' . $path);
+    $route = $parameters[RouteObjectInterface::ROUTE_OBJECT];
+    return \Drupal::service('router.admin_context')->isAdminRoute($route);
   }
-  return $path_map['admin'][$path] && !$path_map['non_admin'][$path];
-}
-
-/**
- * Gets a list of administrative and non-administrative paths.
- *
- * @return array
- *   An associative array containing the following keys:
- *   'admin': An array of administrative paths and regular expressions
- *            in a format suitable for drupal_match_path().
- *   'non_admin': An array of non-administrative paths and regular expressions.
- *
- * @see hook_admin_paths()
- * @see hook_admin_paths_alter()
- */
-function path_get_admin_paths() {
-  $patterns = &drupal_static(__FUNCTION__);
-  if (!isset($patterns)) {
-    $paths = \Drupal::moduleHandler()->invokeAll('admin_paths');
-    drupal_alter('admin_paths', $paths);
-    // Combine all admin paths into one array, and likewise for non-admin paths,
-    // for easier handling.
-    $patterns = array();
-    $patterns['admin'] = array();
-    $patterns['non_admin'] = array();
-    foreach ($paths as $path => $enabled) {
-      if ($enabled) {
-        $patterns['admin'][] = $path;
-      }
-      else {
-        $patterns['non_admin'][] = $path;
-      }
-    }
-    $patterns['admin'] = implode("\n", $patterns['admin']);
-    $patterns['non_admin'] = implode("\n", $patterns['non_admin']);
+  catch (ParamNotConvertedException $e) {
+    return FALSE;
   }
-  return $patterns;
 }
 
 /**
diff --git a/core/lib/Drupal/Core/Routing/AdminContext.php b/core/lib/Drupal/Core/Routing/AdminContext.php
new file mode 100644
index 0000000..0937848
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/AdminContext.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Routing\AdminContext.
+ */
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Provides a helper class to determine whether the route is an admin one.
+ */
+class AdminContext {
+
+  /**
+   * The route object.
+   *
+   * @var \Symfony\Component\Routing\Route
+   */
+  protected $route;
+
+  /**
+   * Sets the request object to use.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request object.
+   */
+  public function setRequest(Request $request) {
+    $this->route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT);
+  }
+
+  /**
+   * Determines whether the active route is an admin one.
+   *
+   * @param \Symfony\Component\Routing\Route $route
+   *   (optional) The route to determine whether it is an admin one. Per default
+   *   this falls back to the route object on the active request.
+   *
+   * @return bool
+   *   Returns TRUE if the route is an admin one, otherwise FALSE.
+   */
+  public function isAdminRoute(Route $route = NULL) {
+    if (!$route) {
+      $route = $this->route;
+      if (!$route) {
+        return FALSE;
+      }
+    }
+    return (bool) $route->getOption('_admin_route');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[RoutingEvents::ALTER] = array('onAlterRoutes', -10);
+    return $events;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/RouteSubscriberBase.php b/core/lib/Drupal/Core/Routing/RouteSubscriberBase.php
index 1172428..f7cf537 100644
--- a/core/lib/Drupal/Core/Routing/RouteSubscriberBase.php
+++ b/core/lib/Drupal/Core/Routing/RouteSubscriberBase.php
@@ -25,8 +25,7 @@
    *   The provider these routes belong to. For dynamically added routes, the
    *   provider name will be 'dynamic_routes'.
    */
-  protected function alterRoutes(RouteCollection $collection, $provider) {
-  }
+  abstract protected function alterRoutes(RouteCollection $collection, $provider);
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index daea82c..a5e9ec5 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -520,18 +520,6 @@ function block_menu_delete($menu) {
 }
 
 /**
- * Implements hook_admin_paths().
- */
-function block_admin_paths() {
-  $paths = array(
-    // Exclude the block demonstration page from admin treatment.
-    // This allows us to present this page in its true form, full page.
-    'admin/structure/block/demo/*' => FALSE,
-  );
-  return $paths;
-}
-
-/**
  * Implements hook_language_delete().
  *
  * Delete the potential block visibility settings of the deleted language.
diff --git a/core/modules/block/block.routing.yml b/core/modules/block/block.routing.yml
index e7bbf22..47bb915 100644
--- a/core/modules/block/block.routing.yml
+++ b/core/modules/block/block.routing.yml
@@ -5,6 +5,8 @@ block.admin_demo:
   requirements:
     _access_theme: 'TRUE'
     _permission: 'administer blocks'
+  options:
+    _admin_route: FALSE
 
 block.admin_block_delete:
   path: '/admin/structure/block/manage/{block}/delete'
diff --git a/core/modules/block/custom_block/custom_block.module b/core/modules/block/custom_block/custom_block.module
index cdf90c7..e9bfeef 100644
--- a/core/modules/block/custom_block/custom_block.module
+++ b/core/modules/block/custom_block/custom_block.module
@@ -211,17 +211,3 @@ function custom_block_add_body_field($block_type_id, $label = 'Block body') {
 
   return $instance;
 }
-
-/**
- * Implements hook_admin_paths().
- */
-function custom_block_admin_paths() {
-  $paths = array(
-    'block/add' => TRUE,
-    'block/add/*' => TRUE,
-    'block/*' => TRUE,
-    'block/*/delete' => TRUE,
-    'admin/structure/block/custom-blocks/*' => TRUE,
-  );
-  return $paths;
-}
diff --git a/core/modules/block/custom_block/custom_block.routing.yml b/core/modules/block/custom_block/custom_block.routing.yml
index 622d08d..6cea531 100644
--- a/core/modules/block/custom_block/custom_block.routing.yml
+++ b/core/modules/block/custom_block/custom_block.routing.yml
@@ -10,6 +10,8 @@ custom_block.add_page:
   defaults:
     _content: 'Drupal\custom_block\Controller\CustomBlockController::add'
     _title: 'Add custom block'
+  options:
+    _admin_route: TRUE
   requirements:
     _permission: 'administer blocks'
 
@@ -18,6 +20,8 @@ custom_block.add_form:
   defaults:
     _content: 'Drupal\custom_block\Controller\CustomBlockController::addForm'
     _title_callback: 'Drupal\custom_block\Controller\CustomBlockController::getAddFormTitle'
+  options:
+    _admin_route: TRUE
   requirements:
     _permission: 'administer blocks'
 
@@ -28,11 +32,15 @@ custom_block.type_delete:
     _title: 'Delete'
   requirements:
     _entity_access: 'custom_block_type.delete'
+  options:
+    _admin_route: TRUE
 
 custom_block.edit:
   path: '/block/{custom_block}'
   defaults:
     _entity_form: 'custom_block.edit'
+  options:
+    _admin_route: TRUE
   requirements:
     _entity_access: 'custom_block.update'
 
@@ -40,6 +48,8 @@ custom_block.delete:
   path: '/block/{custom_block}/delete'
   defaults:
     _entity_form: 'custom_block.delete'
+  options:
+    _admin_route: TRUE
   requirements:
     _entity_access: 'custom_block.delete'
 
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index 8fe9e41..5f3fb9d 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -204,19 +204,6 @@ function book_menu_link_defaults() {
 }
 
 /**
- * Implements hook_admin_paths().
- */
-function book_admin_paths() {
-  if (\Drupal::config('node.settings')->get('use_admin_theme')) {
-    $paths = array(
-      'node/*/outline' => TRUE,
-      'node/*/outline/remove' => TRUE,
-    );
-    return $paths;
-  }
-}
-
-/**
  * Returns an array of all books.
  *
  * @todo Remove in favor of BookManager Service. http://drupal.org/node/1963894
diff --git a/core/modules/book/book.routing.yml b/core/modules/book/book.routing.yml
index 75e3ae8..6b49530 100644
--- a/core/modules/book/book.routing.yml
+++ b/core/modules/book/book.routing.yml
@@ -38,6 +38,8 @@ book.outline:
   requirements:
     _permission: 'administer book outlines'
     _entity_access: 'node.view'
+  options:
+    _node_admin_route: TRUE
 
 book.admin_edit:
   path: '/admin/structure/book/{node}'
@@ -56,6 +58,7 @@ book.remove:
     _title: 'Remove from outline'
   options:
     _access_mode: 'ALL'
+    _node_admin_route: TRUE
   requirements:
     _permission: 'administer book outlines'
     _entity_access: 'node.view'
diff --git a/core/modules/edit/edit.module b/core/modules/edit/edit.module
index 87732dc..9e62a9f 100644
--- a/core/modules/edit/edit.module
+++ b/core/modules/edit/edit.module
@@ -37,8 +37,7 @@ function edit_page_build(&$page) {
   }
 
   // In-place editing is only supported on the front-end.
-  $path = \Drupal::request()->attributes->get('_system_path');
-  if (path_is_admin($path)) {
+  if (\Drupal::service('router.admin_context')->isAdminRoute()) {
     return;
   }
 
diff --git a/core/modules/locale/lib/Drupal/locale/ParamConverter/LocaleAdminPathConfigEntityConverter.php b/core/modules/locale/lib/Drupal/locale/ParamConverter/LocaleAdminPathConfigEntityConverter.php
index 2eca5dd..29be8ed 100644
--- a/core/modules/locale/lib/Drupal/locale/ParamConverter/LocaleAdminPathConfigEntityConverter.php
+++ b/core/modules/locale/lib/Drupal/locale/ParamConverter/LocaleAdminPathConfigEntityConverter.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\locale\ParamConverter;
 
+use Drupal\Core\Routing\AdminContext;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Route;
 use Drupal\Core\Config\ConfigFactory;
@@ -38,14 +39,28 @@ class LocaleAdminPathConfigEntityConverter extends EntityConverter {
   protected $configFactory;
 
   /**
+   * The route admin context to determine whether a route is an admin one.
+   *
+   * @var \Drupal\Core\Routing\AdminContext
+   */
+  protected $adminContext;
+
+  /**
    * Constructs a new EntityConverter.
    *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entityManager
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The config factory.
+   * @param \Drupal\Core\Routing\AdminContext $admin_context
+   *   The route admin context service.
+   *
    */
-  public function __construct(EntityManagerInterface $entity_manager, ConfigFactory $config_factory) {
-    $this->configFactory = $config_factory;
+  public function __construct(EntityManagerInterface $entity_manager, ConfigFactory $config_factory, AdminContext $admin_context) {
     parent::__construct($entity_manager);
+
+    $this->configFactory = $config_factory;
+    $this->adminContext = $admin_context;
   }
 
   /**
@@ -73,9 +88,7 @@ public function applies($definition, $name, Route $route) {
       $entity_type = substr($definition['type'], strlen('entity:'));
       $info = $this->entityManager->getDefinition($entity_type);
       if ($info->isSubclassOf('\Drupal\Core\Config\Entity\ConfigEntityInterface')) {
-        // path_is_admin() needs the path without the leading slash.
-        $path = ltrim($route->getPath(), '/');
-        return path_is_admin($path);
+        return $this->adminContext->isAdminRoute($route);
       }
     }
     return FALSE;
diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml
index b5f4cf4..d400131 100644
--- a/core/modules/locale/locale.services.yml
+++ b/core/modules/locale/locale.services.yml
@@ -3,7 +3,7 @@ services:
     class: Drupal\locale\ParamConverter\LocaleAdminPathConfigEntityConverter
     tags:
       - { name: paramconverter, priority: 5 }
-    arguments: ['@entity.manager', '@config.factory']
+    arguments: ['@entity.manager', '@config.factory', '@router.admin_context']
   locale.config.typed:
     class: Drupal\locale\LocaleConfigManager
     arguments: ['@config.storage', '@config.storage.schema', '@config.storage.installer', '@locale.storage', '@cache.config', '@config.factory']
diff --git a/core/modules/node/lib/Drupal/node/EventSubscriber/NodeAdminRouteSubscriber.php b/core/modules/node/lib/Drupal/node/EventSubscriber/NodeAdminRouteSubscriber.php
new file mode 100644
index 0000000..38cea31
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/EventSubscriber/NodeAdminRouteSubscriber.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\EventSubscriber\NodeAdminRouteSubscriber.
+ */
+
+namespace Drupal\node\EventSubscriber;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Routing\RouteSubscriberBase;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Sets the _admin_route for specific node-related routes.
+ */
+class NodeAdminRouteSubscriber extends RouteSubscriberBase {
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * Constructs a new NodeAdminRouteSubscriber.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory) {
+    $this->configFactory = $config_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterRoutes(RouteCollection $collection, $provider) {
+    if ($this->configFactory->get('node.settings')->get('use_admin_theme')) {
+      foreach ($collection->all() as $route) {
+        if ($route->hasOption('_node_admin_route')) {
+          $route->setOption('_admin_route', TRUE);
+        }
+      }
+    }
+  }
+
+}
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 2a9504b..5c041e6 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -234,26 +234,6 @@ function node_uri(NodeInterface $node) {
 }
 
 /**
- * Implements hook_admin_paths().
- */
-function node_admin_paths() {
-  if (\Drupal::config('node.settings')->get('use_admin_theme')) {
-    $paths = array(
-      'node/*/edit' => TRUE,
-      'node/*/delete' => TRUE,
-      'node/*/revisions' => TRUE,
-      'node/*/revisions/*/revert' => TRUE,
-      'node/*/revisions/*/delete' => TRUE,
-      'node/*/translations' => TRUE,
-      'node/*/translations/*' => TRUE,
-      'node/add' => TRUE,
-      'node/add/*' => TRUE,
-    );
-    return $paths;
-  }
-}
-
-/**
  * Gathers a listing of links to nodes.
  *
  * @param $result
@@ -1373,6 +1353,7 @@ function node_form_system_themes_admin_form_submit($form, &$form_state) {
   \Drupal::config('node.settings')
     ->set('use_admin_theme', $form_state['values']['use_admin_theme'])
     ->save();
+  \Drupal::service('router.builder')->setRebuildNeeded();
 }
 
 /**
diff --git a/core/modules/node/node.routing.yml b/core/modules/node/node.routing.yml
index b16080c..74d63fd 100644
--- a/core/modules/node/node.routing.yml
+++ b/core/modules/node/node.routing.yml
@@ -19,6 +19,8 @@ node.page_edit:
     _entity_form: 'node.edit'
   requirements:
     _entity_access: 'node.update'
+  options:
+    _node_admin_route: TRUE
 
 node.add_page:
   path: '/node/add'
@@ -27,6 +29,7 @@ node.add_page:
     _content: '\Drupal\node\Controller\NodeController::addPage'
   options:
     _access_mode: 'ANY'
+    _node_admin_route: TRUE
   requirements:
     _permission: 'administer content types'
     _node_add_access: 'node'
@@ -38,6 +41,8 @@ node.add:
     _title_callback: '\Drupal\node\Controller\NodeController::addPageTitle'
   requirements:
     _node_add_access: 'node:{node_type}'
+  options:
+    _node_admin_route: TRUE
 
 node.view:
   path: '/node/{node}'
@@ -53,6 +58,8 @@ node.delete_confirm:
     _entity_form: 'node.delete'
   requirements:
     _entity_access: 'node.delete'
+  options:
+    _node_admin_route: TRUE
 
 node.revision_overview:
   path: '/node/{node}/revisions'
@@ -61,6 +68,8 @@ node.revision_overview:
     _content: '\Drupal\node\Controller\NodeController::revisionOverview'
   requirements:
     _access_node_revision: 'view'
+  options:
+    _node_admin_route: TRUE
 
 node.revision_show:
   path: '/node/{node}/revisions/{node_revision}/view'
@@ -77,6 +86,8 @@ node.revision_revert_confirm:
     _title: 'Revert to earlier revision'
   requirements:
     _access_node_revision: 'update'
+  options:
+    _node_admin_route: TRUE
 
 node.revision_delete_confirm:
   path: '/node/{node}/revisions/{node_revision}/delete'
@@ -85,6 +96,8 @@ node.revision_delete_confirm:
     _title: 'Delete earlier revision'
   requirements:
     _access_node_revision: 'delete'
+  options:
+    _node_admin_route: TRUE
 
 node.overview_types:
   path: '/admin/structure/types'
diff --git a/core/modules/node/node.services.yml b/core/modules/node/node.services.yml
index 7329685..e211f7b 100644
--- a/core/modules/node/node.services.yml
+++ b/core/modules/node/node.services.yml
@@ -12,3 +12,8 @@ services:
     arguments: ['@entity.manager']
     tags:
       - { name: access_check, applies_to: _node_add_access }
+  node.admin_path.route_subscriber:
+    class: Drupal\node\EventSubscriber\NodeAdminRouteSubscriber
+    arguments: ['@config.factory']
+    tags:
+      - { name: event_subscriber }
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index a988cf4..f6337b7 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -101,16 +101,6 @@ function shortcut_menu_link_defaults() {
 }
 
 /**
- * Implements hook_admin_paths().
- */
-function shortcut_admin_paths() {
-  $paths = array(
-    'user/*/shortcuts' => TRUE,
-  );
-  return $paths;
-}
-
-/**
  * Access callback for editing a shortcut set.
  *
  * @param Drupal\shortcut\ShortcutSetInterface $shortcut_set
diff --git a/core/modules/shortcut/shortcut.routing.yml b/core/modules/shortcut/shortcut.routing.yml
index 9ec8d64..d0721c0 100644
--- a/core/modules/shortcut/shortcut.routing.yml
+++ b/core/modules/shortcut/shortcut.routing.yml
@@ -74,4 +74,6 @@ shortcut.overview:
     _title: 'Shortcuts'
   requirements:
     _access_shortcut_set_switch: 'TRUE'
+  options:
+    _admin_route: TRUE
 
diff --git a/core/modules/system/lib/Drupal/system/EventSubscriber/AdminRouteSubscriber.php b/core/modules/system/lib/Drupal/system/EventSubscriber/AdminRouteSubscriber.php
new file mode 100644
index 0000000..eebe05c
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/EventSubscriber/AdminRouteSubscriber.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\EventSubscriber\AdminRouteSubscriber
+ */
+
+namespace Drupal\system\EventSubscriber;
+
+use Drupal\Core\Routing\RouteSubscriberBase;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Adds the _admin_route option to each admin route.
+ */
+class AdminRouteSubscriber extends RouteSubscriberBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterRoutes(RouteCollection $collection, $provider) {
+    foreach ($collection->all() as $route) {
+      if (strpos($route->getPath(), '/admin') === 0 && !$route->hasOption('_admin_route')) {
+        $route->setOption('_admin_route', TRUE);
+      }
+    }
+  }
+
+}
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index a996604..4fce6d4 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -47,50 +47,6 @@ function hook_hook_info() {
 }
 
 /**
- * Define administrative paths.
- *
- * Modules may specify whether or not the routing paths they define are
- * to be considered administrative. Other modules may use this information to
- * display those pages differently.
- *
- * To change the administrative status of menu items defined in another module's
- * routing paths, modules should implement hook_admin_paths_alter().
- *
- * @return
- *   An associative array. For each item, the key is the path in question, in
- *   a format acceptable to drupal_match_path(). The value for each item should
- *   be TRUE (for paths considered administrative) or FALSE (for non-
- *   administrative paths).
- *
- * @see drupal_match_path()
- * @see hook_admin_paths_alter()
- */
-function hook_admin_paths() {
-  $paths = array(
-    'mymodule/*/add' => TRUE,
-    'mymodule/*/edit' => TRUE,
-  );
-  return $paths;
-}
-
-/**
- * Redefine administrative paths defined by other modules.
- *
- * @param $paths
- *   An associative array of administrative paths, as defined by implementations
- *   of hook_admin_paths().
- *
- * @see hook_admin_paths()
- */
-function hook_admin_paths_alter(&$paths) {
-  // Treat all user pages as administrative.
-  $paths['user'] = TRUE;
-  $paths['user/*'] = TRUE;
-  // Treat the forum topic node form as a non-administrative page.
-  $paths['node/add/forum'] = FALSE;
-}
-
-/**
  * Perform periodic actions.
  *
  * Modules that require some commands to be executed periodically can
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 065d280..4ef0247 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2348,7 +2348,7 @@ function system_page_build(&$page) {
     'weight' => CSS_SKIN - 10,
     'every_page' => TRUE,
   );
-  if (path_is_admin(current_path())) {
+  if (\Drupal::service('router.admin_context')->isAdminRoute()) {
     $page['#attached']['css'][$path . '/css/system.admin.css'] = array(
       'weight' => CSS_COMPONENT - 10,
     );
@@ -3155,18 +3155,3 @@ function theme_confirm_form($variables) {
 function theme_system_config_form($variables) {
   return drupal_render_children($variables['form']);
 }
-
-/**
- * Implements hook_admin_paths().
- */
-function system_admin_paths() {
-  $paths = array(
-    'admin' => TRUE,
-    'admin/*' => TRUE,
-    'batch' => TRUE,
-    // This page should not be treated as administrative since it outputs its
-    // own content (outside of any administration theme).
-    'admin/reports/status/php' => FALSE,
-  );
-  return $paths;
-}
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index 9be827e..39de86b 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -283,6 +283,10 @@ system.php:
     _controller: 'Drupal\system\Controller\SystemInfoController::php'
   requirements:
     _permission: 'administer site configuration'
+  # This page should not be treated as administrative since it outputs its own
+  # content (outside of any administration theme).
+  options:
+    _admin_route: FALSE
 
 system.admin_index:
   path: '/admin/index'
@@ -387,6 +391,8 @@ system.batch_page.normal:
     _content: '\Drupal\system\Controller\BatchController::batchPage'
   requirements:
     _access: 'TRUE'
+  options:
+    _admin_route: TRUE
 
 system.batch_page.json:
   path: '/batch'
@@ -395,6 +401,8 @@ system.batch_page.json:
   requirements:
     _access: 'TRUE'
     _format: 'json'
+  options:
+    _admin_route: TRUE
 
 system.update:
   path: '/core/update.php'
diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml
index 179bb27..38c30db 100644
--- a/core/modules/system/system.services.yml
+++ b/core/modules/system/system.services.yml
@@ -15,6 +15,10 @@ services:
     class: Drupal\system\PathProcessor\PathProcessorFiles
     tags:
       - { name: path_processor_inbound, priority: 200 }
+  system.admin_path.route_subscriber:
+    class: Drupal\system\EventSubscriber\AdminRouteSubscriber
+    tags:
+      - { name: event_subscriber }
   theme.negotiator.system.batch:
     class: Drupal\system\Theme\BatchNegotiator
     arguments: ['@batch.storage']
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index f2fe6fe..6199f6a 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -273,19 +273,6 @@ function taxonomy_menu_link_defaults() {
 }
 
 /**
- * Implements hook_admin_paths().
- */
-function taxonomy_admin_paths() {
-  $paths = array(
-    'taxonomy/term/*/edit' => TRUE,
-    'taxonomy/term/*/delete' => TRUE,
-    'taxonomy/term/*/translations' => TRUE,
-    'taxonomy/term/*/translations/*' => TRUE,
-  );
-  return $paths;
-}
-
-/**
  * Checks and updates the hierarchy flag of a vocabulary.
  *
  * Checks the current parents of all terms in a vocabulary and updates the
diff --git a/core/modules/taxonomy/taxonomy.routing.yml b/core/modules/taxonomy/taxonomy.routing.yml
index 63b374d..014f957 100644
--- a/core/modules/taxonomy/taxonomy.routing.yml
+++ b/core/modules/taxonomy/taxonomy.routing.yml
@@ -19,6 +19,8 @@ taxonomy.term_edit:
   defaults:
     _entity_form: 'taxonomy_term.default'
     _title: 'Edit term'
+  options:
+    _admin_route: TRUE
   requirements:
     _entity_access: 'taxonomy_term.update'
 
@@ -27,6 +29,8 @@ taxonomy.term_delete:
   defaults:
     _entity_form: 'taxonomy_term.delete'
     _title: 'Delete term'
+  options:
+    _admin_route: TRUE
   requirements:
     _entity_access: 'taxonomy_term.delete'
 
diff --git a/core/modules/tour/tests/tour_test/tour_test.module b/core/modules/tour/tests/tour_test/tour_test.module
index 0df3e57..ca750ec 100644
--- a/core/modules/tour/tests/tour_test/tour_test.module
+++ b/core/modules/tour/tests/tour_test/tour_test.module
@@ -8,16 +8,6 @@
 use Drupal\Core\Entity\EntityInterface;
 
 /**
- * Implements hook_admin_paths().
- */
-function tour_test_admin_paths() {
-  $paths = array(
-    'tour-test-1' => TRUE,
-  );
-  return $paths;
-}
-
-/**
  * Implements hook_menu().
  */
 function tour_test_menu() {
diff --git a/core/modules/tour/tests/tour_test/tour_test.routing.yml b/core/modules/tour/tests/tour_test/tour_test.routing.yml
index 5e4d254..f039dc0 100644
--- a/core/modules/tour/tests/tour_test/tour_test.routing.yml
+++ b/core/modules/tour/tests/tour_test/tour_test.routing.yml
@@ -2,6 +2,8 @@ tour_test.1:
   path: '/tour-test-1'
   defaults:
     _content: '\Drupal\tour_test\Controller\TourTestController::tourTest1'
+  options:
+    _admin_route: TRUE
   requirements:
     _access: 'TRUE'
 
diff --git a/core/modules/user/lib/Drupal/user/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php b/core/modules/user/lib/Drupal/user/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php
index c7a42eb..755df10 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php
@@ -7,7 +7,11 @@
 
 namespace Drupal\user\Plugin\LanguageNegotiation;
 
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Routing\AdminContext;
 use Drupal\language\LanguageNegotiationMethodBase;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -21,7 +25,7 @@
  *   description = @Translation("Account administration pages language setting.")
  * )
  */
-class LanguageNegotiationUserAdmin extends LanguageNegotiationMethodBase {
+class LanguageNegotiationUserAdmin extends LanguageNegotiationMethodBase implements ContainerInjectionInterface {
 
   /**
    * The language negotiation method id.
@@ -29,6 +33,32 @@ class LanguageNegotiationUserAdmin extends LanguageNegotiationMethodBase {
   const METHOD_ID = 'language-user-admin';
 
   /**
+   * The admin context.
+   *
+   * @var \Drupal\Core\Routing\AdminContext
+   */
+  protected $adminContext;
+
+  /**
+   * Constructs a new LanguageNegotiationUserAdmin instance.
+   *
+   * @param \Drupal\Core\Routing\AdminContext $admin_context
+   *   The admin context.
+   */
+  public function __construct(AdminContext $admin_context = NULL) {
+    $this->adminContext = $admin_context;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('router.admin_context')
+    );
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getLangcode(Request $request = NULL) {
@@ -51,17 +81,16 @@ public function getLangcode(Request $request = NULL) {
   /**
    * Checks whether the given path is an administrative one.
    *
-   * @param string $path
-   *   A Drupal path.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request object.
    *
    * @return bool
    *   TRUE if the path is administrative, FALSE otherwise.
    */
   public function isAdminPath(Request $request) {
     $result = FALSE;
-    if ($request && function_exists('path_is_admin')) {
-      $path = urldecode(trim($request->getPathInfo(), '/'));
-      $result = path_is_admin($path);
+    if ($request && $this->adminContext) {
+      $result = $this->adminContext->isAdminRoute($request->attributes->get(RouteObjectInterface::ROUTE_OBJECT));
     }
     return $result;
   }
diff --git a/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php b/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php
index faaeb40..3e27dbf 100644
--- a/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php
+++ b/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php
@@ -9,8 +9,10 @@
 
 use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Routing\AdminContext;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Theme\ThemeNegotiatorInterface;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -40,6 +42,13 @@ class AdminNegotiator implements ThemeNegotiatorInterface {
   protected $entityManager;
 
   /**
+   * The route admin context to determine whether a route is an admin one.
+   *
+   * @var \Drupal\Core\Routing\AdminContext
+   */
+  protected $adminContext;
+
+  /**
    * Creates a new AdminNegotiator instance.
    *
    * @param \Drupal\Core\Session\AccountInterface $user
@@ -49,18 +58,18 @@ class AdminNegotiator implements ThemeNegotiatorInterface {
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
    */
-  public function __construct(AccountInterface $user, ConfigFactory $config_factory, EntityManagerInterface $entity_manager) {
+  public function __construct(AccountInterface $user, ConfigFactory $config_factory, EntityManagerInterface $entity_manager, AdminContext $admin_context) {
     $this->user = $user;
     $this->configFactory = $config_factory;
     $this->entityManager = $entity_manager;
+    $this->adminContext = $admin_context;
   }
 
   /**
    * {@inheritdoc}
    */
   public function applies(Request $request) {
-    $path = $request->attributes->get('_system_path');
-    return ($this->entityManager->hasController('user_role', 'storage') && $this->user->hasPermission('view the administration theme') && path_is_admin($path));
+    return ($this->entityManager->hasController('user_role', 'storage') && $this->user->hasPermission('view the administration theme') && $this->adminContext->isAdminRoute($request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)));
   }
 
   /**
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 4262403..90cb7cb 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -874,20 +874,6 @@ function user_translated_menu_link_alter(MenuLink &$menu_link) {
 }
 
 /**
- * Implements hook_admin_paths().
- */
-function user_admin_paths() {
-  $paths = array(
-    'user/*/cancel' => TRUE,
-    'user/*/edit' => TRUE,
-    'user/*/edit/*' => TRUE,
-    'user/*/translations' => TRUE,
-    'user/*/translations/*' => TRUE,
-  );
-  return $paths;
-}
-
-/**
  * Returns $arg or the user ID of the current user if $arg is '%' or empty.
  *
  * Deprecated. Use %user_uid_optional instead.
diff --git a/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml
index f59aabc..7812eea 100644
--- a/core/modules/user/user.routing.yml
+++ b/core/modules/user/user.routing.yml
@@ -151,6 +151,8 @@ user.edit:
   defaults:
     _entity_form: 'user.default'
     _title_callback: 'Drupal\user\Controller\UserController::userTitle'
+  options:
+    _admin_route: TRUE
   requirements:
     _entity_access: 'user.update'
 
@@ -159,6 +161,8 @@ user.cancel:
   defaults:
     _title: 'Cancel account'
     _entity_form: 'user.cancel'
+  options:
+    _admin_route: TRUE
   requirements:
     _entity_access: 'user.delete'
 
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index 23d8706..b0c1694 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -27,7 +27,7 @@ services:
       - { name: event_subscriber }
   theme.negotiator.admin_theme:
     class: Drupal\user\Theme\AdminNegotiator
-    arguments: ['@current_user', '@config.factory', '@entity.manager']
+    arguments: ['@current_user', '@config.factory', '@entity.manager', '@router.admin_context']
     tags:
       - { name: theme_negotiator, priority: -40 }
   user.permissions_hash:
