diff --git a/core/core.services.yml b/core/core.services.yml
index c3bb8d2..9afdce3 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -19,7 +19,7 @@ services:
       - { name: cache.context}
   cache_context.theme:
     class: Drupal\Core\Cache\ThemeCacheContext
-    arguments: ['@request', '@theme.negotiator']
+    arguments: ['@theme.negotiator', '@route_match_stack', '@request_stack']
     tags:
       - { name: cache.context}
   cache.backend.database:
@@ -175,7 +175,7 @@ services:
       - { name: http_client_subscriber }
   theme.negotiator:
     class: Drupal\Core\Theme\ThemeNegotiator
-    arguments: ['@access_check.theme', '@request_stack']
+    arguments: ['@access_check.theme']
   theme.negotiator.default:
     class: Drupal\Core\Theme\DefaultNegotiator
     arguments: ['@config.factory']
@@ -247,6 +247,10 @@ services:
     class: Symfony\Component\HttpFoundation\RequestStack
     tags:
       - { name: persist }
+  route_match_stack:
+     class: Drupal\Core\Routing\RouteMatchStack
+     tags:
+       - { name: persist }
   event_dispatcher:
     class: Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher
     arguments: ['@service_container']
@@ -472,10 +476,10 @@ services:
   ajax_response_renderer:
     class: Drupal\Core\Ajax\AjaxResponseRenderer
   router_listener:
-    class: Symfony\Component\HttpKernel\EventListener\RouterListener
+    class: Drupal\Core\EventSubscriber\RouterListener
     tags:
       - { name: event_subscriber }
-    arguments: ['@router', '@router.request_context', NULL, '@request_stack']
+    arguments: ['@router', '@route_match_stack']
   content_negotiation:
     class: Drupal\Core\ContentNegotiation
   view_subscriber:
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 9f8088a..895e89c 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -19,9 +19,11 @@
 use Drupal\Core\StringTranslation\Translator\FileTranslation;
 use Drupal\Core\Extension\ExtensionDiscovery;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Routing\RouteMatch;
 use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Route;
 
 use GuzzleHttp\Exception\RequestException;
 
@@ -368,6 +370,7 @@ function install_begin_request(&$install_state) {
   $container->enterScope('request');
   $container->set('request', $request, 'request');
   $container->get('request_stack')->push($request);
+  $container->get('route_match_stack')->push(new RouteMatch('', new Route('')));
 
   // Register the file translation service.
   if (isset($GLOBALS['config']['locale.settings']['translation.path'])) {
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index d45c2e5..458b826 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -102,8 +102,9 @@ function drupal_theme_initialize() {
   // @todo Let the theme.negotiator listen to the kernel request event.
   // Determine the active theme for the theme negotiator service. This includes
   // the default theme as well as really specific ones like the ajax base theme.
+  $route_match = \Drupal::routeMatch();
   $request = \Drupal::request();
-  $theme = \Drupal::service('theme.negotiator')->determineActiveTheme($request) ?: 'stark';
+  $theme = \Drupal::service('theme.negotiator')->determineActiveTheme($route_match, $request) ?: 'stark';
 
   // Store the identifier for retrieving theme settings with.
   $theme_key = $theme;
diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index 9ae8cbb..4eb7821 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -188,6 +188,16 @@ public static function request() {
   }
 
   /**
+   * Retrieves the currently active route match object.
+   *
+   * @return \Drupal\Core\Routing\RouteMatch
+   *   The currently active route match object.
+   */
+  public static function routeMatch() {
+    return static::$container->get('route_match_stack')->getCurrentRouteMatch();
+  }
+
+  /**
    * Gets the current active user.
    *
    * @return \Drupal\Core\Session\AccountProxyInterface
diff --git a/core/lib/Drupal/Core/Cache/ThemeCacheContext.php b/core/lib/Drupal/Core/Cache/ThemeCacheContext.php
index d777471..97b61b9 100644
--- a/core/lib/Drupal/Core/Cache/ThemeCacheContext.php
+++ b/core/lib/Drupal/Core/Cache/ThemeCacheContext.php
@@ -7,7 +7,8 @@
 
 namespace Drupal\Core\Cache;
 
-use Symfony\Component\HttpFoundation\Request;
+use Drupal\Core\Routing\RouteMatchStack;
+use Symfony\Component\HttpFoundation\RequestStack;
 use Drupal\Core\Theme\ThemeNegotiatorInterface;
 
 /**
@@ -16,30 +17,40 @@
 class ThemeCacheContext implements CacheContextInterface {
 
   /**
-   * The current request.
+   * The theme negotiator.
    *
-   * @var \Symfony\Component\HttpFoundation\Request
+   * @var \Drupal\Core\Theme\ThemeNegotiator
    */
-  protected $request;
+  protected $themeNegotiator;
 
   /**
-   * The theme negotiator.
+   * The route match stack.
    *
-   * @var \Drupal\Core\Theme\ThemeNegotiator
+   * @var \Drupal\Core\Routing\RouteMatchStack
    */
-  protected $themeNegotiator;
+  protected $routeMatchStack;
+
+  /**
+   * The request stack.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
 
   /**
    * Constructs a new ThemeCacheContext service.
    *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The HTTP request object.
    * @param \Drupal\Core\Theme\ThemeNegotiatorInterface $theme_negotiator
    *   The theme negotiator.
+   * @param \Drupal\Core\Routing\RouteMatchStack $route_match_stack
+   *   The route match stack.
+   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
+   *   The request stack.
    */
-  public function __construct(Request $request, ThemeNegotiatorInterface $theme_negotiator) {
-    $this->request = $request;
+  public function __construct(ThemeNegotiatorInterface $theme_negotiator, RouteMatchStack $route_match_stack, RequestStack $request_stack) {
     $this->themeNegotiator = $theme_negotiator;
+    $this->routeMatchStack = $route_match_stack;
+    $this->requestStack = $request_stack;
   }
 
   /**
@@ -53,7 +64,7 @@ public static function getLabel() {
    * {@inheritdoc}
    */
   public function getContext() {
-    return $this->themeNegotiator->determineActiveTheme($this->request) ?: 'stark';
+    return $this->themeNegotiator->determineActiveTheme($this->routeMatchStack->getCurrentRouteMatch(), $this->requestStack->getCurrentRequest()) ?: 'stark';
   }
 
 }
diff --git a/core/lib/Drupal/Core/EventSubscriber/RouterListener.php b/core/lib/Drupal/Core/EventSubscriber/RouterListener.php
new file mode 100644
index 0000000..d4b866a
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/RouterListener.php
@@ -0,0 +1,143 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\EventSubscriber\RouterListener.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Drupal\Core\Routing\RouteMatch;
+use Drupal\Core\Routing\RouteMatchStack;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Performs routing.
+ */
+class RouterListener implements EventSubscriberInterface {
+
+  protected $matcher;
+  protected $routeMatchStack;
+
+  /**
+   * Constructor.
+   *
+   * @param \Symfony\Component\Routing\Matcher\RequestMatcherInterface $matcher
+   *   The Request matcher.
+   * @param \Drupal\Core\Routing\RouteMatchStack $route_match_stack
+   *   The RouteMatch stack.
+   */
+  public function __construct(RequestMatcherInterface $matcher, RouteMatchStack $route_match_stack) {
+    $this->matcher = $matcher;
+    $this->routeMatchStack = $route_match_stack;
+  }
+
+  /**
+   * Matches the request to a route.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The Event to process.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
+   * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
+   */
+  public function onKernelRequest(GetResponseEvent $event) {
+    $request = $event->getRequest();
+
+    // The controller might already be set, for example, as part of handling a
+    // subrequest from an exception handler (@see
+    // \Symfony\Component\HttpKernel\EventListener\ExceptionListener).
+    if ($request->attributes->has('_controller')) {
+      // If we're skipping routing, then we still need to push something onto
+      // the route match stack.
+      // @todo Would pushing NULL be better than a fake RouteMatch?
+      $this->routeMatchStack->push(new RouteMatch('', new Route('')));
+      return;
+    }
+
+    try {
+      $route_arguments = $this->matcher->matchRequest($request);
+    }
+    catch (ResourceNotFoundException $e) {
+      $message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getPathInfo());
+      if ($referer = $request->headers->get('referer')) {
+        $message .= sprintf(' (from "%s")', $referer);
+      }
+      throw new NotFoundHttpException($message, $e);
+    }
+    catch (MethodNotAllowedException $e) {
+      $message = sprintf('No route found for "%s %s": Method Not Allowed (Allow: %s)', $request->getMethod(), $request->getPathInfo(), implode(', ', $e->getAllowedMethods()));
+      throw new MethodNotAllowedHttpException($e->getAllowedMethods(), $message, $e);
+    }
+
+    $this->processRouteArguments($route_arguments, $request);
+  }
+
+  /**
+   * Creates a RouteMatch object and pushes it onto the stack.
+   *
+   * @param array $route_arguments
+   *   The arguments array returned by RequestMatcherInterface::matchRequest().
+   *   This includes special keys, such as _route and _raw_variables, as well
+   *   as the actual route arguments, so must be unpacked into a RouteMatch
+   *   object.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   */
+  protected function processRouteArguments(array $route_arguments, Request $request) {
+    $route_name = $route_arguments[RouteObjectInterface::ROUTE_NAME];
+    $route = $route_arguments[RouteObjectInterface::ROUTE_OBJECT];
+    $arguments = array();
+    foreach ($route_arguments as $key => $value) {
+      if (substr($key, 0, 1) !== '_') {
+        $arguments[$key] = $value;
+      }
+    }
+    $raw_arguments = array();
+    if (isset($route_arguments['_raw_variables'])) {
+      foreach ($route_arguments['_raw_variables'] as $key => $value) {
+        if (substr($key, 0, 1) !== '_') {
+          $raw_arguments[$key] = $value;
+        }
+      }
+    }
+    $route_match = new RouteMatch($route_name, $route, $arguments, $raw_arguments);
+    $this->routeMatchStack->push($route_match);
+
+    // @todo Remove. See https://drupal.org/node/2124749.
+    $request->attributes->add($route_arguments);
+  }
+
+  /**
+   * Pops the route match stack when the request is finished.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\FinishRequestEvent $event
+   *   The Event to process.
+   */
+  public function onKernelFinishRequest(FinishRequestEvent $event) {
+    $this->routeMatchStack->pop();
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    return array(
+      KernelEvents::REQUEST => array(array('onKernelRequest', 32)),
+      KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)),
+    );
+  }
+}
diff --git a/core/lib/Drupal/Core/Routing/RouteMatch.php b/core/lib/Drupal/Core/Routing/RouteMatch.php
new file mode 100644
index 0000000..ecc671e
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RouteMatch.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Routing\RouteMatchStack.
+ */
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\HttpFoundation\ParameterBag;
+use Symfony\Component\Routing\Route;
+
+/**
+ * RouteMatches are representations arguments INDIRECTLY related to the
+ * request such as special keys, such as _route and _raw_variables.
+ *
+ * NB That arguments directly related to the request are stored in the
+ * request object.
+ *
+ * @see RouteListener::processRouteArguments()
+ */
+class RouteMatch {
+
+  protected $routeName;
+  protected $route;
+  protected $arguments;
+  protected $rawArguments;
+
+  public function __construct($route_name, Route $route, $arguments = array(), $raw_arguments = array()) {
+    $this->routeName = $route_name;
+    $this->route = $route;
+    $this->arguments = new ParameterBag($arguments);
+    $this->rawArguments = new ParameterBag($raw_arguments);
+  }
+
+  public function getRouteName() {
+    return $this->routeName;
+  }
+
+  public function getRouteObject() {
+    return $this->route;
+  }
+
+  public function getArgument($parameter_name) {
+    return $this->arguments->get($parameter_name);
+  }
+
+  public function getArguments() {
+    // A RouteMatch is constructed during routing, and should be immutable
+    // after that, so return a clone to prevent callers from modifying.
+    return clone($this->arguments);
+  }
+
+  public function getRawArgument($parameter_name) {
+    return $this->rawArguments->get($parameter_name);
+  }
+
+  public function getRawArguments() {
+    // A RouteMatch is constructed during routing, and should be immutable
+    // after that, so return a clone to prevent callers from modifying.
+    return clone($this->rawArguments);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/RouteMatchStack.php b/core/lib/Drupal/Core/Routing/RouteMatchStack.php
new file mode 100644
index 0000000..094c332
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RouteMatchStack.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Routing\RouteMatchStack.
+ */
+
+namespace Drupal\Core\Routing;
+
+/* The route match stack controls the lifecycle of "route matches"
+ *
+ * RouteMatches are representations arguments indirectly related to the
+ * request such as special keys, such as _route and _raw_variables.
+ *
+ * Route matches are poped onto this stack as parting of routing process.
+ * @see RouteListener::processRouteArguments()
+ *
+ * You can have more than one request in a single PHP process, that is
+ * many sub requests. So during the life cycle of a container as the request
+ * instance changes so must the arguments stored in the "route matches".
+ *
+ * @see Symfony\Component\HttpFoundation\RequestStack
+ *
+ */
+class RouteMatchStack {
+
+  protected $routeMatches = array();
+
+  public function push(RouteMatch $route_match) {
+    $this->routeMatches[] = $route_match;
+  }
+
+  public function pop() {
+    return array_pop($this->routeMatches);
+  }
+
+  public function getCurrentRouteMatch() {
+    return end($this->routeMatches) ?: NULL;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php
index d3a4bac..7a5b960 100644
--- a/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php
+++ b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php
@@ -9,7 +9,7 @@
 
 use Drupal\Core\Access\CsrfTokenGenerator;
 use Drupal\Core\Config\ConfigFactoryInterface;
-use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Drupal\Core\Routing\RouteMatch;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -61,9 +61,9 @@ public function __construct(CsrfTokenGenerator $token_generator, ConfigFactoryIn
   /**
    * {@inheritdoc}
    */
-  public function applies(Request $request) {
+  public function applies(RouteMatch $route_match, Request $request) {
     // Check whether the route was configured to use the base page theme.
-    return ($route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT))
+    return ($route = $route_match->getRouteObject())
       && $route->hasOption('_theme')
       && $route->getOption('_theme') == 'ajax_base_page';
   }
@@ -71,7 +71,7 @@ public function applies(Request $request) {
   /**
    * {@inheritdoc}
    */
-  public function determineActiveTheme(Request $request) {
+  public function determineActiveTheme(RouteMatch $route_match, Request $request) {
     if (($ajax_page_state = $request->request->get('ajax_page_state'))  && !empty($ajax_page_state['theme']) && !empty($ajax_page_state['theme_token'])) {
       $theme = $ajax_page_state['theme'];
       $token = $ajax_page_state['theme_token'];
diff --git a/core/lib/Drupal/Core/Theme/DefaultNegotiator.php b/core/lib/Drupal/Core/Theme/DefaultNegotiator.php
index 22b5863..b46408c 100644
--- a/core/lib/Drupal/Core/Theme/DefaultNegotiator.php
+++ b/core/lib/Drupal/Core/Theme/DefaultNegotiator.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Theme;
 
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Routing\RouteMatch;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -35,14 +36,14 @@ public function __construct(ConfigFactoryInterface $config_factory) {
   /**
    * {@inheritdoc}
    */
-  public function applies(Request $request) {
+  public function applies(RouteMatch $route_match, Request $request) {
     return TRUE;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function determineActiveTheme(Request $request) {
+  public function determineActiveTheme(RouteMatch $route_match, Request $request) {
     return $this->config->get('default');
   }
 
diff --git a/core/lib/Drupal/Core/Theme/ThemeNegotiator.php b/core/lib/Drupal/Core/Theme/ThemeNegotiator.php
index 054d6be..0f985b3 100644
--- a/core/lib/Drupal/Core/Theme/ThemeNegotiator.php
+++ b/core/lib/Drupal/Core/Theme/ThemeNegotiator.php
@@ -7,8 +7,8 @@
 
 namespace Drupal\Core\Theme;
 
+use Drupal\Core\Routing\RouteMatch;
 use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
  * Provides a class which determines the active theme of the page.
@@ -38,13 +38,6 @@ class ThemeNegotiator implements ThemeNegotiatorInterface {
   protected $sortedNegotiators;
 
   /**
-   * The request stack.
-   *
-   * @var \Symfony\Component\HttpFoundation\RequestStack
-   */
-  protected $requestStack;
-
-  /**
    * The access checker for themes.
    *
    * @var \Drupal\Core\Theme\ThemeAccessCheck
@@ -57,9 +50,8 @@ class ThemeNegotiator implements ThemeNegotiatorInterface {
    * @param \Drupal\Core\Theme\ThemeAccessCheck $theme_access
    *   The access checker for themes.
    */
-  public function __construct(ThemeAccessCheck $theme_access, RequestStack $request_stack) {
+  public function __construct(ThemeAccessCheck $theme_access) {
     $this->themeAccess = $theme_access;
-    $this->requestStack = $request_stack;
   }
 
   /**
@@ -97,36 +89,21 @@ protected function getSortedNegotiators() {
   }
 
   /**
-   * Get the current active theme.
-   *
-   * @return string
-   *   The current active string.
-   */
-  public function getActiveTheme() {
-    $request = $this->requestStack->getCurrentRequest();
-    if (!$request->attributes->has('_theme_active')) {
-      $this->determineActiveTheme($request);
-    }
-    return $request->attributes->get('_theme_active');
-  }
-
-  /**
    * {@inheritdoc}
    */
-  public function applies(Request $request) {
+  public function applies(RouteMatch $route_match, Request $request) {
     return TRUE;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function determineActiveTheme(Request $request) {
+  public function determineActiveTheme(RouteMatch $route_match, Request $request) {
     foreach ($this->getSortedNegotiators() as $negotiator) {
-      if ($negotiator->applies($request)) {
-        $theme = $negotiator->determineActiveTheme($request);
+      if ($negotiator->applies($route_match, $request)) {
+        $theme = $negotiator->determineActiveTheme($route_match, $request);
         if ($theme !== NULL && $this->themeAccess->checkAccess($theme)) {
-          $request->attributes->set('_theme_active', $theme);
-          return $request->attributes->get('_theme_active');
+          return $theme;
         }
       }
     }
diff --git a/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php b/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php
index e0631c2..2d845e7 100644
--- a/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php
+++ b/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Theme;
 
+use Drupal\Core\Routing\RouteMatch;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -30,6 +31,8 @@
   /**
    * Whether this theme negotiator should be used to set the theme.
    *
+   * @param \Drupal\Core\Routing\RouteMatch $route_match
+   *   The current route match object.
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   The current request object.
    *
@@ -37,17 +40,19 @@
    *   TRUE if this negotiator should be used or FALSE to let other negotiators
    *   decide.
    */
-  public function applies(Request $request);
+  public function applies(RouteMatch $route_match, Request $request);
 
   /**
    * Determine the active theme for the request.
    *
+   * @param \Drupal\Core\Routing\RouteMatch $route_match
+   *   The active route match of the site.
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   The active request of the site.
    *
    * @return string|null
    *   Returns the active theme name, else return NULL.
    */
-  public function determineActiveTheme(Request $request);
+  public function determineActiveTheme(RouteMatch $route_match, Request $request);
 
 }
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 5190436..8e00f81 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -107,7 +107,7 @@ function block_page_build(&$page) {
 
   // Fetch a list of regions for the current theme.
   $all_regions = system_region_list($theme);
-  if (\Drupal::request()->attributes->get(RouteObjectInterface::ROUTE_NAME) != 'block.admin_demo') {
+  if (\Drupal::routeMatch()->getRouteName() != 'block.admin_demo') {
     // Load all region content assigned via blocks.
     foreach (array_keys($all_regions) as $region) {
       // Assign blocks to region.
diff --git a/core/modules/block/lib/Drupal/block/Theme/AdminDemoNegotiator.php b/core/modules/block/lib/Drupal/block/Theme/AdminDemoNegotiator.php
index d175b91..13963ee 100644
--- a/core/modules/block/lib/Drupal/block/Theme/AdminDemoNegotiator.php
+++ b/core/modules/block/lib/Drupal/block/Theme/AdminDemoNegotiator.php
@@ -7,8 +7,8 @@
 
 namespace Drupal\block\Theme;
 
+use Drupal\Core\Routing\RouteMatch;
 use Drupal\Core\Theme\ThemeNegotiatorInterface;
-use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -19,17 +19,17 @@ class AdminDemoNegotiator implements ThemeNegotiatorInterface {
   /**
    * {@inheritdoc}
    */
-  public function applies(Request $request) {
-    return $request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'block.admin_demo';
+  public function applies(RouteMatch $route_match, Request $request) {
+    return $route_match->getRouteName() == 'block.admin_demo';
   }
 
   /**
    * {@inheritdoc}
    */
-  public function determineActiveTheme(Request $request) {
+  public function determineActiveTheme(RouteMatch $route_match, Request $request) {
     // We return exactly what was passed in, to guarantee that the page will
     // always be displayed using the theme whose blocks are being configured.
-    return $request->attributes->get('theme');
+    return $route_match->getArgument('theme');
   }
 
 }
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 5facc3c..2513b5f 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -104,8 +104,7 @@ function forum_menu_local_tasks(&$data, $route_name) {
 
   // Add action link to 'node/add/forum' on 'forum' sub-pages.
   if (in_array($route_name, array('forum.index', 'forum.page'))) {
-    $request = \Drupal::request();
-    $forum_term = $request->attributes->get('taxonomy_term');
+    $forum_term = \Drupal::routeMatch()->getArgument('taxonomy_term');
     $vid = \Drupal::config('forum.settings')->get('vocabulary');
     $links = array();
     // Loop through all bundles for forum taxonomy vocabulary field.
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 6b77d21..20cf9e3 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -572,9 +572,9 @@ function node_revision_delete($revision_id) {
  *   The ID of the node if this is a full page view, otherwise FALSE.
  */
 function node_is_page(NodeInterface $node) {
-  $request = \Drupal::request();
-  if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'node.view') {
-    $page_node = $request->attributes->get('node');
+  $route_match = \Drupal::routeMatch();
+  if ($route_match->getRouteName() == 'node.view') {
+    $page_node = $route_match->getArgument('node');
   }
   return (!empty($page_node) ? $page_node->id() == $node->id() : FALSE);
 }
@@ -584,7 +584,7 @@ function node_is_page(NodeInterface $node) {
  */
 function node_preprocess_html(&$variables) {
   // If on an individual node page, add the node type to body classes.
-  if (($node = \Drupal::request()->attributes->get('node')) && $node instanceof NodeInterface) {
+  if (($node = \Drupal::routeMatch()->getArgument('node')) && $node instanceof NodeInterface) {
     $variables['attributes']['class'][] = drupal_html_class('node-type-' . $node->getType());
   }
 }
@@ -1044,16 +1044,14 @@ function node_block_access(Block $block, $operation, AccountInterface $account,
 
     // For blocks with node types associated, if the node type does not match
     // the settings from this block, deny access to it.
-    $request = \Drupal::request();
-    if ($node = $request->attributes->get('node')) {
+    $route_match = \Drupal::routeMatch();
+    if ($node = $route_match->getArgument('node')) {
       // Page has node.
       return in_array($node->bundle(), $allowed_types);
     }
     // This is a node creation page.
-    // $request->attributes->has('node_type') would also match administration
-    // configuration pages, which the previous URI path options did not.
-    if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'node.add') {
-      $node_type = $request->attributes->get('node_type');
+    if ($route_match->getRouteName() == 'node.add') {
+      $node_type = $route_match->getArgument('node_type');
       return in_array($node_type->id(), $allowed_types);
     }
 
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index bf8c60e..2952dc1 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -351,7 +351,7 @@ function rdf_preprocess_user(&$variables) {
   }
   // If we are on the user account page, add the relationship between the
   // sioc:UserAccount and the foaf:Person who holds the account.
-  if (\Drupal::request()->attributes->get(RouteObjectInterface::ROUTE_NAME) == $uri->getRouteName()) {
+  if (\Drupal::routeMatch()->getRouteName() == $uri->getRouteName()) {
     // Adds the markup for username as language neutral literal, see
     // rdf_preprocess_username().
     $name_mapping = $mapping->getPreparedFieldMapping('name');
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 870bd8a..6f2a484 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -338,10 +338,9 @@ function shortcut_preprocess_page(&$variables) {
   // we do not want to display it on "access denied" or "page not found"
   // pages).
   // Load the router item corresponding to the current page.
-  $request = \Drupal::request();
   $item = array();
-  if ($route = $request->attributes->get(RouteObjectInterface::ROUTE_NAME)) {
-    $item['href'] = $request->attributes->get('_system_path');
+  if (\Drupal::routeMatch()->getRouteName()) {
+    $item['href'] = \Drupal::request()->attributes->get('_system_path');
     // @todo What should be done on a 404/403 page?
     $item['access'] = TRUE;
   }
diff --git a/core/modules/system/lib/Drupal/system/Theme/BatchNegotiator.php b/core/modules/system/lib/Drupal/system/Theme/BatchNegotiator.php
index 7002504..b71bbe3 100644
--- a/core/modules/system/lib/Drupal/system/Theme/BatchNegotiator.php
+++ b/core/modules/system/lib/Drupal/system/Theme/BatchNegotiator.php
@@ -8,8 +8,8 @@
 namespace Drupal\system\Theme;
 
 use Drupal\Core\Batch\BatchStorageInterface;
+use Drupal\Core\Routing\RouteMatch;
 use Drupal\Core\Theme\ThemeNegotiatorInterface;
-use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -37,14 +37,14 @@ public function __construct(BatchStorageInterface $batch_storage) {
   /**
    * {@inheritdoc}
    */
-  public function applies(Request $request) {
-    return $request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'system.batch_page';
+  public function applies(RouteMatch $route_match, Request $request) {
+    return $route_match->getRouteName() == 'system.batch_page';
   }
 
   /**
    * {@inheritdoc}
    */
-  public function determineActiveTheme(Request $request) {
+  public function determineActiveTheme(RouteMatch $route_match, Request $request) {
     // Retrieve the current state of the batch.
     $batch = &batch_get();
     if (!$batch && $request->request->has('id')) {
diff --git a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Theme/TestThemeNegotiator.php b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Theme/TestThemeNegotiator.php
index 77954a3..4f0cc03 100644
--- a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Theme/TestThemeNegotiator.php
+++ b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Theme/TestThemeNegotiator.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\menu_test\Theme;
 
+use Drupal\Core\Routing\RouteMatch;
 use Drupal\Core\Theme\ThemeNegotiatorInterface;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -21,15 +22,15 @@ class TestThemeNegotiator implements ThemeNegotiatorInterface {
   /**
    * {@inheritdoc}
    */
-  public function applies(Request $request) {
-    return $request->attributes->has('inherited');
+  public function applies(RouteMatch $route_match, Request $request) {
+    return (bool) $route_match->getArgument('inherited');
   }
 
   /**
    * {@inheritdoc}
    */
-  public function determineActiveTheme(Request $request) {
-    $argument = $request->attributes->get('inherited');
+  public function determineActiveTheme(RouteMatch $route_match, Request $request) {
+    $argument = $route_match->getArgument('inherited');
     // Test using the variable administrative theme.
     if ($argument == 'use-admin-theme') {
       return \Drupal::config('system.theme')->get('admin');
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.module b/core/modules/system/tests/modules/menu_test/menu_test.module
index a4212bc..924c099 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.module
+++ b/core/modules/system/tests/modules/menu_test/menu_test.module
@@ -132,7 +132,7 @@ function menu_test_theme_page_callback($inherited = FALSE) {
   // Initialize the theme system so that $theme_key will be populated.
   drupal_theme_initialize();
   // Now we check what the theme negotiator service returns.
-  $active_theme = \Drupal::service('theme.negotiator')->getActiveTheme('getActiveTheme');
+  $active_theme = \Drupal::service('theme.negotiator')->determineActiveTheme(\Drupal::routeMatch());
   $output = "Active theme: $active_theme. Actual theme: $theme_key.";
   if ($inherited) {
     $output .= ' Theme negotiation inheritance is being tested.';
diff --git a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/CustomThemeNegotiator.php b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/CustomThemeNegotiator.php
index 33b9bec..7d07460 100644
--- a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/CustomThemeNegotiator.php
+++ b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/CustomThemeNegotiator.php
@@ -7,10 +7,9 @@
 
 namespace Drupal\theme_test\Theme;
 
+use Drupal\Core\Routing\RouteMatch;
 use Drupal\Core\Theme\ThemeNegotiatorInterface;
-use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\Route;
 
 /**
  * Just forces the 'test_theme' theme.
@@ -20,16 +19,16 @@ class CustomThemeNegotiator implements ThemeNegotiatorInterface {
   /**
    * {@inheritdoc}
    */
-  public function applies(Request $request) {
-    return (($route_object = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)) && $route_object instanceof Route && $route_object->hasOption('_custom_theme'));
+  public function applies(RouteMatch $route_match, Request $request) {
+    $route = $route_match->getRouteObject();
+    return ($route && $route->hasOption('_custom_theme'));
   }
 
   /**
    * {@inheritdoc}
    */
-  public function determineActiveTheme(Request $request) {
-    $route_object = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT);
-    return $route_object->getOption('_custom_theme');
+  public function determineActiveTheme(RouteMatch $route_match, Request $request) {
+    return $route_match->getRouteObject()->getOption('_custom_theme');
   }
 
 }
diff --git a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/HighPriorityThemeNegotiator.php b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/HighPriorityThemeNegotiator.php
index 91fc936..5ae18fa 100644
--- a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/HighPriorityThemeNegotiator.php
+++ b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/HighPriorityThemeNegotiator.php
@@ -7,8 +7,8 @@
 
 namespace Drupal\theme_test\Theme;
 
+use Drupal\Core\Routing\RouteMatch;
 use Drupal\Core\Theme\ThemeNegotiatorInterface;
-use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -16,19 +16,17 @@
  */
 class HighPriorityThemeNegotiator implements ThemeNegotiatorInterface {
 
-
-
   /**
    * {@inheritdoc}
    */
-  public function applies(Request $request) {
-    return (($route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME)) && $route_name == 'theme_test.priority');
+  public function applies(RouteMatch $route_match, Request $request) {
+    return ($route_match->getRouteName() == 'theme_test.priority');
   }
 
   /**
    * {@inheritdoc}
    */
-  public function determineActiveTheme(Request $request) {
+  public function determineActiveTheme(RouteMatch $route_match, Request $request) {
     return 'stark';
   }
 
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index f1db93f..d840ffb 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -341,9 +341,7 @@ function template_preprocess_taxonomy_term(&$variables) {
  *   A taxonomy term entity.
  */
 function taxonomy_term_is_page(Term $term) {
-  $request = \Drupal::request();
-  if ($request->attributes->has('taxonomy_term')) {
-    $page_term = $request->attributes->get('taxonomy_term');
+  if ($page_term = \Drupal::routeMatch()->getArgument('taxonomy_term')) {
     return $page_term->id() == $term->id();
   }
   return FALSE;
diff --git a/core/modules/tour/tour.module b/core/modules/tour/tour.module
index 610c025..3f6bceb 100644
--- a/core/modules/tour/tour.module
+++ b/core/modules/tour/tour.module
@@ -82,8 +82,8 @@ function tour_preprocess_page(&$variables) {
   }
 
   // Load all of the items and match on route name.
-  $request = \Drupal::request();
-  $route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME);
+  $route_match = \Drupal::routeMatch();
+  $route_name = $route_match->getRouteName();
 
   $results = \Drupal::entityQuery('tour')
     ->condition('routes.*.route_name', $route_name)
@@ -91,7 +91,7 @@ function tour_preprocess_page(&$variables) {
   if (!empty($results) && $tours = entity_load_multiple('tour', array_keys($results))) {
     foreach ($tours as $id => $tour) {
       // Match on params.
-      if (!$tour->hasMatchingRoute($route_name, $request->attributes->get('_raw_variables')->all())) {
+      if (!$tour->hasMatchingRoute($route_name, $route_match->getRawArguments())) {
         unset($tours[$id]);
       }
     }
diff --git a/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php b/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php
index ce6f04b..e637553 100644
--- a/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php
+++ b/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php
@@ -10,9 +10,9 @@
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Routing\AdminContext;
+use Drupal\Core\Routing\RouteMatch;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Theme\ThemeNegotiatorInterface;
-use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -68,14 +68,14 @@ public function __construct(AccountInterface $user, ConfigFactoryInterface $conf
   /**
    * {@inheritdoc}
    */
-  public function applies(Request $request) {
-    return ($this->entityManager->hasController('user_role', 'storage') && $this->user->hasPermission('view the administration theme') && $this->adminContext->isAdminRoute($request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)));
+  public function applies(RouteMatch $route_match, Request $request) {
+    return ($this->entityManager->hasController('user_role', 'storage') && $this->user->hasPermission('view the administration theme') && $this->adminContext->isAdminRoute($route_match->getRouteObject()));
   }
 
   /**
    * {@inheritdoc}
    */
-  public function determineActiveTheme(Request $request) {
+  public function determineActiveTheme(RouteMatch $route_match, Request $request) {
     return $this->configFactory->get('system.theme')->get('admin');
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php b/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php
index 89a1e46..3959637 100644
--- a/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php
+++ b/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php
@@ -7,10 +7,12 @@
 
 namespace Drupal\Tests\Core\Theme;
 
+use Drupal\Core\Routing\RouteMatch;
 use Drupal\Core\Theme\ThemeNegotiator;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\Routing\Route;
 
 /**
  * Tests the theme negotiator.
@@ -78,11 +80,11 @@ public function testDetermineActiveTheme() {
       ->method('checkAccess')
       ->will($this->returnValue(TRUE));
 
+    $route_match = new RouteMatch('test_route', new Route('/test-route'), array(), array());
     $request = Request::create('/test-route');
-    $theme = $this->themeNegotiator->determineActiveTheme($request);
+    $theme = $this->themeNegotiator->determineActiveTheme($route_match, $request);
 
     $this->assertEquals('example_test', $theme);
-    $this->assertEquals('example_test', $request->attributes->get('_theme_active'));
   }
 
   /**
@@ -113,11 +115,11 @@ public function testDetermineActiveThemeWithPriority() {
       ->method('checkAccess')
       ->will($this->returnValue(TRUE));
 
+    $route_match = new RouteMatch('test_route', new Route('/test-route'), array(), array());
     $request = Request::create('/test-route');
-    $theme = $this->themeNegotiator->determineActiveTheme($request);
+    $theme = $this->themeNegotiator->determineActiveTheme($route_match, $request);
 
     $this->assertEquals('example_test', $theme);
-    $this->assertEquals('example_test', $request->attributes->get('_theme_active'));
   }
 
   /**
@@ -156,11 +158,11 @@ public function testDetermineActiveThemeWithAccessCheck() {
       ->with('example_test2')
       ->will($this->returnValue(TRUE));
 
+    $route_match = new RouteMatch('test_route', new Route('/test-route'), array(), array());
     $request = Request::create('/test-route');
-    $theme = $this->themeNegotiator->determineActiveTheme($request);
+    $theme = $this->themeNegotiator->determineActiveTheme($route_match, $request);
 
     $this->assertEquals('example_test2', $theme);
-    $this->assertEquals('example_test2', $request->attributes->get('_theme_active'));
   }
 
   /**
@@ -192,11 +194,11 @@ public function testDetermineActiveThemeWithNotApplyingNegotiator() {
       ->method('checkAccess')
       ->will($this->returnValue(TRUE));
 
+    $route_match = new RouteMatch('test_route', new Route('/test-route'), array(), array());
     $request = Request::create('/test-route');
-    $theme = $this->themeNegotiator->determineActiveTheme($request);
+    $theme = $this->themeNegotiator->determineActiveTheme($route_match, $request);
 
     $this->assertEquals('example_test2', $theme);
-    $this->assertEquals('example_test2', $request->attributes->get('_theme_active'));
   }
 
 }
