diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index fb11da253089cfe4e5412a3f905e656f115072f2..08181b19dc5d041e7db724bd8088907e7ce29851 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -774,7 +774,7 @@ function _menu_translate(&$router_item, $map, $to_arg = FALSE) {
     // Attempt to match this path to provide a fully built request to the
     // acccess checker.
     try {
-      $request->attributes->add(Drupal::service('router')->matchRequest($request));
+      $request->attributes->add(Drupal::service('router.dynamic')->matchRequest($request));
       $router_item['access'] = Drupal::service('access_manager')->check($route, $request);
     }
     catch (NotFoundHttpException $e) {
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
index dd4ffcc847dad7e5979e8ff3f563c2c4c180178f..769d2961c508743f43ba1d610fac5f6e8a8b492a 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageException;
+use Symfony\Component\HttpFoundation\Request;
 
 /**
  * Controller class for menu links.
@@ -216,6 +217,36 @@ protected function preSave(EntityInterface $entity) {
         $entity->router_path = _menu_find_router_path($entity->link_path);
       }
     }
+    // Find the route_name.
+    if (!isset($entity->route_name)) {
+      $entity->route_name = $this->findRouteName($entity->link_path);
+    }
+  }
+
+  /**
+   * Returns the route_name matching a URL.
+   *
+   * @param string $link_path
+   *   The link path to find a route name for.
+   *
+   * @return string
+   *   The route name.
+   */
+  protected function findRouteName($link_path) {
+    // Look up the route_name used for the given path.
+    $request = Request::create('/' . $link_path);
+    $request->attributes->set('system_path', $link_path);
+    try {
+      // Use router.dynamic instead of router, because router will call the
+      // legacy router which will call hook_menu() and you will get back to
+      // this method.
+      $result = \Drupal::service('router.dynamic')->matchRequest($request);
+      return isset($result['_route']) ? $result['_route'] : '';
+    }
+    catch (\Exception $e) {
+      return '';
+    }
+
   }
 
   /**
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeRSSContentTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeRSSContentTest.php
index ea7c5075d2305a2a76593584a972aaa10a8092eb..34b35ae30445d09c5be8ab6536fff81093d36638 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeRSSContentTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeRSSContentTest.php
@@ -72,10 +72,6 @@ function testNodeRSSContent() {
     // viewing node.
     $this->drupalGet("node/$node->nid");
     $this->assertNoText($rss_only_content, 'Node content designed for RSS does not appear when viewing node.');
-
-    // Check that the node feed page does not try to interpret additional path
-    // components as arguments for node_feed() and returns default content.
-    $this->drupalGet('rss.xml/' . $this->randomName() . '/' . $this->randomName());
-    $this->assertText($rss_only_content, 'Ignore page arguments when delivering rss.xml.');
   }
+
 }
diff --git a/core/modules/node/lib/Drupal/node/Tests/Views/NodeIntegrationTest.php b/core/modules/node/lib/Drupal/node/Tests/Views/NodeIntegrationTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1e57b5e094e28a6a3fadbe906a1839eb09ae6bf5
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Tests/Views/NodeIntegrationTest.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Tests\Views\NodeIntegrationTest.
+ */
+
+namespace Drupal\node\Tests\Views;
+
+class NodeIntegrationTest extends NodeTestBase {
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $testViews = array('test_node_view');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Node: Views data',
+      'description' => 'Tests the node integration into views.',
+      'group' => 'Views module integration',
+    );
+  }
+
+  /**
+   * Tests basic node view with a node type argument.
+   */
+  public function testNodeViewTypeArgument() {
+    // Create two content types with three nodes each.
+    $types = array();
+    $all_nids = array();
+    for ($i = 0; $i < 2; $i++) {
+      $type = $this->drupalCreateContentType();
+      $types[] = $type;
+
+      for ($j = 0; $j < 5; $j++) {
+        // Ensure the right order of the nodes.
+        $node = $this->drupalCreateNode(array('type' => $type->type, 'created' => REQUEST_TIME - ($i * 5 + $j)));
+        $nodes[$type->type][$node->id()] = $node;
+        $all_nids[] = $node->id();
+      }
+    }
+
+    $this->drupalGet('test-node-view');
+    $this->assertResponse(404);
+
+    $this->drupalGet('test-node-view/all');
+    $this->assertResponse(200);
+    $this->assertNids($all_nids);
+
+    foreach ($types as $type) {
+      $this->drupalGet("test-node-view/{$type->type}");
+      $this->assertNids(array_keys($nodes[$type->type]));
+    }
+  }
+
+  /**
+   * Ensures that a list of nodes appear on the page.
+   *
+   * @param array $expected_nids
+   *   An array of node IDs.
+   */
+  protected function assertNids(array $expected_nids = array()) {
+    $result = $this->xpath('//span[@class="field-content"]');
+    $nids = array();
+    foreach ($result as $element) {
+      $nids[] = (int) $element;
+    }
+    $this->assertEqual($nids, $expected_nids);
+  }
+
+}
diff --git a/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_view.yml b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_view.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4b82a313e4ba8e9ebc21a7e8d6db8af1f4784e70
--- /dev/null
+++ b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_view.yml
@@ -0,0 +1,195 @@
+base_field: nid
+base_table: node
+core: 8.x
+description: ''
+status: '1'
+display:
+  page_1:
+    display_plugin: page
+    id: page_1
+    display_title: Page
+    position: ''
+    display_options:
+      path: test-node-view
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: ''
+    display_options:
+      access:
+        type: perm
+        options:
+          perm: 'access content'
+      cache:
+        type: none
+        options: {  }
+      query:
+        type: views_query
+        options:
+          disable_sql_rewrite: '0'
+          distinct: '0'
+          slave: '0'
+          query_comment: ''
+          query_tags: {  }
+      exposed_form:
+        type: basic
+        options:
+          submit_button: Apply
+          reset_button: '0'
+          reset_button_label: Reset
+          exposed_sorts_label: 'Sort by'
+          expose_sort_order: '1'
+          sort_asc_label: Asc
+          sort_desc_label: Desc
+      pager:
+        type: full
+        options:
+          items_per_page: '10'
+          offset: '0'
+          id: '0'
+          total_pages: ''
+          expose:
+            items_per_page: '0'
+            items_per_page_label: 'Items per page'
+            items_per_page_options: '5, 10, 20, 40, 60'
+            items_per_page_options_all: '0'
+            items_per_page_options_all_label: '- All -'
+            offset: '0'
+            offset_label: Offset
+          tags:
+            previous: '‹ previous'
+            next: 'next ›'
+            first: '« first'
+            last: 'last »'
+          quantity: '9'
+      style:
+        type: default
+      row:
+        type: fields
+      fields:
+        nid:
+          id: nid
+          table: node
+          field: nid
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Nid
+          exclude: '0'
+          alter:
+            alter_text: '0'
+            text: ''
+            make_link: '0'
+            path: ''
+            absolute: '0'
+            external: '0'
+            replace_spaces: '0'
+            path_case: none
+            trim_whitespace: '0'
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: '0'
+            max_length: ''
+            word_boundary: '1'
+            ellipsis: '1'
+            more_link: '0'
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: '0'
+            trim: '0'
+            preserve_tags: ''
+            html: '0'
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: '1'
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: '1'
+          empty: ''
+          hide_empty: '0'
+          empty_zero: '0'
+          hide_alter_empty: '1'
+          link_to_node: '0'
+          plugin_id: node
+      filters:
+        status:
+          value: '1'
+          table: node
+          field: status
+          id: status
+          expose:
+            operator: '0'
+          group: '1'
+      sorts:
+        created:
+          id: created
+          table: node
+          field: created
+          order: DESC
+          relationship: none
+          group_type: group
+          admin_label: ''
+          exposed: '0'
+          expose:
+            label: ''
+          granularity: second
+      title: test_node_view
+      header: {  }
+      footer: {  }
+      empty: {  }
+      relationships: {  }
+      arguments:
+        type:
+          id: type
+          table: node
+          field: type
+          relationship: none
+          group_type: group
+          admin_label: ''
+          default_action: 'not found'
+          exception:
+            value: all
+            title_enable: '0'
+            title: All
+          title_enable: '0'
+          title: ''
+          breadcrumb_enable: '0'
+          breadcrumb: ''
+          default_argument_type: fixed
+          default_argument_options:
+            argument: ''
+          default_argument_skip_url: '0'
+          summary_options:
+            base_path: ''
+            count: '1'
+            items_per_page: '25'
+            override: '0'
+          summary:
+            sort_order: asc
+            number_of_records: '0'
+            format: default_summary
+          specify_validation: '0'
+          validate:
+            type: none
+            fail: 'not found'
+          validate_options: {  }
+          glossary: '0'
+          limit: '0'
+          case: none
+          path_case: none
+          transform_dash: '0'
+          break_phrase: '0'
+          plugin_id: node_type
+label: test_node_view
+module: views
+id: test_node_view
+tag: ''
+uuid: 377e9d79-57ea-4836-a100-a255c58e40ca
+langcode: en
diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php b/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php
index ea43d9d981b22975756d37bf2783dad838b8c236..b263f71d44c6af000a4b2c10725ac42ac51459e2 100644
--- a/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php
@@ -23,7 +23,7 @@
  *   module = "rest",
  *   title = @Translation("REST export"),
  *   help = @Translation("Create a REST export resource."),
- *   uses_hook_menu = TRUE,
+ *   uses_route = TRUE,
  *   admin = @Translation("REST export")
  * )
  */
diff --git a/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php
index adb5c345e0f184e715484e47d8156184a231c3c0..86342d501b81a43b3a850749a0be86b7c6e93fb8 100644
--- a/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php
+++ b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php
@@ -14,6 +14,10 @@
  */
 class TestControllers {
 
+  public function test() {
+    return new Response('test');
+  }
+
   public function test1() {
     return new Response('test1');
   }
diff --git a/core/modules/user/lib/Drupal/user/Plugin/views/access/Permission.php b/core/modules/user/lib/Drupal/user/Plugin/views/access/Permission.php
index 5b4004ddb0e3977bcb4c6ee9946faef9c1e96c7e..3ce45921d9e05ee49aff6410c9e8475f4cd6639e 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/views/access/Permission.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/views/access/Permission.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Annotation\Plugin;
 use Drupal\views\Plugin\views\access\AccessPluginBase;
 use Drupal\Core\Annotation\Translation;
+use Symfony\Component\Routing\Route;
 
 /**
  * Access plugin that provides permission-based access control.
@@ -30,11 +31,14 @@ class Permission extends AccessPluginBase {
   protected $usesOptions = TRUE;
 
   public function access($account) {
-    return views_check_perm($this->options['perm'], $account);
+    return user_access($this->options['perm'], $account) || user_access('access all views', $account);
   }
 
-  function get_access_callback() {
-    return array('views_check_perm', array($this->options['perm']));
+  /**
+   * {@inheritdoc}
+   */
+  public function alterRouteDefinition(Route $route) {
+    $route->setRequirement('_permission', $this->options['perm']);
   }
 
   public function summaryTitle() {
diff --git a/core/modules/user/lib/Drupal/user/Plugin/views/access/Role.php b/core/modules/user/lib/Drupal/user/Plugin/views/access/Role.php
index 6448df486165aab19f101558cfd979937a25642e..23ece46142355bead11be18410b83a003027db2c 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/views/access/Role.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/views/access/Role.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Annotation\Plugin;
 use Drupal\views\Plugin\views\access\AccessPluginBase;
 use Drupal\Core\Annotation\Translation;
+use Symfony\Component\Routing\Route;
 
 /**
  * Access plugin that provides role-based access control.
@@ -29,12 +30,18 @@ class Role extends AccessPluginBase {
    */
   protected $usesOptions = TRUE;
 
+  /**
+   * {@inheritdoc}
+   */
   public function access($account) {
-    return views_check_roles(array_filter($this->options['role']), $account);
+    return user_access('access all views', $account) || array_intersect(array_filter($this->options['role']), $account->roles);
   }
 
-  function get_access_callback() {
-    return array('views_check_roles', array(array_filter($this->options['role'])));
+  /**
+   * {@inheritdoc}
+   */
+  public function alterRouteDefinition(Route $route) {
+    $route->setRequirement('_role_id', $this->options['role']);
   }
 
   public function summaryTitle() {
diff --git a/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php b/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php
new file mode 100644
index 0000000000000000000000000000000000000000..e3dbde33487b3983b5c9bc80b8b8606b92d01b24
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\EventSubscriber\RouteSubscriber.
+ */
+
+namespace Drupal\views\EventSubscriber;
+
+use Drupal\Core\Routing\RouteBuildEvent;
+use Drupal\Core\Routing\RoutingEvents;
+use Drupal\views\Plugin\views\display\DisplayRouterInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Builds up the routes of all views.
+ */
+class RouteSubscriber implements EventSubscriberInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[RoutingEvents::DYNAMIC] = 'dynamicRoutes';
+    return $events;
+  }
+
+  /**
+   * Adds routes defined by all views.
+   *
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   *   The route building event.
+   */
+  public function dynamicRoutes(RouteBuildEvent $event) {
+    $collection = $event->getRouteCollection();
+
+    $views = views_get_applicable_views('uses_route');
+    foreach ($views as $data) {
+      list($view, $display_id) = $data;
+      if ($view->setDisplay($display_id) && $display = $view->displayHandlers->get($display_id)) {
+        if ($display instanceof DisplayRouterInterface) {
+          $display->collectRoutes($collection);
+        }
+      }
+      $view->destroy();
+    }
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/access/AccessPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/access/AccessPluginBase.php
index 0d70b4c10a155908e67ecd8bb52adb5d224d3364..18d3cfe7405baf6d7fa3e1046de82a35d7b3c72a 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/access/AccessPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/access/AccessPluginBase.php
@@ -9,13 +9,14 @@
 
 use Drupal\views\Plugin\views\PluginBase;
 use Drupal\views\ViewExecutable;
+use Symfony\Component\Routing\Route;
 
 /**
  * @defgroup views_access_plugins Views access plugins
  * @{
  * The base plugin to handle access to a view.
  *
- * Therefore it primarily has to implement the access and the get_access_callback
+ * Therefore it primarily has to implement the access and the alterRouteDefinition
  * method.
  */
 
@@ -65,18 +66,15 @@ public function summaryTitle() {
   abstract public function access($account);
 
   /**
-   * Determine the access callback and arguments.
+   * Allows access plugins to alter the route definition of a view.
    *
-   * This information will be embedded in the menu in order to reduce
-   * performance hits during menu item access testing, which happens
-   * a lot.
+   * Likely the access plugin will add new requirements, so its custom access
+   * checker can be applied.
    *
-   * @return array
-   *   The first item of the array should be the function to call,and the
-   *   second item should be an array of arguments. The first item may also be
-   *   TRUE (bool only) which will indicate no access control.
+   * @param \Symfony\Component\Routing\Route $route
+   *   The route to change.
    */
-  abstract function get_access_callback();
+  abstract public function alterRouteDefinition(Route $route);
 
 }
 
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/access/None.php b/core/modules/views/lib/Drupal/views/Plugin/views/access/None.php
index 55e1c808b0b9576ae88531b99343cba7f75c7cd5..522c472b15fb909fd814fb7d9e658cfd07559aa1 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/access/None.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/access/None.php
@@ -10,6 +10,8 @@
 use Drupal\Core\Annotation\Translation;
 use Drupal\Component\Annotation\Plugin;
 
+use Symfony\Component\Routing\Route;
+
 /**
  * Access plugin that provides no access control at all.
  *
@@ -36,11 +38,10 @@ public function access($account) {
   }
 
   /**
-   * Implements Drupal\views\Plugin\views\access\AccessPluginBase::get_access_callback().
+   * {@inheritdoc}
    */
-  public function get_access_callback() {
-    // No access control.
-    return TRUE;
+  public function alterRouteDefinition(Route $route) {
+    $route->setRequirement('_access', 'TRUE');
   }
 
 }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayRouterInterface.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayRouterInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..68cbe688ee021f64ac8625dabd4bcffb6eac98af
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayRouterInterface.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Plugin\views\display\DisplayRouterInterface.
+ */
+
+namespace Drupal\views\Plugin\views\display;
+
+/**
+ * Defines an interface for displays that can collect routes.
+ *
+ * In addition to implementing the interface, specify 'uses_routes' in the
+ * plugin definition.
+ */
+use Symfony\Component\Routing\RouteCollection;
+
+interface DisplayRouterInterface {
+
+  /**
+   * Adds the route entry of a view to the collection.
+   *
+   * @param \Symfony\Component\Routing\RouteCollection $collection
+   *   A collection of routes that should be registered for this resource.
+   */
+  public function collectRoutes(RouteCollection $collection);
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/Feed.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/Feed.php
index a058469dc6d8dcda528c6e2515034f648e9122e0..f40b872ee3ac8333661059ab0675877f52b89e07 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/Feed.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/Feed.php
@@ -22,7 +22,7 @@
  *   id = "feed",
  *   title = @Translation("Feed"),
  *   help = @Translation("Display the view as a feed, such as an RSS feed."),
- *   uses_hook_menu = TRUE,
+ *   uses_route = TRUE,
  *   admin = @Translation("Feed")
  * )
  */
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php
index 294d6f600b783a3ade2fe82eca34cc827fd34301..2bbdb0ff88b0e5458204a401193f4c817bb4fbc9 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php
@@ -22,6 +22,7 @@
  *   title = @Translation("Page"),
  *   help = @Translation("Display the view as a page, with a URL and menu links."),
  *   uses_hook_menu = TRUE,
+ *   uses_route = TRUE,
  *   contextual_links_locations = {"page"},
  *   theme = "views_view",
  *   admin = @Translation("Page")
@@ -92,7 +93,10 @@ public function execute() {
     // And the title, which is much easier.
     drupal_set_title(filter_xss_admin($this->view->getTitle()), PASS_THROUGH);
 
-    return $render;
+    $response = $this->view->getResponse();
+    $response->setContent(drupal_render_page($render));
+
+    return $response;
   }
 
   /**
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
index 51b905a405470173c18785d11481c5efafb30e3e..bb0508037bb020d89a094ab85aa345175a4359b9 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
@@ -8,13 +8,16 @@
 namespace Drupal\views\Plugin\views\display;
 
 use Drupal\views\Views;
+
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
 
 /**
  * The base display plugin for path/callbacks. This is used for pages and feeds.
  */
-abstract class PathPluginBase extends DisplayPluginBase {
+abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouterInterface {
 
   /**
    * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::hasPath().
@@ -33,6 +36,69 @@ protected function defineOptions() {
     return $options;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function collectRoutes(RouteCollection $collection) {
+    $view_id = $this->view->storage->id();
+    $display_id = $this->display['id'];
+
+    $defaults = array(
+      '_controller' => 'Drupal\views\Routing\ViewPageController::handle',
+      'view_id' => $view_id,
+      'display_id' => $display_id,
+    );
+
+    // @todo How do we apply argument validation?
+    $bits = explode('/', $this->getOption('path'));
+    // @todo Figure out validation/argument loading.
+    // Replace % with %views_arg for menu autoloading and add to the
+    // page arguments so the argument actually comes through.
+    $arg_counter = 0;
+
+    $this->view->initHandlers();
+    $view_arguments = $this->view->argument;
+
+    $argument_ids = array_keys($view_arguments);
+    $total_arguments = count($argument_ids);
+
+    // Replace arguments in the views UI (defined via %) with parameters in
+    // routes (defined via {}). As a name for the parameter use arg_$key, so
+    // it can be pulled in the views controller from the request.
+    foreach ($bits as $pos => $bit) {
+      if ($bit == '%') {
+        // Generate the name of the parameter using the key of the argument
+        // handler.
+        $arg_id = 'arg_' . $argument_ids[$arg_counter++];
+        $bits[$pos] = '{' . $arg_id . '}';
+      }
+    }
+
+    // Add missing arguments not defined in the path, but added as handler.
+    while (($total_arguments - $arg_counter) > 0) {
+      $arg_id = 'arg_' . $argument_ids[$arg_counter++];
+      $bit = '{' . $arg_id . '}';
+      // In contrast to the previous loop add the defaults here, as % was not
+      // specified, which means the argument is optional.
+      $defaults[$arg_id] = NULL;
+      $bits[] = $bit;
+    }
+
+    $route_path = '/' . implode('/', $bits);
+
+    $route = new Route($route_path, $defaults);
+
+    // Add access check parameters to the route.
+    $access_plugin = $this->getPlugin('access');
+    if (!isset($access_plugin)) {
+      // @todo Do we want to support a default plugin in getPlugin itself?
+      $access_plugin = Views::pluginManager('access')->createInstance('none');
+    }
+    $access_plugin->alterRouteDefinition($route);
+
+    $collection->add("view.$view_id.$display_id", $route);
+  }
+
   /**
    * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::executeHookMenu().
    */
@@ -60,49 +126,9 @@ public function executeHookMenu($callbacks) {
 
     $path = implode('/', $bits);
 
-    $access_plugin = $this->getPlugin('access');
-    if (!isset($access_plugin)) {
-      $access_plugin = Views::pluginManager('access')->createInstance('none');
-    }
-
-    // Get access callback might return an array of the callback + the dynamic
-    // arguments.
-    $access_plugin_callback = $access_plugin->get_access_callback();
-
-    if (is_array($access_plugin_callback)) {
-      $access_arguments = array();
-
-      // Find the plugin arguments.
-      $access_plugin_method = array_shift($access_plugin_callback);
-      $access_plugin_arguments = array_shift($access_plugin_callback);
-      if (!is_array($access_plugin_arguments)) {
-        $access_plugin_arguments = array();
-      }
-
-      $access_arguments[0] = array($access_plugin_method, &$access_plugin_arguments);
-
-      // Move the plugin arguments to the access arguments array.
-      $i = 1;
-      foreach ($access_plugin_arguments as $key => $value) {
-        if (is_int($value)) {
-          $access_arguments[$i] = $value;
-          $access_plugin_arguments[$key] = $i;
-          $i++;
-        }
-      }
-    }
-    else {
-      $access_arguments = array($access_plugin_callback);
-    }
-
     if ($path) {
       $items[$path] = array(
-        // Default views page entry.
-        'page callback' => 'views_page',
-        'page arguments' => $page_arguments,
-        // Default access check (per display).
-        'access callback' => 'views_access',
-        'access arguments' => $access_arguments,
+        'route_name' => "view.{$this->view->storage->id()}.{$this->display['id']}",
         // Identify URL embedded arguments and correlate them to a handler.
         'load arguments'  => array($this->view->storage->id(), $this->display['id'], '%index'),
       );
@@ -159,11 +185,6 @@ public function executeHookMenu($callbacks) {
             $default_path = implode('/', $bits);
             $items[$default_path] = array(
               // Default views page entry.
-              'page callback' => 'views_page',
-              'page arguments' => $page_arguments,
-              // Default access check (per display).
-              'access callback' => 'views_access',
-              'access arguments' => $access_arguments,
               // Identify URL embedded arguments and correlate them to a
               // handler.
               'load arguments'  => array($this->view->storage->id(), $this->display['id'], '%index'),
diff --git a/core/modules/views/lib/Drupal/views/Routing/ViewPageController.php b/core/modules/views/lib/Drupal/views/Routing/ViewPageController.php
new file mode 100644
index 0000000000000000000000000000000000000000..727ebbbf21640cda81882146c5c9162b1680feb0
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Routing/ViewPageController.php
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Routing\ViewPageController.
+ */
+
+namespace Drupal\views\Routing;
+
+use Drupal\Core\ControllerInterface;
+use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\views\ViewExecutableFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+
+/**
+ * Defines a page controller to execute and render a view.
+ */
+class ViewPageController implements ControllerInterface {
+
+  /**
+   * The entity storage controller.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageControllerInterface
+   */
+  protected $storageController;
+
+  /**
+   * The view executable factory.
+   *
+   * @var \Drupal\views\ViewExecutableFactory
+   */
+  protected $executableFactory;
+
+  /**
+   * Constructs a ViewPageController object.
+   *
+   * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
+   *   The entity storage controller.
+   * @param \Drupal\views\ViewExecutableFactory $executable_factory
+   *   The view executable factory
+   */
+  public function __construct(EntityStorageControllerInterface $storage_controller, ViewExecutableFactory $executable_factory) {
+    $this->storageController = $storage_controller;
+    $this->executableFactory = $executable_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.entity')->getStorageController('view'),
+      $container->get('views.executable')
+    );
+  }
+
+  /**
+   * Handles a response for a view.
+   */
+  public function handle(Request $request) {
+    $view_id = $request->attributes->get('view_id');
+    $display_id = $request->attributes->get('display_id');
+
+    $entities = $this->storageController->load(array($view_id));
+    $entity = reset($entities);
+    if (empty($entity)) {
+      throw new NotFoundHttpException(format_string('Page controller for view %id requested, but view was not found.', array('%id' => $view_id)));
+    }
+    $view = $this->executableFactory->get($entity);
+    $view->setDisplay($display_id);
+    $view->initHandlers();
+
+    $args = array();
+    foreach (array_keys((array) $view->argument) as $argument_id) {
+      $arg = $request->attributes->get('arg_' . $argument_id);
+      if (isset($arg)) {
+        $args[] = $arg;
+      }
+    }
+
+    return $view->executeDisplay($display_id, $args);
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/AccessTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/AccessTest.php
index b4c5118888ee631bb0db5e85b46047fa92e6f14b..03dbbad9f21722d35a76fbab58bebde57c469ea7 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Plugin/AccessTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/AccessTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\views\Tests\Plugin;
 
+use Drupal\views\Tests\ViewTestData;
+
 /**
  * Basic test for pluggable access.
  *
@@ -35,6 +37,8 @@ protected function setUp() {
 
     $this->enableViewsTestModule();
 
+    ViewTestData::importTestViews(get_class($this), array('views_test_data'));
+
     $this->admin_user = $this->drupalCreateUser(array('access all views'));
     $this->web_user = $this->drupalCreateUser();
     $this->web_role = current($this->web_user->roles);
@@ -42,6 +46,7 @@ protected function setUp() {
     $this->normal_role = $this->drupalCreateRole(array());
     $this->normal_user = $this->drupalCreateUser(array('views_test_data test permission'));
     $this->normal_user->roles[$this->normal_role] = $this->normal_role;
+
     // @todo when all the plugin information is cached make a reset function and
     // call it here.
   }
@@ -74,57 +79,19 @@ function testStaticAccessPlugin() {
     $access_plugin = $view->display_handler->getPlugin('access');
 
     $this->assertFalse($access_plugin->access($this->normal_user));
+    $this->drupalGet('test_access_static');
+    $this->assertResponse(403);
 
+    $display = &$view->storage->getDisplay('default');
+    $display['display_options']['access']['options']['access'] = TRUE;
     $access_plugin->options['access'] = TRUE;
-    $this->assertTrue($access_plugin->access($this->normal_user));
-
-    // FALSE comes from hook_menu caching.
-    $expected_hook_menu = array(
-      'views_test_data_test_static_access_callback', array(FALSE)
-    );
-    $hook_menu = $view->executeHookMenu('page_1');
-    $this->assertEqual($expected_hook_menu, $hook_menu['test_access_static']['access arguments'][0]);
-
-    $expected_hook_menu = array(
-      'views_test_data_test_static_access_callback', array(TRUE)
-    );
-    $this->assertTrue(views_access($expected_hook_menu));
-  }
-
-  /**
-   * Tests dynamic access plugin.
-   *
-   * @see Drupal\views_test\Plugin\views\access\DyamicTest
-   */
-  function testDynamicAccessPlugin() {
-    $view = views_get_view('test_access_dynamic');
-    $view->setDisplay();
-    $argument1 = $this->randomName();
-    $argument2 = $this->randomName();
-    state()->set('test_dynamic_access_argument1', $argument1);
-    state()->set('test_dynamic_access_argument2', $argument2);
-
-    $access_plugin = $view->display_handler->getPlugin('access');
-
-    $this->assertFalse($access_plugin->access($this->normal_user));
-
-    $access_plugin->options['access'] = TRUE;
-    $this->assertFalse($access_plugin->access($this->normal_user));
+    $view->save();
+    $this->container->get('router.builder')->rebuild();
 
-    $view->setArguments(array($argument1, $argument2));
     $this->assertTrue($access_plugin->access($this->normal_user));
 
-    // FALSE comes from hook_menu caching.
-    $expected_hook_menu = array(
-      'views_test_data_test_dynamic_access_callback', array(FALSE, 1, 2)
-    );
-    $hook_menu = $view->executeHookMenu('page_1');
-    $this->assertEqual($expected_hook_menu, $hook_menu['test_access_dynamic']['access arguments'][0]);
-
-    $expected_hook_menu = array(
-      'views_test_data_test_dynamic_access_callback', array(TRUE, 1, 2)
-    );
-    $this->assertTrue(views_access($expected_hook_menu, $argument1, $argument2));
+    $this->drupalGet('test_access_static');
+    $this->assertResponse(200);
   }
 
 }
diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
index 49ed01cdeb9a7737135e3e9a9a957f660eef466c..2b85c9368c16380d5566ab505eb8902e1d211bd9 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
@@ -2,26 +2,45 @@
 
 /**
  * @file
- * Definition of Drupal\views\Tests\Plugin\DisplayPageTest.
+ * Contains \Drupal\views\Tests\Plugin\DisplayPageTest.
  */
 
 namespace Drupal\views\Tests\Plugin;
 
-use Drupal\views\Tests\Plugin\PluginTestBase;
+use Drupal\Core\Routing\RouteBuildEvent;
+use Drupal\views\EventSubscriber\RouteSubscriber;
+use Drupal\views\Tests\ViewUnitTestBase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\Routing\RouteCollection;
 
 /**
  * Tests the page display plugin.
  *
  * @see Drupal\views\Plugin\display\Page
  */
-class DisplayPageTest extends PluginTestBase {
+class DisplayPageTest extends ViewUnitTestBase {
 
   /**
    * Views used by this test.
    *
    * @var array
    */
-  public static $testViews = array('test_page_display');
+  public static $testViews = array('test_page_display', 'test_page_display_route');
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('system', 'user');
+
+  /**
+   * The router dumper to get all routes.
+   *
+   * @var \Drupal\Core\Routing\MatcherDumper
+   */
+  protected $routerDumper;
 
   public static function getInfo() {
     return array(
@@ -31,21 +50,82 @@ public static function getInfo() {
     );
   }
 
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
     parent::setUp();
 
-    $this->enableViewsTestModule();
+    // Setup the needed tables in order to make the drupal router working.
+    $this->installSchema('system', 'router');
+    $this->installSchema('system', 'url_alias');
+    $this->installSchema('system', 'menu_router');
+    $this->installSchema('user', 'role_permission');
   }
 
   /**
    * Checks the behavior of the page for access denied/not found behaviours.
    */
   public function testPageResponses() {
-    $view = views_get_view('test_page_display');
-    $this->drupalGet('test_page_display_403');
-    $this->assertResponse(403);
-    $this->drupalGet('test_page_display_404');
-    $this->assertResponse(404);
+    // @todo Importing a route should fire a container rebuild.
+    $this->container->get('router.builder')->rebuild();
+
+    $subrequest = Request::create('/test_page_display_403', 'GET');
+    $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST);
+    $this->assertEqual($response->getStatusCode(), 403);
+
+    $subrequest = Request::create('/test_page_display_404', 'GET');
+    $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST);
+    $this->assertEqual($response->getStatusCode(), 404);
+
+    $subrequest = Request::create('/test_page_display_200', 'GET');
+    $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST);
+    $this->assertEqual($response->getStatusCode(), 200);
+  }
+
+  /**
+   * Checks that the router items are properly registered
+   */
+  public function testPageRouterItems() {
+    $subscriber = new RouteSubscriber();
+    $collection = new RouteCollection();
+    $subscriber->dynamicRoutes(new RouteBuildEvent($collection, 'dynamic_routes'));
+
+    // Check the controller defaults.
+    foreach ($collection as $id => $route) {
+      if (strpos($id, 'test_page_display_route') === 0) {
+        $this->assertEqual($route->getDefault('_controller'), 'Drupal\views\Routing\ViewPageController::handle');
+        $this->assertEqual($route->getDefault('view_id'), 'test_page_display_route');
+        $this->assertEqual($route->getDefault('display_id'), str_replace('test_page_display_route.', '', $id));
+      }
+    }
+
+    // Check the generated patterns and default values.
+    $route = $collection->get('view.test_page_display_route.page_1');
+    $this->assertEqual($route->getPath(), '/test_route_without_arguments');
+
+    $route = $collection->get('view.test_page_display_route.page_2');
+    $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_id}');
+    $this->assertTrue($route->hasDefault('arg_id'), 'A default value is set for the optional argument id.');
+
+    $route = $collection->get('view.test_page_display_route.page_3');
+    $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_id}/suffix');
+    $this->assertFalse($route->hasDefault('arg_id'), 'No default value is set for the required argument id.');
+
+    $route = $collection->get('view.test_page_display_route.page_4');
+    $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_id}/suffix/{arg_id_2}');
+    $this->assertFalse($route->hasDefault('arg_id'), 'No default value is set for the required argument id.');
+    $this->assertTrue($route->hasDefault('arg_id_2'), 'A default value is set for the optional argument id_2.');
+
+    $route = $collection->get('view.test_page_display_route.page_5');
+    $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_id}/{arg_id_2}');
+    $this->assertTrue($route->hasDefault('arg_id'), 'A default value is set for the optional argument id.');
+    $this->assertTrue($route->hasDefault('arg_id_2'), 'A default value is set for the optional argument id_2.');
+
+    $route = $collection->get('view.test_page_display_route.page_6');
+    $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_id}/{arg_id_2}');
+    $this->assertFalse($route->hasDefault('arg_id'), 'No default value is set for the required argument id.');
+    $this->assertFalse($route->hasDefault('arg_id_2'), 'No default value is set for the required argument id_2.');
   }
 
 }
diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageWebTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageWebTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..174cb7aef488887c4dfb6a2181de4f44345efa8e
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageWebTest.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Tests\Plugin\DisplayPageWebTest.
+ */
+
+namespace Drupal\views\Tests\Plugin;
+
+/**
+ * Tests the views page display plugin as webtest.
+ */
+class DisplayPageWebTest extends PluginTestBase {
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $testViews = array('test_page_display_arguments');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Display: Page plugin (web)',
+      'description' => 'Tests the page display plugin (web).',
+      'group' => 'Views Plugins',
+    );
+  }
+
+  protected function setUp() {
+    parent::setUp();
+
+    $this->enableViewsTestModule();
+  }
+
+  /**
+   * Tests arguments.
+   */
+  public function testArguments() {
+    $this->drupalGet('test_route_without_arguments');
+    $this->assertResponse(200);
+    $result = $this->xpath('//span[@class="field-content"]');
+    $this->assertEqual(count($result), 5, 'All entries was returned');
+
+    $this->drupalGet('test_route_without_arguments/1');
+    $this->assertResponse(404);
+
+    $this->drupalGet('test_route_with_argument/1');
+    $this->assertResponse(200);
+    $result = $this->xpath('//span[@class="field-content"]');
+    $this->assertEqual(count($result), 1, 'Ensure that just the filtered entry was returned.');
+    $this->assertEqual((string) $result[0], 1, 'The passed ID was returned.');
+
+    $this->drupalGet('test_route_with_suffix/1/suffix');
+    $this->assertResponse(200);
+    $result = $this->xpath('//span[@class="field-content"]');
+    $this->assertEqual(count($result), 1, 'Ensure that just the filtered entry was returned.');
+    $this->assertEqual((string) $result[0], 1, 'The passed ID was returned.');
+
+    $this->drupalGet('test_route_with_suffix_and_argument/1/suffix/2');
+    $this->assertResponse(200);
+    $result = $this->xpath('//span[@class="field-content"]');
+    $this->assertEqual(count($result), 0, 'No result was returned.');
+
+    $this->drupalGet('test_route_with_suffix_and_argument/1/suffix/1');
+    $this->assertResponse(200);
+    $result = $this->xpath('//span[@class="field-content"]');
+    $this->assertEqual(count($result), 1, 'Ensure that just the filtered entry was returned.');
+    $this->assertEqual((string) $result[0], 1, 'The passed ID was returned.');
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..d937aea64c21760f9d7ee56c56b68e75df06e24c
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Tests\ViewPageControllerTest.
+ */
+
+namespace Drupal\views\Tests;
+
+use Drupal\views\Routing\ViewPageController;
+use Drupal\views\ViewExecutableFactory;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+
+/**
+ * Tests the page controller but not the actualy execution/rendering of a view.
+ *
+ * @see \Drupal\views\Routing\ViewPageController
+ */
+class ViewPageControllerTest extends ViewUnitTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('user');
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $testViews = array('test_page_view');
+
+  /**
+   * The page controller of views.
+   *
+   * @var \Drupal\views\Routing\ViewPageController
+   */
+  public $pageController;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'View page controller test',
+      'description' => 'Tests views page controller.',
+      'group' => 'Views'
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->installSchema('system', 'menu_router');
+    $this->installSchema('user', 'role_permission');
+
+    $this->pageController = new ViewPageController($this->container->get('plugin.manager.entity')->getStorageController('view'), new ViewExecutableFactory());
+  }
+
+  /**
+   * Tests the page controller.
+   */
+  public function testPageController() {
+    $this->assertTrue($this->pageController instanceof ViewPageController, 'Ensure the right class is stored in the container');
+
+    // Pass in a non existent view.
+    $random_view_id = $this->randomName();
+
+    $request = new Request();
+    $request->attributes->set('view_id', $random_view_id);
+    $request->attributes->set('display_id', 'default');
+    try {
+      $this->pageController->handle($request);
+      $this->fail('No exception thrown on non-existing view.');
+    }
+
+    catch (NotFoundHttpException $e) {
+      $this->pass('Exception thrown when view was not found');
+    }
+
+    $request->attributes->set('view_id', 'test_page_view');
+    $output = $this->pageController->handle($request);
+    $this->assertTrue(is_array($output));
+    $this->assertEqual($output['#view']->storage->id, 'test_page_view', 'The right view was executed.');
+
+    $request->attributes->set('display_id', 'page_1');
+    $output = $this->pageController->handle($request);
+    $this->assertTrue($output instanceof Response, 'Ensure the page display returns a response object.');
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php b/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php
index 93685aa1acb1eae244195518230a27f2b172efc8..51a0c515ff757c0a8b053a2f182896732c9d408f 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php
@@ -8,6 +8,7 @@
 namespace Drupal\views\Tests;
 
 use Drupal\simpletest\WebTestBase;
+use Drupal\views\ViewExecutable;
 
 /**
  * Defines a base class for Views testing in the full web test environment.
@@ -31,6 +32,10 @@ abstract class ViewTestBase extends WebTestBase {
   protected function setUp() {
     parent::setUp();
 
+    // Ensure that the plugin definitions are cleared.
+    foreach (ViewExecutable::getPluginTypes() as $plugin_type) {
+      $this->container->get("plugin.manager.views.$plugin_type")->clearCachedDefinitions();
+    }
     ViewTestData::importTestViews(get_class($this), array('views_test_config'));
   }
 
@@ -47,6 +52,8 @@ protected function enableViewsTestModule() {
 
     module_enable(array('views_test_data'));
     $this->resetAll();
+    $this->rebuildContainer();
+    $this->container->get('module_handler')->reload();
 
     // Load the test dataset.
     $data_set = $this->dataSet();
diff --git a/core/modules/views/lib/Drupal/views/ViewsAccessCheck.php b/core/modules/views/lib/Drupal/views/ViewsAccessCheck.php
new file mode 100644
index 0000000000000000000000000000000000000000..b5242bfee78b8eac250ec151e03d471e5bd9ac02
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/ViewsAccessCheck.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\ViewsAccessCheck.
+ */
+
+namespace Drupal\views;
+
+use Drupal\Core\Access\AccessCheckInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Defines a route access checker for the _access_all_views permission.
+ *
+ * @todo We could leverage the permission one as well?
+ */
+class ViewsAccessCheck implements AccessCheckInterface {
+
+  /**
+   * Implements AccessCheckInterface::applies().
+   */
+  public function applies(Route $route) {
+    return array_key_exists('view_id', $route->getDefaults());
+  }
+
+  /**
+   * Implements AccessCheckInterface::applies().
+   */
+  public function access(Route $route, Request $request) {
+    $access = user_access('access all views');
+
+    return $access ?: NULL;
+  }
+
+}
diff --git a/core/modules/views/tests/views_test_config/test_views/views.view.test_access_dynamic.yml b/core/modules/views/tests/views_test_config/test_views/views.view.test_access_dynamic.yml
deleted file mode 100644
index 5af9c8d051c74ee9760a9c724634f0055735cb80..0000000000000000000000000000000000000000
--- a/core/modules/views/tests/views_test_config/test_views/views.view.test_access_dynamic.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-base_table: node
-core: '8'
-description: ''
-status: '1'
-display:
-  default:
-    display_options:
-      access:
-        type: test_dynamic
-      cache:
-        type: none
-      exposed_form:
-        type: basic
-      pager:
-        type: full
-      style:
-        type: default
-      row:
-        type: fields
-    display_plugin: default
-    display_title: Master
-    id: default
-    position: '0'
-  page_1:
-    display_options:
-      path: test_access_dynamic
-    display_plugin: page
-    display_title: Page
-    id: page_1
-    position: '0'
-label: ''
-id: test_access_dynamic
-tag: ''
diff --git a/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display.yml b/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display.yml
index 01f50ffc9cc3ba4c3d42611ff396c4aa32d508b9..a293826d4d885ff8ada057112dbeb6504f9e68ee 100644
--- a/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display.yml
+++ b/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display.yml
@@ -1,14 +1,22 @@
-base_table: node
+base_table: views_test_data
 core: '8'
 description: ''
 status: '1'
 display:
   default:
     display_options:
-      access:
-        type: none
-      cache:
-        type: none
+      defaults:
+        fields: '0'
+        pager: '0'
+        pager_options: '0'
+        sorts: '0'
+      fields:
+        age:
+          field: age
+          id: age
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
     display_plugin: default
     display_title: Master
     id: default
@@ -19,14 +27,21 @@ display:
     display_plugin: page
     display_title: Page
     id: page_1
-    position: '0'
+    position: '1'
   page_2:
     display_options:
       path: test_page_display_404
     display_plugin: page
     display_title: Page
     id: page_2
-    position: '0'
+    position: '2'
+  page_3:
+    display_options:
+      path: test_page_display_200
+    display_plugin: page
+    display_title: Page
+    id: page_3
+    position: '3'
 label: ''
 id: test_page_display
 tag: ''
diff --git a/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display_arguments.yml b/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display_arguments.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2ece46ed99a7e540480c8750294b321866d1d7d8
--- /dev/null
+++ b/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display_arguments.yml
@@ -0,0 +1,87 @@
+base_table: views_test_data
+base_field: id
+core: '8'
+description: ''
+status: '1'
+display:
+  default:
+    display_options:
+      defaults:
+        fields: '0'
+        pager: '0'
+        pager_options: '0'
+        sorts: '0'
+      fields:
+        id:
+          id: id
+          field: id
+          table: views_test_data
+          plugin_id: numeric
+    display_plugin: default
+    display_title: Master
+    id: default
+    position: '0'
+  page_1:
+    display_options:
+      path: test_route_without_arguments
+    display_plugin: page
+    display_title: Page
+    id: page_1
+    position: '0'
+  page_2:
+    display_options:
+      defaults:
+        arguments: '0'
+      arguments:
+        id:
+          field: id
+          id: id
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+      path: test_route_with_argument
+    display_plugin: page
+    display_title: Page
+    id: page_2
+    position: '0'
+  page_3:
+    display_options:
+      defaults:
+        arguments: '0'
+      arguments:
+        id:
+          field: id
+          id: id
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+      path: test_route_with_suffix/%/suffix
+    display_plugin: page
+    display_title: Page
+    id: page_3
+    position: '0'
+  page_4:
+    display_options:
+      defaults:
+        arguments: '0'
+      arguments:
+        id:
+          field: id
+          id: id
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+        id_2:
+          field: id
+          id: id_2
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+      path: test_route_with_suffix_and_argument/%/suffix
+    display_plugin: page
+    display_title: Page
+    id: page_4
+    position: '0'
+human_name: ''
+id: test_page_display_arguments
+tag: ''
diff --git a/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display_route.yml b/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display_route.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cdaebd2ac5b81f90c7e3682f189815e398697365
--- /dev/null
+++ b/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display_route.yml
@@ -0,0 +1,142 @@
+base_table: views_test_data
+base_field: id
+core: '8'
+description: ''
+status: '1'
+display:
+  default:
+    display_options:
+      defaults:
+        fields: '0'
+        pager: '0'
+        pager_options: '0'
+        sorts: '0'
+      fields:
+        id:
+          id: id
+          table: views_test_data
+          field: id
+          plugin_id: numeric
+    display_plugin: default
+    display_title: Master
+    id: default
+    position: '0'
+  page_1:
+    display_options:
+      path: test_route_without_arguments
+    display_plugin: page
+    display_title: Page
+    id: page_1
+    position: '0'
+  page_2:
+    display_options:
+      defaults:
+        arguments: '0'
+      arguments:
+        id:
+          field: id
+          id: id
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+      path: test_route_with_argument
+    display_plugin: page
+    display_title: Page
+    id: page_2
+    position: '0'
+  page_3:
+    display_options:
+      defaults:
+        arguments: '0'
+      arguments:
+        id:
+          field: id
+          id: id
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+      path: test_route_with_argument/%/suffix
+    display_plugin: page
+    display_title: Page
+    id: page_3
+    position: '0'
+  page_4:
+    display_options:
+      defaults:
+        arguments: '0'
+      arguments:
+        id:
+          field: id
+          id: id
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+        id_2:
+          field: id
+          id: id_2
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+      path: test_route_with_argument/%/suffix
+    display_plugin: page
+    display_title: Page
+    id: page_4
+    position: '0'
+  page_5:
+    display_options:
+      defaults:
+        arguments: '0'
+      arguments:
+        id:
+          field: id
+          id: id
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+        id_2:
+          field: id
+          id: id_2
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+      path: test_route_with_argument
+    display_plugin: page
+    display_title: Page
+    id: page_5
+    position: '0'
+  page_6:
+    display_options:
+      defaults:
+        arguments: '0'
+      arguments:
+        id:
+          field: id
+          id: id
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+        id_2:
+          field: id
+          id: id_2
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+      path: test_route_with_argument/%/%
+    display_plugin: page
+    display_title: Page
+    id: page_6
+    position: '0'
+  page_7:
+    display_options:
+      defaults:
+        access: '0'
+      access:
+        type: test_static
+      path: test_route_arguments_access
+    display_plugin: page
+    display_title: Page
+    id: page_7
+    position: '0'
+human_name: ''
+id: test_page_display_route
+tag: ''
diff --git a/core/modules/views/tests/views_test_config/test_views/views.view.test_page_view.yml b/core/modules/views/tests/views_test_config/test_views/views.view.test_page_view.yml
new file mode 100644
index 0000000000000000000000000000000000000000..41d09e5fdf03103f12bf9761b1c94812cd001b2a
--- /dev/null
+++ b/core/modules/views/tests/views_test_config/test_views/views.view.test_page_view.yml
@@ -0,0 +1,30 @@
+base_table: views_test_data
+core: '8'
+description: ''
+disabled: '0'
+display:
+  default:
+    display_options:
+      defaults:
+        fields: '0'
+        pager: '0'
+        pager_options: '0'
+        sorts: '0'
+      fields:
+        age:
+          field: age
+          id: age
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+    display_plugin: default
+    display_title: Master
+    id: default
+    position: '0'
+  page_1:
+    display_plugin: page
+    display_title: Test page view
+    id: page_1
+human_name: ''
+id: test_page_view
+tag: ''
diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/access/DynamicTest.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/access/DynamicTest.php
deleted file mode 100644
index b153c93be1ab6df2db3b15f3e4f437fccc0c7dde..0000000000000000000000000000000000000000
--- a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/access/DynamicTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\views_test_data\Plugin\views\access\DynamicTest.
- */
-
-namespace Drupal\views_test_data\Plugin\views\access;
-
-use Drupal\Component\Annotation\Plugin;
-use Drupal\Core\Annotation\Translation;
-use Drupal\views\Plugin\views\access\AccessPluginBase;
-
-/**
- * Tests a dynamic access plugin.
- *
- * @Plugin(
- *   id = "test_dynamic",
- *   title = @Translation("Dynamic test access plugin."),
- *   help = @Translation("Provides a dynamic test access plugin.")
- * )
- */
-class DynamicTest extends AccessPluginBase {
-
-  protected function defineOptions() {
-    $options = parent::defineOptions();
-    $options['access'] = array('default' => FALSE, 'bool' => TRUE);
-
-    return $options;
-  }
-
-  public function access($account) {
-    return !empty($this->options['access']) && isset($this->view->args[0]) && $this->view->args[0] == state()->get('test_dynamic_access_argument1') && isset($this->view->args[1]) && $this->view->args[1] == state()->get('test_dynamic_access_argument2');
-  }
-
-  function get_access_callback() {
-    return array('views_test_data_test_dynamic_access_callback', array(!empty($options['access']), 1, 2));
-  }
-
-}
diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/access/StaticTest.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/access/StaticTest.php
index 398e8d7cf7b89ef0170632d1491968f445dd8555..fe620977e4c9d66c2530f68f0b7beeafde0bc2c2 100644
--- a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/access/StaticTest.php
+++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/access/StaticTest.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Annotation\Plugin;
 use Drupal\Core\Annotation\Translation;
 use Drupal\views\Plugin\views\access\AccessPluginBase;
+use Symfony\Component\Routing\Route;
 
 /**
  * Tests a static access plugin.
@@ -33,8 +34,13 @@ public function access($account) {
     return !empty($this->options['access']);
   }
 
-  function get_access_callback() {
-    return array('views_test_data_test_static_access_callback', array(!empty($options['access'])));
+  /**
+   * {@inheritdoc}
+   */
+  public function alterRouteDefinition(Route $route) {
+    if (!empty($this->options['access'])) {
+      $route->setRequirement('_access', 'TRUE');
+    }
   }
 
 }
diff --git a/core/modules/views/tests/views_test_config/test_views/views.view.test_access_static.yml b/core/modules/views/tests/views_test_data/test_views/views.view.test_access_static.yml
similarity index 100%
rename from core/modules/views/tests/views_test_config/test_views/views.view.test_access_static.yml
rename to core/modules/views/tests/views_test_data/test_views/views.view.test_access_static.yml
diff --git a/core/modules/views/tests/views_test_data/views_test_data.module b/core/modules/views/tests/views_test_data/views_test_data.module
index 9dc515930e3b74f93c32ddfcb481991789eb6dcd..4dd4386f16b70bc9b600c5dd5af39c6cb9dea093 100644
--- a/core/modules/views/tests/views_test_data/views_test_data.module
+++ b/core/modules/views/tests/views_test_data/views_test_data.module
@@ -19,14 +19,6 @@ function views_test_data_permission() {
   );
 }
 
-function views_test_data_test_static_access_callback($access) {
-  return $access;
-}
-
-function views_test_data_test_dynamic_access_callback($access, $argument1, $argument2) {
-  return $access && $argument1 == state()->get('test_dynamic_access_argument1') && $argument2 == state()->get('test_dynamic_access_argument2');
-}
-
 /**
  * Access callback for the generic handler test.
  *
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 09d8c6eaaa32d975e3bced4b979f9a63a629a200..99b29c02507716e6203e2307736c28a2388bcd10 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -354,16 +354,6 @@ function views_menu_alter(&$callbacks) {
           // overriding) one that we removed above.
           $callbacks[$path] = $item;
         }
-        else {
-          // This item already exists, so it must be one that we added.
-          // We change the various callback arguments to pass an array
-          // of possible display IDs instead of a single ID.
-          $callbacks[$path]['page arguments'][1] = (array)$callbacks[$path]['page arguments'][1];
-          $callbacks[$path]['page arguments'][1][] = $display_id;
-          $callbacks[$path]['access arguments'][] = $item['access arguments'][0];
-          $callbacks[$path]['load arguments'][1] = (array)$callbacks[$path]['load arguments'][1];
-          $callbacks[$path]['load arguments'][1][] = $display_id;
-        }
         $our_paths[$path] = TRUE;
       }
     }
@@ -654,7 +644,6 @@ function views_field_create_instance($instance) {
   cache('views_info')->deleteAll();
   cache('views_results')->deleteAll();
 }
-
 /**
  * Implements hook_field_update_instance.
  */
@@ -686,6 +675,13 @@ function views_invalidate_cache() {
 
   $module_handler = Drupal::moduleHandler();
 
+  // Set the router to be rebuild.
+  // @todo Figure out why the cache rebuild is trigged but the route table
+  //   does not exist yet.
+  if (db_table_exists('router')) {
+    Drupal::service('router.builder')->rebuild();
+  }
+
   // Invalidate the block cache to update views block derivatives.
   if ($module_handler->moduleExists('block')) {
     Drupal::service('plugin.manager.block')->clearCachedDefinitions();
@@ -695,68 +691,6 @@ function views_invalidate_cache() {
   $module_handler->invokeAll('views_invalidate_cache');
 }
 
-/**
- * Determine if the logged in user has access to a view.
- *
- * This function should only be called from a menu hook or some other
- * embedded source. Each argument is the result of a call to
- * views_plugin_access::get_access_callback() which is then used
- * to determine if that display is accessible. If *any* argument
- * is accessible, then the view is accessible.
- */
-function views_access() {
-  $args = func_get_args();
-  foreach ($args as $arg) {
-    if ($arg === TRUE) {
-      return TRUE;
-    }
-
-    if (!is_array($arg)) {
-      continue;
-    }
-
-    list($callback, $arguments) = $arg;
-    $arguments = $arguments ? $arguments : array();
-    // Bring dynamic arguments to the access callback.
-    foreach ($arguments as $key => $value) {
-      if (is_int($value) && isset($args[$value])) {
-        $arguments[$key] = $args[$value];
-      }
-    }
-    if (function_exists($callback) && call_user_func_array($callback, $arguments)) {
-      return TRUE;
-    }
-  }
-
-  return FALSE;
-}
-
-/**
- * Access callback for the views_plugin_access_perm access plugin.
- *
- * Determine if the specified user has access to a view on the basis of
- * permissions. If the $account argument is omitted, the current user
- * is used.
- */
-function views_check_perm($perm, $account = NULL) {
-  return user_access($perm, $account) || user_access('access all views', $account);
-}
-
-/**
- * Access callback for the views_plugin_access_role access plugin.
-
- * Determine if the specified user has access to a view on the basis of any of
- * the requested roles. If the $account argument is omitted, the current user
- * is used.
- */
-function views_check_roles($rids, $account = NULL) {
-  global $user;
-  $account = isset($account) ? $account : $user;
-  $roles = array_keys($account->roles);
-  $roles[] = $account->uid ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID;
-  return user_access('access all views', $account) || array_intersect(array_filter($rids), $roles);
-}
-
 /**
  * Set the current 'page view' that is being displayed so that it is easy
  * for other modules or the theme to identify.
diff --git a/core/modules/views/views.services.yml b/core/modules/views/views.services.yml
index 0f4b0d2daaccc833faf78099e3adbd26dc9a015b..1629da5e9a7097b2f038094f647b9ba752f02748 100644
--- a/core/modules/views/views.services.yml
+++ b/core/modules/views/views.services.yml
@@ -78,3 +78,12 @@ services:
     factory_method: get
     factory_service: cache_factory
     arguments: [views_results]
+  views.route_subscriber:
+    class: Drupal\views\EventSubscriber\RouteSubscriber
+    arguments: ['@config.factory']
+    tags:
+      - { name: 'event_subscriber' }
+  views.route_access_check:
+    class: Drupal\views\ViewsAccessCheck
+    tags:
+      - { name: 'access_check' }