diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index d578499..23aba4f 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -130,8 +130,8 @@ function menu_local_tasks($level = 0) {
     if (!\Drupal::request()->attributes->has('exception') && !empty($route_name)) {
       $manager = \Drupal::service('plugin.manager.menu.local_task');
       $local_tasks = $manager->getTasksBuild($route_name);
-      foreach ($local_tasks as $level => $items) {
-        $data['tabs'][$level] = empty($data['tabs'][$level]) ? $items : array_merge($data['tabs'][$level], $items);
+      foreach ($local_tasks as $tab_level => $items) {
+        $data['tabs'][$tab_level] = empty($data['tabs'][$tab_level]) ? $items : array_merge($data['tabs'][$tab_level], $items);
       }
     }
 
@@ -177,16 +177,6 @@ function menu_secondary_local_tasks() {
 }
 
 /**
- * Returns the rendered local actions at the current level.
- */
-function menu_get_local_actions() {
-  $links = menu_local_tasks();
-  $route_name = Drupal::routeMatch()->getRouteName();
-  $manager = \Drupal::service('plugin.manager.menu.local_action');
-  return $manager->getActionsForRoute($route_name) + $links['actions'];
-}
-
-/**
  * Returns the router path, or the path for a default local task's parent.
  */
 function menu_tab_root_path() {
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 3b156a5..dfdcfaf 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1362,14 +1362,6 @@ function template_preprocess_page(&$variables) {
     $variables['is_front'] = FALSE;
     $variables['db_is_active'] = FALSE;
   }
-  if (!defined('MAINTENANCE_MODE')) {
-    $variables['action_links']   = menu_get_local_actions();
-    $variables['tabs']           = menu_local_tabs();
-  }
-  else {
-    $variables['action_links']   = array();
-    $variables['tabs']           = array();
-  }
 
   if ($node = \Drupal::routeMatch()->getParameter('node')) {
     $variables['node'] = $node;
diff --git a/core/lib/Drupal/Core/Menu/LocalActionManager.php b/core/lib/Drupal/Core/Menu/LocalActionManager.php
index db6acef..bdee9f3 100644
--- a/core/lib/Drupal/Core/Menu/LocalActionManager.php
+++ b/core/lib/Drupal/Core/Menu/LocalActionManager.php
@@ -190,10 +190,11 @@ public function getActionsForRoute($route_appears) {
           'url' => Url::fromRoute($route_name, $route_parameters),
           'localized_options' => $plugin->getOptions($this->routeMatch),
         ),
-        '#access' => $this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account),
+        '#access' => $this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account, TRUE),
         '#weight' => $plugin->getWeight(),
       );
     }
+
     return $links;
   }
 
diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManager.php b/core/lib/Drupal/Core/Menu/LocalTaskManager.php
index 70b1102..826e549 100644
--- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php
+++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php
@@ -301,31 +301,28 @@ public function getTasksBuild($current_route_name) {
         $route_name = $child->getRouteName();
         $route_parameters = $child->getRouteParameters($this->routeMatch);
 
-        // Find out whether the user has access to the task.
-        $access = $this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account);
-        if ($access) {
-          $active = $this->isRouteActive($current_route_name, $route_name, $route_parameters);
+        $active = $this->isRouteActive($current_route_name, $route_name, $route_parameters);
 
-          // The plugin may have been set active in getLocalTasksForRoute() if
-          // one of its child tabs is the active tab.
-          $active = $active || $child->getActive();
-          // @todo It might make sense to use link render elements instead.
+        // The plugin may have been set active in getLocalTasksForRoute() if
+        // one of its child tabs is the active tab.
+        $active = $active || $child->getActive();
+        // @todo It might make sense to use link render elements instead.
 
-          $link = array(
-            'title' => $this->getTitle($child),
-            'url' => Url::fromRoute($route_name, $route_parameters),
-            'localized_options' => $child->getOptions($this->routeMatch),
-          );
-          $build[$level][$plugin_id] = array(
-            '#theme' => 'menu_local_task',
-            '#link' => $link,
-            '#active' => $active,
-            '#weight' => $child->getWeight(),
-            '#access' => $access,
-          );
-        }
+        $link = array(
+          'title' => $this->getTitle($child),
+          'url' => Url::fromRoute($route_name, $route_parameters),
+          'localized_options' => $child->getOptions($this->routeMatch),
+        );
+        $build[$level][$plugin_id] = array(
+          '#theme' => 'menu_local_task',
+          '#link' => $link,
+          '#active' => $active,
+          '#weight' => $child->getWeight(),
+          '#access' => $access = $this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account, TRUE),
+        );
       }
     }
+
     return $build;
   }
 
diff --git a/core/modules/aggregator/src/Tests/AggregatorTestBase.php b/core/modules/aggregator/src/Tests/AggregatorTestBase.php
index 3bee7f4..7a45c33 100644
--- a/core/modules/aggregator/src/Tests/AggregatorTestBase.php
+++ b/core/modules/aggregator/src/Tests/AggregatorTestBase.php
@@ -28,7 +28,7 @@
    *
    * @var array
    */
-  public static $modules = array('node', 'aggregator', 'aggregator_test', 'views');
+  public static $modules = ['block', 'node', 'aggregator', 'aggregator_test', 'views'];
 
   /**
    * {@inheritdoc}
@@ -43,6 +43,7 @@ protected function setUp() {
 
     $this->adminUser = $this->drupalCreateUser(array('access administration pages', 'administer news feeds', 'access news feeds', 'create article content'));
     $this->drupalLogin($this->adminUser);
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/block/src/Tests/BlockHiddenRegionTest.php b/core/modules/block/src/Tests/BlockHiddenRegionTest.php
index 347cdaf..99d9deb 100644
--- a/core/modules/block/src/Tests/BlockHiddenRegionTest.php
+++ b/core/modules/block/src/Tests/BlockHiddenRegionTest.php
@@ -42,6 +42,7 @@ protected function setUp() {
 
     $this->drupalLogin($this->adminUser);
     $this->drupalPlaceBlock('search_form_block');
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php
index 5ab46e1..015d8a1 100644
--- a/core/modules/block/src/Tests/BlockTest.php
+++ b/core/modules/block/src/Tests/BlockTest.php
@@ -222,6 +222,7 @@ public function testBlockThemeSelector() {
   function testThemeName() {
     // Enable the help block.
     $this->drupalPlaceBlock('help_block', array('region' => 'help'));
+    $this->drupalPlaceBlock('system_tabs_block');
     // Explicitly set the default and admin themes.
     $theme = 'block_test_specialchars_theme';
     \Drupal::service('theme_handler')->install(array($theme));
diff --git a/core/modules/block/src/Tests/NonDefaultBlockAdminTest.php b/core/modules/block/src/Tests/NonDefaultBlockAdminTest.php
index bc55ab2..27b0232 100644
--- a/core/modules/block/src/Tests/NonDefaultBlockAdminTest.php
+++ b/core/modules/block/src/Tests/NonDefaultBlockAdminTest.php
@@ -24,6 +24,15 @@ class NonDefaultBlockAdminTest extends WebTestBase {
   public static $modules = array('block');
 
   /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->drupalPlaceBlock('system_tabs_block');
+  }
+
+  /**
    * Test non-default theme admin.
    */
   function testNonDefaultBlockAdmin() {
diff --git a/core/modules/block_content/src/Tests/BlockContentTestBase.php b/core/modules/block_content/src/Tests/BlockContentTestBase.php
index 82139c0..4500fd0 100644
--- a/core/modules/block_content/src/Tests/BlockContentTestBase.php
+++ b/core/modules/block_content/src/Tests/BlockContentTestBase.php
@@ -60,6 +60,7 @@ protected function setUp() {
     }
 
     $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalPlaceBlock('system_local_actions_block');
   }
 
   /**
diff --git a/core/modules/comment/src/Tests/CommentTestBase.php b/core/modules/comment/src/Tests/CommentTestBase.php
index a52cbb3..3040c6a 100644
--- a/core/modules/comment/src/Tests/CommentTestBase.php
+++ b/core/modules/comment/src/Tests/CommentTestBase.php
@@ -27,7 +27,7 @@
    *
    * @var array
    */
-  public static $modules = array('comment', 'node', 'history', 'field_ui', 'datetime');
+  public static $modules = ['block', 'comment', 'node', 'history', 'field_ui', 'datetime'];
 
   /**
    * An administrative user with permission to configure comment settings.
@@ -86,6 +86,7 @@ protected function setUp() {
 
     // Create a test node authored by the web user.
     $this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'uid' => $this->webUser->id()));
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/config/src/Tests/ConfigEntityListTest.php b/core/modules/config/src/Tests/ConfigEntityListTest.php
index ee58be5..209039a 100644
--- a/core/modules/config/src/Tests/ConfigEntityListTest.php
+++ b/core/modules/config/src/Tests/ConfigEntityListTest.php
@@ -23,7 +23,7 @@ class ConfigEntityListTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('config_test');
+  public static $modules = ['block', 'config_test'];
 
   /**
    * {@inheritdoc}
@@ -33,6 +33,7 @@ protected function setUp() {
     // Delete the override config_test entity since it is not required by this
     // test.
     \Drupal::entityManager()->getStorage('config_test')->load('override')->delete();
+    $this->drupalPlaceBlock('system_local_actions_block');
   }
 
   /**
diff --git a/core/modules/config_translation/src/Tests/ConfigTranslationListUiTest.php b/core/modules/config_translation/src/Tests/ConfigTranslationListUiTest.php
index 4ceec20..634d934 100644
--- a/core/modules/config_translation/src/Tests/ConfigTranslationListUiTest.php
+++ b/core/modules/config_translation/src/Tests/ConfigTranslationListUiTest.php
@@ -80,6 +80,7 @@ protected function setUp() {
     $this->config('locale.settings')
       ->set('translation.import_enabled', TRUE)
       ->save();
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/config_translation/src/Tests/ConfigTranslationOverviewTest.php b/core/modules/config_translation/src/Tests/ConfigTranslationOverviewTest.php
index 4c83a38..54decc8 100644
--- a/core/modules/config_translation/src/Tests/ConfigTranslationOverviewTest.php
+++ b/core/modules/config_translation/src/Tests/ConfigTranslationOverviewTest.php
@@ -24,6 +24,7 @@ class ConfigTranslationOverviewTest extends WebTestBase {
    * @var array
    */
   public static $modules = [
+    'block',
     'config_test',
     'config_translation',
     'config_translation_test',
@@ -67,6 +68,7 @@ protected function setUp() {
       ConfigurableLanguage::createFromLangcode($langcode)->save();
     }
     $this->localeStorage = $this->container->get('locale.storage');
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php b/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php
index 4bbb408..0fb16c7 100644
--- a/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php
+++ b/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php
@@ -28,7 +28,7 @@ class ConfigTranslationUiTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('node', 'contact', 'contact_test', 'config_translation', 'config_translation_test', 'views', 'views_ui', 'contextual', 'filter', 'filter_test');
+  public static $modules = ['block', 'node', 'contact', 'contact_test', 'config_translation', 'config_translation_test', 'views', 'views_ui', 'contextual', 'filter', 'filter_test'];
 
   /**
    * Languages to enable.
@@ -101,6 +101,7 @@ protected function setUp() {
       ConfigurableLanguage::createFromLangcode($langcode)->save();
     }
     $this->localeStorage = $this->container->get('locale.storage');
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/contact/src/Tests/ContactSitewideTest.php b/core/modules/contact/src/Tests/ContactSitewideTest.php
index 87d693a..9cb6910 100644
--- a/core/modules/contact/src/Tests/ContactSitewideTest.php
+++ b/core/modules/contact/src/Tests/ContactSitewideTest.php
@@ -39,6 +39,7 @@ class ContactSitewideTest extends WebTestBase {
   protected function setUp() {
     parent::setUp();
     $this->drupalPlaceBlock('system_breadcrumb_block');
+    $this->drupalPlaceBlock('system_local_actions_block');
   }
 
   /**
diff --git a/core/modules/contact/src/Tests/ContactStorageTest.php b/core/modules/contact/src/Tests/ContactStorageTest.php
index 5d62e10..f18d10e 100644
--- a/core/modules/contact/src/Tests/ContactStorageTest.php
+++ b/core/modules/contact/src/Tests/ContactStorageTest.php
@@ -28,13 +28,14 @@ class ContactStorageTest extends ContactSitewideTest {
    *
    * @var array
    */
-  public static $modules = array(
+  public static $modules = [
+    'block',
     'text',
     'contact',
     'field_ui',
     'contact_storage_test',
     'contact_test',
-  );
+  ];
 
   /**
    * Tests configuration options and the site-wide contact form.
diff --git a/core/modules/field_ui/src/Tests/EntityDisplayModeTest.php b/core/modules/field_ui/src/Tests/EntityDisplayModeTest.php
index 7b7fe12..62b2768 100644
--- a/core/modules/field_ui/src/Tests/EntityDisplayModeTest.php
+++ b/core/modules/field_ui/src/Tests/EntityDisplayModeTest.php
@@ -19,9 +19,18 @@ class EntityDisplayModeTest extends WebTestBase {
   /**
    * Modules to enable.
    *
-   * @var array
+   * @var string[]
    */
-  public static $modules = array('entity_test', 'field_ui');
+  public static $modules = ['block', 'entity_test', 'field_ui'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->drupalPlaceBlock('system_local_actions_block');
+  }
 
   /**
    * Tests the EntityViewMode user interface.
diff --git a/core/modules/field_ui/src/Tests/FieldUIRouteTest.php b/core/modules/field_ui/src/Tests/FieldUIRouteTest.php
index 332a87d..4a6380b 100644
--- a/core/modules/field_ui/src/Tests/FieldUIRouteTest.php
+++ b/core/modules/field_ui/src/Tests/FieldUIRouteTest.php
@@ -23,7 +23,7 @@ class FieldUIRouteTest extends WebTestBase {
    *
    * @var string[]
    */
-  public static $modules = array('entity_test', 'field_ui');
+  public static $modules = ['block', 'entity_test', 'field_ui'];
 
   /**
    * {@inheritdoc}
@@ -32,6 +32,7 @@ protected function setUp() {
     parent::setUp();
 
     $this->drupalLogin($this->rootUser);
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/field_ui/src/Tests/ManageFieldsTest.php b/core/modules/field_ui/src/Tests/ManageFieldsTest.php
index 8bd8688..753ebfc 100644
--- a/core/modules/field_ui/src/Tests/ManageFieldsTest.php
+++ b/core/modules/field_ui/src/Tests/ManageFieldsTest.php
@@ -66,7 +66,10 @@ class ManageFieldsTest extends WebTestBase {
    */
   protected function setUp() {
     parent::setUp();
+
     $this->drupalPlaceBlock('system_breadcrumb_block');
+    $this->drupalPlaceBlock('system_local_actions_block');
+    $this->drupalPlaceBlock('system_tabs_block');
 
     // Create a test user.
     $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer node fields', 'administer node form display', 'administer node display', 'administer taxonomy', 'administer taxonomy_term fields', 'administer taxonomy_term display', 'administer users', 'administer account settings', 'administer user display', 'bypass node access'));
diff --git a/core/modules/filter/src/Tests/FilterAdminTest.php b/core/modules/filter/src/Tests/FilterAdminTest.php
index 7e6a2d3..897943cf 100644
--- a/core/modules/filter/src/Tests/FilterAdminTest.php
+++ b/core/modules/filter/src/Tests/FilterAdminTest.php
@@ -22,7 +22,7 @@ class FilterAdminTest extends WebTestBase {
   /**
    * {@inheritdoc}
    */
-  public static $modules = array('filter', 'node');
+  public static $modules = ['block', 'filter', 'node'];
 
   /**
    * An user with administration permissions.
@@ -105,6 +105,7 @@ protected function setUp() {
     user_role_grant_permissions('authenticated', array($basic_html_format->getPermissionName()));
     user_role_grant_permissions('anonymous', array($restricted_html_format->getPermissionName()));
     $this->drupalLogin($this->adminUser);
+    $this->drupalPlaceBlock('system_local_actions_block');
   }
 
   /**
diff --git a/core/modules/filter/src/Tests/FilterFormatAccessTest.php b/core/modules/filter/src/Tests/FilterFormatAccessTest.php
index 029c60b..5df5bdc 100644
--- a/core/modules/filter/src/Tests/FilterFormatAccessTest.php
+++ b/core/modules/filter/src/Tests/FilterFormatAccessTest.php
@@ -24,7 +24,7 @@ class FilterFormatAccessTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('filter', 'node');
+  public static $modules = ['block', 'filter', 'node'];
 
   /**
    * A user with administrative permissions.
@@ -114,6 +114,7 @@ protected function setUp() {
       $this->secondAllowedFormat->getPermissionName(),
       $this->disallowedFormat->getPermissionName(),
     ));
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/forum/src/Tests/ForumTest.php b/core/modules/forum/src/Tests/ForumTest.php
index 73089e3..e80e9ad 100644
--- a/core/modules/forum/src/Tests/ForumTest.php
+++ b/core/modules/forum/src/Tests/ForumTest.php
@@ -115,6 +115,7 @@ protected function setUp() {
       'access comments',
     ));
     $this->drupalPlaceBlock('help_block', array('region' => 'help'));
+    $this->drupalPlaceBlock('system_local_actions_block');
   }
 
   /**
diff --git a/core/modules/language/src/Tests/LanguagePathMonolingualTest.php b/core/modules/language/src/Tests/LanguagePathMonolingualTest.php
index c20a57e..a4c92c0 100644
--- a/core/modules/language/src/Tests/LanguagePathMonolingualTest.php
+++ b/core/modules/language/src/Tests/LanguagePathMonolingualTest.php
@@ -21,7 +21,7 @@ class LanguagePathMonolingualTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('language', 'path');
+  public static $modules = ['block', 'language', 'path'];
 
   protected function setUp() {
     parent::setUp();
@@ -56,6 +56,7 @@ protected function setUp() {
     // Set language detection to URL.
     $edit = array('language_interface[enabled][language-url]' => TRUE);
     $this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
+    $this->drupalPlaceBlock('system_local_actions_block');
   }
 
   /**
diff --git a/core/modules/language/src/Tests/LanguageTourTest.php b/core/modules/language/src/Tests/LanguageTourTest.php
index 98418b6..91cbdab 100644
--- a/core/modules/language/src/Tests/LanguageTourTest.php
+++ b/core/modules/language/src/Tests/LanguageTourTest.php
@@ -28,7 +28,7 @@ class LanguageTourTest extends TourTestBase {
    *
    * @var array
    */
-  public static $modules = array('language', 'tour');
+  public static $modules = ['block', 'language', 'tour'];
 
   /**
    * {@inheritdoc}
@@ -37,6 +37,7 @@ protected function setUp() {
     parent::setUp();
     $this->adminUser = $this->drupalCreateUser(array('administer languages', 'access tour'));
     $this->drupalLogin($this->adminUser);
+    $this->drupalPlaceBlock('system_local_actions_block');
   }
 
   /**
diff --git a/core/modules/node/src/Tests/NodeTranslationUITest.php b/core/modules/node/src/Tests/NodeTranslationUITest.php
index c392750..013466c 100644
--- a/core/modules/node/src/Tests/NodeTranslationUITest.php
+++ b/core/modules/node/src/Tests/NodeTranslationUITest.php
@@ -28,6 +28,7 @@ class NodeTranslationUITest extends ContentTranslationUITestBase {
     'languages:language_interface',
     'theme',
     'user.permissions',
+    'route.name',
     'route.menu_active_trails:account',
     'route.menu_active_trails:footer',
     'route.menu_active_trails:main',
diff --git a/core/modules/node/src/Tests/PageEditTest.php b/core/modules/node/src/Tests/PageEditTest.php
index 9082dca..9a9f28c 100644
--- a/core/modules/node/src/Tests/PageEditTest.php
+++ b/core/modules/node/src/Tests/PageEditTest.php
@@ -16,11 +16,19 @@ class PageEditTest extends NodeTestBase {
   protected $webUser;
   protected $adminUser;
 
+  /**
+   * Modules to enable.
+   *
+   * @var string[]
+   */
+  public static $modules = ['block', 'node', 'datetime'];
+
   protected function setUp() {
     parent::setUp();
 
     $this->webUser = $this->drupalCreateUser(array('edit own page content', 'create page content'));
     $this->adminUser = $this->drupalCreateUser(array('bypass node access', 'administer nodes'));
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
index c471cea..2965267 100644
--- a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
+++ b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
@@ -103,6 +103,8 @@ function testPageCacheTags() {
       'config:block.block.bartik_main_menu',
       'config:block.block.bartik_account_menu',
       'config:block.block.bartik_messages',
+      'config:block.block.bartik_local_actions',
+      'config:block.block.bartik_tabs',
       'node_view',
       'node:' . $node_1->id(),
       'user:' . $author_1->id(),
@@ -137,6 +139,8 @@ function testPageCacheTags() {
       'config:block.block.bartik_main_menu',
       'config:block.block.bartik_account_menu',
       'config:block.block.bartik_messages',
+      'config:block.block.bartik_local_actions',
+      'config:block.block.bartik_tabs',
       'node_view',
       'node:' . $node_2->id(),
       'user:' . $author_2->id(),
diff --git a/core/modules/search/src/Tests/SearchConfigSettingsFormTest.php b/core/modules/search/src/Tests/SearchConfigSettingsFormTest.php
index a99ea45..c897616 100644
--- a/core/modules/search/src/Tests/SearchConfigSettingsFormTest.php
+++ b/core/modules/search/src/Tests/SearchConfigSettingsFormTest.php
@@ -58,6 +58,7 @@ protected function setUp() {
 
     // Enable the search block.
     $this->drupalPlaceBlock('search_form_block');
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/search/src/Tests/SearchPageTextTest.php b/core/modules/search/src/Tests/SearchPageTextTest.php
index ad311d6..368a1f0 100644
--- a/core/modules/search/src/Tests/SearchPageTextTest.php
+++ b/core/modules/search/src/Tests/SearchPageTextTest.php
@@ -23,11 +23,22 @@ class SearchPageTextTest extends SearchTestBase {
    */
   protected $searchingUser;
 
+  /**
+   * Modules to enable.
+   *
+   * @var string[]
+   */
+  public static $modules = ['block'];
+
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
     parent::setUp();
 
     // Create user.
     $this->searchingUser = $this->drupalCreateUser(array('search content', 'access user profiles', 'use advanced search'));
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/shortcut/src/Tests/ShortcutSetsTest.php b/core/modules/shortcut/src/Tests/ShortcutSetsTest.php
index 7511205..948d0db 100644
--- a/core/modules/shortcut/src/Tests/ShortcutSetsTest.php
+++ b/core/modules/shortcut/src/Tests/ShortcutSetsTest.php
@@ -18,6 +18,22 @@
 class ShortcutSetsTest extends ShortcutTestBase {
 
   /**
+   * Modules to enable.
+   *
+   * @var string[]
+   */
+  public static $modules = ['block'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->drupalPlaceBlock('system_local_actions_block');
+  }
+
+  /**
    * Tests creating a shortcut set.
    */
   function testShortcutSetAdd() {
diff --git a/core/modules/simpletest/src/Tests/BrowserTest.php b/core/modules/simpletest/src/Tests/BrowserTest.php
index 02422e7..6bfaf14 100644
--- a/core/modules/simpletest/src/Tests/BrowserTest.php
+++ b/core/modules/simpletest/src/Tests/BrowserTest.php
@@ -24,6 +24,22 @@ class BrowserTest extends WebTestBase {
   protected static $cookieSet = FALSE;
 
   /**
+   * Modules to enable.
+   *
+   * @var string[]
+   */
+  public static $modules = ['block'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->drupalPlaceBlock('system_tabs_block');
+  }
+
+  /**
    * Test \Drupal\simpletest\WebTestBase::getAbsoluteUrl().
    */
   function testGetAbsoluteUrl() {
diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml
index 7cde50d..3b7ed3f 100644
--- a/core/modules/system/config/schema/system.schema.yml
+++ b/core/modules/system/config/schema/system.schema.yml
@@ -353,6 +353,17 @@ block.settings.system_menu_block:*:
       type: integer
       label: 'Maximum number of levels'
 
+block.settings.system_tabs_block:
+  type: block_settings
+  label: 'Tabs block'
+  mapping:
+    primary:
+      type: boolean
+      label: 'Whether primary tabs are shown'
+    secondary:
+      type: boolean
+      label: 'Whether secondary tabs are shown'
+
 condition.plugin.request_path:
   type: condition.plugin
   mapping:
diff --git a/core/modules/system/src/Plugin/Block/SystemLocalActionsBlock.php b/core/modules/system/src/Plugin/Block/SystemLocalActionsBlock.php
new file mode 100644
index 0000000..23a65c0
--- /dev/null
+++ b/core/modules/system/src/Plugin/Block/SystemLocalActionsBlock.php
@@ -0,0 +1,129 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Plugin\Block\SystemLocalActionsBlock.
+ */
+
+namespace Drupal\system\Plugin\Block;
+
+use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Menu\LocalActionManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+
+/**
+ * Provides a block to display the local actions.
+ *
+ * @Block(
+ *   id = "system_local_actions_block",
+ *   admin_label = @Translation("Primary admin actions")
+ * )
+ */
+class SystemLocalActionsBlock extends BlockBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The local action manager.
+   *
+   * @var \Drupal\Core\Menu\LocalActionManagerInterface
+   */
+  protected $localActionManager;
+
+  /**
+   * The route match.
+   *
+   * @var \Drupal\Core\Routing\RouteMatchInterface
+   */
+  protected $routeMatch;
+
+  /**
+   * Creates a SystemLocalActionsBlock instance.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Menu\LocalActionManagerInterface $local_action_manager
+   *   A local action manager.
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, LocalActionManagerInterface $local_action_manager, RouteMatchInterface $route_match) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->localActionManager = $local_action_manager;
+    $this->routeMatch = $route_match;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('plugin.manager.menu.local_action'),
+      $container->get('current_route_match')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return ['label_display' => FALSE];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    $build = [];
+    $links = menu_local_tasks();
+    $route_name = $this->routeMatch->getRouteName();
+    $local_actions = $this->localActionManager->getActionsForRoute($route_name) + $links['actions'];
+    if (empty($local_actions)) {
+      return [];
+    }
+
+    $build['local_actions'] = $local_actions;
+
+    return $build;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+
+    // The "Primary admin actions" block is never cacheable because hooks creating local
+    // actions don't provide cacheability metadata.
+    // @todo Remove after https://www.drupal.org/node/2511516 has landed.
+    $form['cache']['#disabled'] = TRUE;
+    $form['cache']['#description'] = $this->t('This block is never cacheable.');
+    $form['cache']['max_age']['#value'] = 0;
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheMaxAge() {
+    // @todo Remove after https://www.drupal.org/node/2511516 has landed.
+    return 0;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheContexts() {
+    return ['route.name'];
+  }
+
+}
diff --git a/core/modules/system/src/Plugin/Block/SystemTabsBlock.php b/core/modules/system/src/Plugin/Block/SystemTabsBlock.php
new file mode 100644
index 0000000..9168455
--- /dev/null
+++ b/core/modules/system/src/Plugin/Block/SystemTabsBlock.php
@@ -0,0 +1,146 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Plugin\Block\SystemLocalTasksBlock.
+ */
+
+namespace Drupal\system\Plugin\Block;
+
+use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a "Tabs" block to display the local tasks.
+ *
+ * @Block(
+ *   id = "system_tabs_block",
+ *   admin_label = @Translation("Tabs"),
+ * )
+ */
+class SystemTabsBlock extends BlockBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'label_display' => FALSE,
+      'primary' => TRUE,
+      'secondary' => TRUE,
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    $config = $this->configuration;
+
+    $tabs = [
+      '#theme' => 'menu_local_tasks',
+    ];
+
+    // Add only selected levels for the printed output.
+    if ($config['primary']) {
+      $tabs += [
+        '#primary' => menu_primary_local_tasks(),
+      ];
+    }
+    if ($config['secondary']) {
+      $tabs += [
+        '#secondary' => menu_secondary_local_tasks(),
+      ];
+    }
+
+    if (empty($tabs['#primary']) && empty($tabs['#secondary'])) {
+      return [];
+    }
+
+    $build['tabs'] = $tabs;
+
+    return $build;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+
+    // The "Page actions" block is never cacheable because of hooks creating
+    // local tasks doesn't provide cacheability metadata.
+    // @todo Remove after https://www.drupal.org/node/2511516 has landed.
+    $form['cache']['#disabled'] = TRUE;
+    $form['cache']['#description'] = $this->t('This block is never cacheable.');
+    $form['cache']['max_age']['#value'] = 0;
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheMaxAge() {
+    // @todo Remove after https://www.drupal.org/node/2511516 has landed.
+    return 0;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheContexts() {
+    return ['route.name'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockForm($form, FormStateInterface $form_state) {
+    $config = $this->configuration;
+    $defaults = $this->defaultConfiguration();
+
+    $form['levels'] = array(
+      '#type' => 'details',
+      '#title' => $this->t('Shown tabs'),
+      '#description' => $this->t('Select tabs being shown in the block'),
+      // Open if not set to defaults.
+      '#open' => $defaults['primary'] !== $config['primary'] || $defaults['secondary'] !== $config['secondary'],
+    );
+    $form['levels']['primary'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Show primary tabs'),
+      '#default_value' => $config['primary'],
+    ];
+    $form['levels']['secondary'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Show secondary tabs'),
+      '#default_value' => $config['secondary'],
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockSubmit($form, FormStateInterface $form_state) {
+    $levels = $form_state->getValue('levels');
+    $this->configuration['primary'] = $levels['primary'];
+    $this->configuration['secondary'] = $levels['secondary'];
+  }
+
+}
diff --git a/core/modules/system/src/Tests/Menu/LocalActionTest.php b/core/modules/system/src/Tests/Menu/LocalActionTest.php
index c049198..bd1c593 100644
--- a/core/modules/system/src/Tests/Menu/LocalActionTest.php
+++ b/core/modules/system/src/Tests/Menu/LocalActionTest.php
@@ -18,9 +18,20 @@
 class LocalActionTest extends WebTestBase {
 
   /**
+   * Modules to enable.
+   *
+   * @var string[]
+   */
+  public static $modules = ['block', 'menu_test'];
+
+  /**
    * {@inheritdoc}
    */
-  public static $modules = array('menu_test');
+  protected function setUp() {
+    parent::setUp();
+
+    $this->drupalPlaceBlock('system_local_actions_block');
+  }
 
   /**
    * Tests appearance of local actions.
diff --git a/core/modules/system/src/Tests/Menu/LocalTasksTest.php b/core/modules/system/src/Tests/Menu/LocalTasksTest.php
index b7824ef..168b037 100644
--- a/core/modules/system/src/Tests/Menu/LocalTasksTest.php
+++ b/core/modules/system/src/Tests/Menu/LocalTasksTest.php
@@ -17,7 +17,28 @@
  */
 class LocalTasksTest extends WebTestBase {
 
-  public static $modules = array('menu_test', 'entity_test');
+  /**
+   * Modules to enable.
+   *
+   * @var string[]
+   */
+  public static $modules = ['block', 'menu_test', 'entity_test'];
+
+  /**
+   * The local tasks block under testing.
+   *
+   * @var \Drupal\block\Entity\Block
+   */
+  protected $sut;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->sut = $this->drupalPlaceBlock('system_tabs_block', ['id' => 'tabs_block']);
+  }
 
   /**
    * Asserts local tasks in the page output.
@@ -48,6 +69,20 @@ protected function assertLocalTasks(array $routes, $level = 0) {
   }
 
   /**
+   * Asserts that the local tasks on the specified level are not being printed.
+   *
+   * @param int $level
+   *   (optional) The local tasks level to assert; 0 for primary, 1 for
+   *   secondary. Defaults to 0.
+   */
+  protected function assertNoLocalTasks($level = 0) {
+    $elements = $this->xpath('//*[contains(@class, :class)]//a', array(
+      ':class' => $level == 0 ? 'tabs primary' : 'tabs secondary',
+    ));
+    $this->assertFalse(count($elements), 'Local tasks not found.');
+  }
+
+  /**
    * Tests the plugin based local tasks.
    */
   public function testPluginLocalTask() {
@@ -135,4 +170,52 @@ public function testPluginLocalTask() {
     $this->assertEqual('upcasting sub2', (string) $result[0]->a, 'The "upcasting sub2" tab is active.');
   }
 
+  /**
+   * Tests that local task blocks are configurable to show a specific level.
+   */
+  public function testLocalTaskBlock() {
+    // Remove the default block and create a new one.
+    $this->sut->delete();
+
+    $this->sut = $this->drupalPlaceBlock('system_tabs_block', [
+      'id' => 'tabs_block',
+      'primary' => TRUE,
+      'secondary' => FALSE,
+    ]);
+
+    $this->drupalGet(Url::fromRoute('menu_test.local_task_test_tasks_settings'));
+
+    // Verify that local tasks in the first level appear.
+    $this->assertLocalTasks([
+      ['menu_test.local_task_test_tasks_view', []],
+      ['menu_test.local_task_test_tasks_edit', []],
+      ['menu_test.local_task_test_tasks_settings', []],
+    ]);
+
+    // Verify that local tasks in the second level doesn't appear.
+    $this->assertNoLocalTasks(1);
+
+    $this->sut->delete();
+    $this->sut = $this->drupalPlaceBlock('system_tabs_block', [
+      'id' => 'tabs_block',
+      'primary' => FALSE,
+      'secondary' => TRUE,
+    ]);
+
+    $this->drupalGet(Url::fromRoute('menu_test.local_task_test_tasks_settings'));
+
+    // Verify that local tasks in the first level doesn't appear.
+    $this->assertNoLocalTasks(0);
+
+    // Verify that local tasks in the second level appear.
+    $sub_tasks = [
+      ['menu_test.local_task_test_tasks_settings_sub1', []],
+      ['menu_test.local_task_test_tasks_settings_sub2', []],
+      ['menu_test.local_task_test_tasks_settings_sub3', []],
+      ['menu_test.local_task_test_tasks_settings_derived', ['placeholder' => 'derive1']],
+      ['menu_test.local_task_test_tasks_settings_derived', ['placeholder' => 'derive2']],
+    ];
+    $this->assertLocalTasks($sub_tasks, 1);
+  }
+
 }
diff --git a/core/modules/system/src/Tests/Menu/MenuRouterTest.php b/core/modules/system/src/Tests/Menu/MenuRouterTest.php
index ef78c7b..0f2fb5e 100644
--- a/core/modules/system/src/Tests/Menu/MenuRouterTest.php
+++ b/core/modules/system/src/Tests/Menu/MenuRouterTest.php
@@ -43,6 +43,7 @@ protected function setUp() {
     parent::setUp();
 
     $this->drupalPlaceBlock('system_menu_block:tools');
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/system/src/Tests/Menu/MenuTranslateTest.php b/core/modules/system/src/Tests/Menu/MenuTranslateTest.php
index e316c95..e02a304 100644
--- a/core/modules/system/src/Tests/Menu/MenuTranslateTest.php
+++ b/core/modules/system/src/Tests/Menu/MenuTranslateTest.php
@@ -23,7 +23,16 @@ class MenuTranslateTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('menu_test');
+  public static $modules = ['block', 'menu_test'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->drupalPlaceBlock('system_tabs_block');
+  }
 
   /**
    * Tests _menu_translate().
diff --git a/core/modules/system/src/Tests/System/DateTimeTest.php b/core/modules/system/src/Tests/System/DateTimeTest.php
index 9f0b0ac..9817fc6 100644
--- a/core/modules/system/src/Tests/System/DateTimeTest.php
+++ b/core/modules/system/src/Tests/System/DateTimeTest.php
@@ -22,13 +22,14 @@ class DateTimeTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('node', 'language');
+  public static $modules = ['block', 'node', 'language'];
 
   protected function setUp() {
     parent::setUp();
 
     // Create admin user and log in admin user.
     $this->drupalLogin ($this->drupalCreateUser(array('administer site configuration')));
+    $this->drupalPlaceBlock('system_local_actions_block');
   }
 
   /**
diff --git a/core/modules/system/src/Tests/System/ThemeTest.php b/core/modules/system/src/Tests/System/ThemeTest.php
index 84745b7..e97c75b 100644
--- a/core/modules/system/src/Tests/System/ThemeTest.php
+++ b/core/modules/system/src/Tests/System/ThemeTest.php
@@ -30,7 +30,7 @@ class ThemeTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('node', 'block', 'file');
+  public static $modules = ['node', 'block', 'file'];
 
   protected function setUp() {
     parent::setUp();
@@ -40,6 +40,7 @@ protected function setUp() {
     $this->adminUser = $this->drupalCreateUser(array('access administration pages', 'view the administration theme', 'administer themes', 'bypass node access', 'administer blocks'));
     $this->drupalLogin($this->adminUser);
     $this->node = $this->drupalCreateNode();
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index a0849b1..60ed2e1 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -5,6 +5,7 @@
  * Install, update and uninstall functions for the system module.
  */
 
+use Drupal\block\Entity\Block;
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\Environment;
 use Drupal\Component\Utility\SafeMarkup;
@@ -1085,3 +1086,7 @@ function system_schema() {
 
   return $schema;
 }
+
+/**
+ * @addtogroup updates-8.0.0-beta
+ * @{
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index a826e69..028190a 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -805,6 +805,14 @@ function system_preprocess_block(&$variables) {
       }
       break;
 
+    case 'system_local_actions_block':
+      $variables['content'] = $variables['content']['local_actions'];
+      break;
+
+    case 'system_tabs_block':
+      $variables['content'] = $variables['content']['tabs'];
+      break;
+
     case 'system_powered_by_block':
       $variables['attributes']['role'] = 'complementary';
       break;
diff --git a/core/modules/system/templates/page.html.twig b/core/modules/system/templates/page.html.twig
index c5a3711..b80310e 100644
--- a/core/modules/system/templates/page.html.twig
+++ b/core/modules/system/templates/page.html.twig
@@ -32,10 +32,6 @@
  * - title_suffix: Additional output populated by modules, intended to be
  *   displayed after the main title tag that appears in the template.
  * - messages: Status and error messages. Should be displayed prominently.
- * - tabs: Tabs linking to any sub-pages beneath the current page (e.g., the
- *   view and edit tabs when displaying a node).
- * - action_links: Actions local to the page, such as "Add menu" on the menu
- *   administration interface.
  * - node: Fully loaded node, if there is an automatically-loaded node
  *   associated with the page and the node ID is the second argument in the
  *   page's path (e.g. node/12345 and node/12345/revisions, but not
@@ -110,13 +106,6 @@
         <h1>{{ title }}</h1>
       {% endif %}
       {{ title_suffix }}
-
-      {{ tabs }}
-
-      {% if action_links %}
-        <nav class="action-links">{{ action_links }}</nav>
-      {% endif %}
-
       {{ page.content }}
     </div>{# /.layout-content #}
 
diff --git a/core/modules/taxonomy/src/Tests/TermTest.php b/core/modules/taxonomy/src/Tests/TermTest.php
index 09a0de2..1ef3e47 100644
--- a/core/modules/taxonomy/src/Tests/TermTest.php
+++ b/core/modules/taxonomy/src/Tests/TermTest.php
@@ -35,8 +35,22 @@ class TermTest extends TaxonomyTestBase {
    */
   protected $field;
 
+  /**
+   * Modules to enable.
+   *
+   * @var string[]
+   */
+  public static $modules = ['block'];
+
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
     parent::setUp();
+
+    $this->drupalPlaceBlock('system_local_actions_block');
+    $this->drupalPlaceBlock('system_tabs_block');
+
     $this->drupalLogin($this->drupalCreateUser(['administer taxonomy', 'bypass node access']));
     $this->vocabulary = $this->createVocabulary();
 
@@ -308,10 +322,7 @@ function testTermInterface() {
     // Submitting a term takes us to the add page; we need the List page.
     $this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
 
-    // Test edit link as accessed from Taxonomy administration pages.
-    // Because Simpletest creates its own database when running tests, we know
-    // the first edit link found on the listing page is to our term.
-    $this->clickLink(t('Edit'), 1);
+    $this->clickLink(t('Edit'));
 
     $this->assertRaw($edit['name[0][value]'], 'The randomly generated term name is present.');
     $this->assertText($edit['description[0][value]'], 'The randomly generated term description is present.');
diff --git a/core/modules/taxonomy/src/Tests/VocabularyUiTest.php b/core/modules/taxonomy/src/Tests/VocabularyUiTest.php
index 3a34c2e..69eba32 100644
--- a/core/modules/taxonomy/src/Tests/VocabularyUiTest.php
+++ b/core/modules/taxonomy/src/Tests/VocabularyUiTest.php
@@ -29,6 +29,7 @@ protected function setUp() {
     parent::setUp();
     $this->drupalLogin($this->drupalCreateUser(['administer taxonomy']));
     $this->vocabulary = $this->createVocabulary();
+    $this->drupalPlaceBlock('system_local_actions_block');
   }
 
   /**
diff --git a/core/modules/tour/src/Tests/TourTest.php b/core/modules/tour/src/Tests/TourTest.php
index 8ed261c..582e6d4 100644
--- a/core/modules/tour/src/Tests/TourTest.php
+++ b/core/modules/tour/src/Tests/TourTest.php
@@ -21,7 +21,7 @@ class TourTest extends TourTestBasic {
    *
    * @var array
    */
-  public static $modules = array('tour', 'locale', 'language', 'tour_test');
+  public static $modules = ['block', 'tour', 'locale', 'language', 'tour_test'];
 
   /**
    * The permissions required for a logged in user to test tour tips.
@@ -42,6 +42,18 @@ class TourTest extends TourTestBasic {
   );
 
   /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->drupalPlaceBlock('system_local_actions_block', [
+      'theme' => 'seven',
+      'region' => 'content'
+    ]);
+  }
+
+  /**
    * Test tour functionality.
    */
   public function testTourFunctionality() {
diff --git a/core/modules/tracker/src/Tests/TrackerTest.php b/core/modules/tracker/src/Tests/TrackerTest.php
index c57f1c9..5598ce9 100644
--- a/core/modules/tracker/src/Tests/TrackerTest.php
+++ b/core/modules/tracker/src/Tests/TrackerTest.php
@@ -31,7 +31,7 @@ class TrackerTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('comment', 'tracker', 'history', 'node_test');
+  public static $modules = ['block', 'comment', 'tracker', 'history', 'node_test'];
 
   /**
    * The main user for testing.
@@ -60,6 +60,8 @@ protected function setUp() {
       'access content',
       'access user profiles',
     ));
+    $this->drupalPlaceBlock('system_tabs_block', ['id' => 'page_tabs_block']);
+    $this->drupalPlaceBlock('system_local_actions_block', ['id' => 'page_actions_block']);
   }
 
   /**
@@ -83,10 +85,18 @@ function testTrackerAll() {
     $this->assertLink(t('My recent content'), 0, 'User tab shows up on the global tracker page.');
 
     // Assert cache contexts, specifically the pager and node access contexts.
-    $this->assertCacheContexts(['languages:language_interface', 'theme', 'url.query_args.pagers:0', 'user.node_grants:view', 'user.permissions']);
+    $this->assertCacheContexts(['languages:language_interface', 'route.name', 'theme', 'url.query_args.pagers:0', 'user.node_grants:view', 'user']);
     // Assert cache tags for the visible node and node list cache tag.
     $expected_tags = Cache::mergeTags($published->getCacheTags(), $published->getOwner()->getCacheTags());
-    $expected_tags = Cache::mergeTags($expected_tags, ['node_list', 'rendered']);
+    $additional_cache_tags = [
+      'node_list',
+      'rendered',
+      'block_view',
+      'config:block.block.page_actions_block',
+      'config:block.block.page_tabs_block',
+      'config:block_list',
+    ];
+    $expected_tags = Cache::mergeTags($expected_tags, $additional_cache_tags);
     $this->assertCacheTags($expected_tags);
 
     // Delete a node and ensure it no longer appears on the tracker.
@@ -149,16 +159,24 @@ function testTrackerUser() {
     $this->assertText($other_published_my_comment->label(), "Nodes that the user has commented on appear in the user's tracker listing.");
 
     // Assert cache contexts.
-    $this->assertCacheContexts(['languages:language_interface', 'theme', 'url.query_args.pagers:0', 'user', 'user.node_grants:view']);
+    $this->assertCacheContexts(['languages:language_interface', 'route.name', 'theme', 'url.query_args.pagers:0', 'user', 'user.node_grants:view']);
     // Assert cache tags for the visible nodes (including owners) and node list
     // cache tag.
     $expected_tags = Cache::mergeTags($my_published->getCacheTags(), $my_published->getOwner()->getCacheTags());
     $expected_tags = Cache::mergeTags($expected_tags, $other_published_my_comment->getCacheTags());
     $expected_tags = Cache::mergeTags($expected_tags, $other_published_my_comment->getOwner()->getCacheTags());
-    $expected_tags = Cache::mergeTags($expected_tags, ['node_list', 'rendered']);
+    $additional_cache_tags = [
+      'node_list',
+      'rendered',
+      'block_view',
+      'config:block.block.page_actions_block',
+      'config:block.block.page_tabs_block',
+      'config:block_list',
+    ];
+    $expected_tags = Cache::mergeTags($expected_tags, $additional_cache_tags);
 
     $this->assertCacheTags($expected_tags);
-    $this->assertCacheContexts(['languages:language_interface', 'theme', 'url.query_args.pagers:0', 'user', 'user.node_grants:view']);
+    $this->assertCacheContexts(['languages:language_interface', 'route.name', 'theme', 'url.query_args.pagers:0', 'user', 'user.node_grants:view']);
 
     $this->assertLink($my_published->label());
     $this->assertNoLink($unpublished->label());
diff --git a/core/modules/tracker/src/Tests/TrackerTest.php.orig b/core/modules/tracker/src/Tests/TrackerTest.php.orig
new file mode 100644
index 0000000..c57f1c9
--- /dev/null
+++ b/core/modules/tracker/src/Tests/TrackerTest.php.orig
@@ -0,0 +1,402 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\tracker\Tests\TrackerTest.
+ */
+
+namespace Drupal\tracker\Tests;
+
+use Drupal\comment\CommentInterface;
+use Drupal\comment\Tests\CommentTestTrait;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\node\Entity\Node;
+use Drupal\simpletest\WebTestBase;
+use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
+
+/**
+ * Create and delete nodes and check for their display in the tracker listings.
+ *
+ * @group tracker
+ */
+class TrackerTest extends WebTestBase {
+
+  use CommentTestTrait;
+  use AssertPageCacheContextsAndTagsTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('comment', 'tracker', 'history', 'node_test');
+
+  /**
+   * The main user for testing.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $user;
+
+  /**
+   * A second user that will 'create' comments and nodes.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $otherUser;
+
+  protected function setUp() {
+    parent::setUp();
+
+    $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
+
+    $permissions = array('access comments', 'create page content', 'post comments', 'skip comment approval');
+    $this->user = $this->drupalCreateUser($permissions);
+    $this->otherUser = $this->drupalCreateUser($permissions);
+    $this->addDefaultCommentField('node', 'page');
+    user_role_grant_permissions(AccountInterface::ANONYMOUS_ROLE, array(
+      'access content',
+      'access user profiles',
+    ));
+  }
+
+  /**
+   * Tests for the presence of nodes on the global tracker listing.
+   */
+  function testTrackerAll() {
+    $this->drupalLogin($this->user);
+
+    $unpublished = $this->drupalCreateNode(array(
+      'title' => $this->randomMachineName(8),
+      'status' => 0,
+    ));
+    $published = $this->drupalCreateNode(array(
+      'title' => $this->randomMachineName(8),
+      'status' => 1,
+    ));
+
+    $this->drupalGet('activity');
+    $this->assertNoText($unpublished->label(), 'Unpublished node does not show up in the tracker listing.');
+    $this->assertText($published->label(), 'Published node shows up in the tracker listing.');
+    $this->assertLink(t('My recent content'), 0, 'User tab shows up on the global tracker page.');
+
+    // Assert cache contexts, specifically the pager and node access contexts.
+    $this->assertCacheContexts(['languages:language_interface', 'theme', 'url.query_args.pagers:0', 'user.node_grants:view', 'user.permissions']);
+    // Assert cache tags for the visible node and node list cache tag.
+    $expected_tags = Cache::mergeTags($published->getCacheTags(), $published->getOwner()->getCacheTags());
+    $expected_tags = Cache::mergeTags($expected_tags, ['node_list', 'rendered']);
+    $this->assertCacheTags($expected_tags);
+
+    // Delete a node and ensure it no longer appears on the tracker.
+    $published->delete();
+    $this->drupalGet('activity');
+    $this->assertNoText($published->label(), 'Deleted node does not show up in the tracker listing.');
+
+    // Test proper display of time on activity page when comments are disabled.
+    // Disable comments.
+    FieldStorageConfig::loadByName('node', 'comment')->delete();
+    $node = $this->drupalCreateNode([
+      // This title is required to trigger the custom changed time set in the
+      // node_test module. This is needed in order to ensure a sufficiently
+      // large 'time ago' interval that isn't numbered in seconds.
+      'title' => 'testing_node_presave',
+      'status' => 1,
+    ]);
+
+    $this->drupalGet('activity');
+    $this->assertText($node->label(), 'Published node shows up in the tracker listing.');
+    $this->assertText(\Drupal::service('date.formatter')->formatTimeDiffSince($node->getChangedTime()), 'The changed time was displayed on the tracker listing.');
+  }
+
+  /**
+   * Tests for the presence of nodes on a user's tracker listing.
+   */
+  function testTrackerUser() {
+    $this->drupalLogin($this->user);
+
+    $unpublished = $this->drupalCreateNode(array(
+      'title' => $this->randomMachineName(8),
+      'uid' => $this->user->id(),
+      'status' => 0,
+    ));
+    $my_published = $this->drupalCreateNode(array(
+      'title' => $this->randomMachineName(8),
+      'uid' => $this->user->id(),
+      'status' => 1,
+    ));
+    $other_published_no_comment = $this->drupalCreateNode(array(
+      'title' => $this->randomMachineName(8),
+      'uid' => $this->otherUser->id(),
+      'status' => 1,
+    ));
+    $other_published_my_comment = $this->drupalCreateNode(array(
+      'title' => $this->randomMachineName(8),
+      'uid' => $this->otherUser->id(),
+      'status' => 1,
+    ));
+    $comment = array(
+      'subject[0][value]' => $this->randomMachineName(),
+      'comment_body[0][value]' => $this->randomMachineName(20),
+    );
+    $this->drupalPostForm('comment/reply/node/' . $other_published_my_comment->id() . '/comment', $comment, t('Save'));
+
+    $this->drupalGet('user/' . $this->user->id() . '/activity');
+    $this->assertNoText($unpublished->label(), "Unpublished nodes do not show up in the user's tracker listing.");
+    $this->assertText($my_published->label(), "Published nodes show up in the user's tracker listing.");
+    $this->assertNoText($other_published_no_comment->label(), "Another user's nodes do not show up in the user's tracker listing.");
+    $this->assertText($other_published_my_comment->label(), "Nodes that the user has commented on appear in the user's tracker listing.");
+
+    // Assert cache contexts.
+    $this->assertCacheContexts(['languages:language_interface', 'theme', 'url.query_args.pagers:0', 'user', 'user.node_grants:view']);
+    // Assert cache tags for the visible nodes (including owners) and node list
+    // cache tag.
+    $expected_tags = Cache::mergeTags($my_published->getCacheTags(), $my_published->getOwner()->getCacheTags());
+    $expected_tags = Cache::mergeTags($expected_tags, $other_published_my_comment->getCacheTags());
+    $expected_tags = Cache::mergeTags($expected_tags, $other_published_my_comment->getOwner()->getCacheTags());
+    $expected_tags = Cache::mergeTags($expected_tags, ['node_list', 'rendered']);
+
+    $this->assertCacheTags($expected_tags);
+    $this->assertCacheContexts(['languages:language_interface', 'theme', 'url.query_args.pagers:0', 'user', 'user.node_grants:view']);
+
+    $this->assertLink($my_published->label());
+    $this->assertNoLink($unpublished->label());
+    // Verify that title and tab title have been set correctly.
+    $this->assertText('Activity', 'The user activity tab has the name "Activity".');
+    $this->assertTitle(t('@name | @site', array('@name' => $this->user->getUsername(), '@site' => $this->config('system.site')->get('name'))), 'The user tracker page has the correct page title.');
+
+    // Verify that unpublished comments are removed from the tracker.
+    $admin_user = $this->drupalCreateUser(array('post comments', 'administer comments', 'access user profiles'));
+    $this->drupalLogin($admin_user);
+    $this->drupalPostForm('comment/1/edit', array('status' => CommentInterface::NOT_PUBLISHED), t('Save'));
+    $this->drupalGet('user/' . $this->user->id() . '/activity');
+    $this->assertNoText($other_published_my_comment->label(), 'Unpublished comments are not counted on the tracker listing.');
+  }
+
+  /**
+   * Tests the metadata for the "new"/"updated" indicators.
+   */
+  function testTrackerHistoryMetadata() {
+    $this->drupalLogin($this->user);
+
+    // Create a page node.
+    $edit = array(
+      'title' => $this->randomMachineName(8),
+    );
+    $node = $this->drupalCreateNode($edit);
+
+    // Verify that the history metadata is present.
+    $this->drupalGet('activity');
+    $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->getChangedTime());
+    $this->drupalGet('activity/' . $this->user->id());
+    $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->getChangedTime());
+    $this->drupalGet('user/' . $this->user->id() . '/activity');
+    $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->getChangedTime());
+
+    // Add a comment to the page, make sure it is created after the node by
+    // sleeping for one second, to ensure the last comment timestamp is
+    // different from before.
+    $comment = array(
+      'subject[0][value]' => $this->randomMachineName(),
+      'comment_body[0][value]' => $this->randomMachineName(20),
+    );
+    sleep(1);
+    $this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $comment, t('Save'));
+    // Reload the node so that comment.module's hook_node_load()
+    // implementation can set $node->last_comment_timestamp for the freshly
+    // posted comment.
+    $node = Node::load($node->id());
+
+    // Verify that the history metadata is updated.
+    $this->drupalGet('activity');
+    $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->get('comment')->last_comment_timestamp);
+    $this->drupalGet('activity/' . $this->user->id());
+    $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->get('comment')->last_comment_timestamp);
+    $this->drupalGet('user/' . $this->user->id() . '/activity');
+    $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->get('comment')->last_comment_timestamp);
+
+    // Log out, now verify that the metadata is still there, but the library is
+    // not.
+    $this->drupalLogout();
+    $this->drupalGet('activity');
+    $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->get('comment')->last_comment_timestamp, FALSE);
+    $this->drupalGet('user/' . $this->user->id() . '/activity');
+    $this->assertHistoryMetadata($node->id(), $node->getChangedTime(), $node->get('comment')->last_comment_timestamp, FALSE);
+  }
+
+  /**
+   * Tests for ordering on a users tracker listing when comments are posted.
+   */
+  function testTrackerOrderingNewComments() {
+    $this->drupalLogin($this->user);
+
+    $node_one = $this->drupalCreateNode(array(
+      'title' => $this->randomMachineName(8),
+    ));
+
+    $node_two = $this->drupalCreateNode(array(
+      'title' => $this->randomMachineName(8),
+    ));
+
+    // Now get otherUser to track these pieces of content.
+    $this->drupalLogin($this->otherUser);
+
+    // Add a comment to the first page.
+    $comment = array(
+      'subject[0][value]' => $this->randomMachineName(),
+      'comment_body[0][value]' => $this->randomMachineName(20),
+    );
+    $this->drupalPostForm('comment/reply/node/' . $node_one->id() . '/comment', $comment, t('Save'));
+
+    // If the comment is posted in the same second as the last one then Drupal
+    // can't tell the difference, so we wait one second here.
+    sleep(1);
+
+    // Add a comment to the second page.
+    $comment = array(
+      'subject[0][value]' => $this->randomMachineName(),
+      'comment_body[0][value]' => $this->randomMachineName(20),
+    );
+    $this->drupalPostForm('comment/reply/node/' . $node_two->id() . '/comment', $comment, t('Save'));
+
+    // We should at this point have in our tracker for otherUser:
+    // 1. node_two
+    // 2. node_one
+    // Because that's the reverse order of the posted comments.
+
+    // Now we're going to post a comment to node_one which should jump it to the
+    // top of the list.
+
+    $this->drupalLogin($this->user);
+    // If the comment is posted in the same second as the last one then Drupal
+    // can't tell the difference, so we wait one second here.
+    sleep(1);
+
+    // Add a comment to the second page.
+    $comment = array(
+      'subject[0][value]' => $this->randomMachineName(),
+      'comment_body[0][value]' => $this->randomMachineName(20),
+    );
+    $this->drupalPostForm('comment/reply/node/' . $node_one->id() . '/comment', $comment, t('Save'));
+
+    // Switch back to the otherUser and assert that the order has swapped.
+    $this->drupalLogin($this->otherUser);
+    $this->drupalGet('user/' . $this->otherUser->id() . '/activity');
+    // This is a cheeky way of asserting that the nodes are in the right order
+    // on the tracker page.
+    // It's almost certainly too brittle.
+    $pattern = '/' . preg_quote($node_one->getTitle()) . '.+' . preg_quote($node_two->getTitle()) . '/s';
+    $this->verbose($pattern);
+    $this->assertPattern($pattern, 'Most recently commented on node appears at the top of tracker');
+  }
+
+  /**
+   * Tests that existing nodes are indexed by cron.
+   */
+  function testTrackerCronIndexing() {
+    $this->drupalLogin($this->user);
+
+    // Create 3 nodes.
+    $edits = array();
+    $nodes = array();
+    for ($i = 1; $i <= 3; $i++) {
+      $edits[$i] = array(
+        'title' => $this->randomMachineName(),
+      );
+      $nodes[$i] = $this->drupalCreateNode($edits[$i]);
+    }
+
+    // Add a comment to the last node as other user.
+    $this->drupalLogin($this->otherUser);
+    $comment = array(
+      'subject[0][value]' => $this->randomMachineName(),
+      'comment_body[0][value]' => $this->randomMachineName(20),
+    );
+    $this->drupalPostForm('comment/reply/node/' . $nodes[3]->id() . '/comment', $comment, t('Save'));
+
+    // Start indexing backwards from node 3.
+    \Drupal::state()->set('tracker.index_nid', 3);
+
+    // Clear the current tracker tables and rebuild them.
+    db_delete('tracker_node')
+      ->execute();
+    db_delete('tracker_user')
+      ->execute();
+    tracker_cron();
+
+    $this->drupalLogin($this->user);
+
+    // Fetch the user's tracker.
+    $this->drupalGet('activity/' . $this->user->id());
+
+    // Assert that all node titles are displayed.
+    foreach ($nodes as $i => $node) {
+      $this->assertText($node->label(), format_string('Node @i is displayed on the tracker listing pages.', array('@i' => $i)));
+    }
+
+    // Fetch the site-wide tracker.
+    $this->drupalGet('activity');
+
+    // Assert that all node titles are displayed.
+    foreach ($nodes as $i => $node) {
+      $this->assertText($node->label(), format_string('Node @i is displayed on the tracker listing pages.', array('@i' => $i)));
+    }
+  }
+
+  /**
+   * Tests that publish/unpublish works at admin/content/node.
+   */
+  function testTrackerAdminUnpublish() {
+    \Drupal::service('module_installer')->install(array('views'));
+    \Drupal::service('router.builder')->rebuild();
+    $admin_user = $this->drupalCreateUser(array('access content overview', 'administer nodes', 'bypass node access'));
+    $this->drupalLogin($admin_user);
+
+    $node = $this->drupalCreateNode(array(
+      'title' => $this->randomMachineName(),
+    ));
+
+    // Assert that the node is displayed.
+    $this->drupalGet('activity');
+    $this->assertText($node->label(), 'A node is displayed on the tracker listing pages.');
+
+    // Unpublish the node and ensure that it's no longer displayed.
+    $edit = array(
+      'action' => 'node_unpublish_action',
+      'node_bulk_form[0]' => $node->id(),
+    );
+    $this->drupalPostForm('admin/content', $edit, t('Apply'));
+
+    $this->drupalGet('activity');
+    $this->assertText(t('No content available.'), 'A node is displayed on the tracker listing pages.');
+  }
+
+  /**
+   * Passes if the appropriate history metadata exists.
+   *
+   * Verify the data-history-node-id, data-history-node-timestamp and
+   * data-history-node-last-comment-timestamp attributes, which are used by the
+   * drupal.tracker-history library to add the appropriate "new" and "updated"
+   * indicators, as well as the "x new" replies link to the tracker.
+   * We do this in JavaScript to prevent breaking the render cache.
+   *
+   * @param $node_id
+   *   A node ID, that must exist as a data-history-node-id attribute
+   * @param $node_timestamp
+   *   A node timestamp, that must exist as a data-history-node-timestamp
+   *   attribute.
+   * @param $node_last_comment_timestamp
+   *   A node's last comment timestamp, that must exist as a
+   *   data-history-node-last-comment-timestamp attribute.
+   * @param bool $library_is_present
+   *   Whether the drupal.tracker-history library should be present or not.
+   */
+  function assertHistoryMetadata($node_id, $node_timestamp, $node_last_comment_timestamp, $library_is_present = TRUE) {
+    $settings = $this->getDrupalSettings();
+    $this->assertIdentical($library_is_present, isset($settings['ajaxPageState']) && in_array('tracker/history', explode(',', $settings['ajaxPageState']['libraries'])), 'drupal.tracker-history library is present.');
+    $this->assertIdentical(1, count($this->xpath('//table/tbody/tr/td[@data-history-node-id="' . $node_id . '" and @data-history-node-timestamp="' . $node_timestamp . '"]')), 'Tracker table cell contains the data-history-node-id and data-history-node-timestamp attributes for the node.');
+    $this->assertIdentical(1, count($this->xpath('//table/tbody/tr/td[@data-history-node-last-comment-timestamp="' . $node_last_comment_timestamp . '"]')), 'Tracker table cell contains the data-history-node-last-comment-timestamp attribute for the node.');
+  }
+
+}
diff --git a/core/modules/tracker/src/Tests/TrackerTest.php.rej b/core/modules/tracker/src/Tests/TrackerTest.php.rej
new file mode 100644
index 0000000..c0a2b4b
--- /dev/null
+++ b/core/modules/tracker/src/Tests/TrackerTest.php.rej
@@ -0,0 +1,47 @@
+--- core/modules/tracker/src/Tests/TrackerTest.php
++++ core/modules/tracker/src/Tests/TrackerTest.php
+@@ -54,6 +54,8 @@
+     $this->user = $this->drupalCreateUser($permissions);
+     $this->otherUser = $this->drupalCreateUser($permissions);
+     $this->addDefaultCommentField('node', 'page');
++    $this->drupalPlaceBlock('system_tabs_block', ['id' => 'page_tabs_block']);
++    $this->drupalPlaceBlock('system_local_actions_block', ['id' => 'page_actions_block']);
+   }
+ 
+   /**
+@@ -77,9 +79,17 @@
+     $this->assertLink(t('My recent content'), 0, 'User tab shows up on the global tracker page.');
+ 
+     // Assert cache contexts, specifically the pager and node access contexts.
+-    $this->assertCacheContexts(['languages:language_interface', 'theme', 'url.query_args.pagers:0', 'user.node_grants:view', 'user.permissions']);
++    $this->assertCacheContexts(['languages:language_interface', 'route.name', 'theme', 'url.query_args.pagers:0', 'user', 'user.node_grants:view']);
+     // Assert cache tags for the visible node and node list cache tag.
+-    $this->assertCacheTags(Cache::mergeTags($published->getCacheTags(), $published->getOwner()->getCacheTags(), ['node_list', 'rendered']));
++    $additional_cache_tags = [
++      'node_list',
++      'rendered',
++      'block_view',
++      'config:block.block.page_actions_block',
++      'config:block.block.page_tabs_block',
++      'config:block_list',
++    ];
++    $this->assertCacheTags(Cache::mergeTags($published->getCacheTags(), $published->getOwner()->getCacheTags(), $additional_cache_tags));
+ 
+     // Delete a node and ensure it no longer appears on the tracker.
+     $published->delete();
+@@ -149,7 +159,14 @@
+       $my_published->getOwner()->getCacheTags(),
+       $other_published_my_comment->getCacheTags(),
+       $other_published_my_comment->getOwner()->getCacheTags(),
+-      ['node_list', 'rendered']
++      [
++        'node_list',
++        'rendered',
++        'block_view',
++        'config:block.block.page_actions_block',
++        'config:block.block.page_tabs_block',
++        'config:block_list',
++      ]
+     );
+     $this->assertCacheTags($tags);
+ 
diff --git a/core/modules/update/src/Tests/UpdateCoreTest.php b/core/modules/update/src/Tests/UpdateCoreTest.php
index aef7c5e..3222340 100644
--- a/core/modules/update/src/Tests/UpdateCoreTest.php
+++ b/core/modules/update/src/Tests/UpdateCoreTest.php
@@ -22,12 +22,13 @@ class UpdateCoreTest extends UpdateTestBase {
    *
    * @var array
    */
-  public static $modules = array('update_test', 'update', 'language');
+  public static $modules = ['update_test', 'update', 'language', 'block'];
 
   protected function setUp() {
     parent::setUp();
     $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer modules', 'administer themes'));
     $this->drupalLogin($admin_user);
+    $this->drupalPlaceBlock('system_local_actions_block');
   }
 
   /**
diff --git a/core/modules/user/src/Tests/UserRoleAdminTest.php b/core/modules/user/src/Tests/UserRoleAdminTest.php
index 455d6f7..7d0d350 100644
--- a/core/modules/user/src/Tests/UserRoleAdminTest.php
+++ b/core/modules/user/src/Tests/UserRoleAdminTest.php
@@ -25,9 +25,20 @@ class UserRoleAdminTest extends WebTestBase {
    */
   protected $adminUser;
 
+  /**
+   * Modules to enable.
+   *
+   * @var string[]
+   */
+  public static $modules = ['block'];
+
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
     parent::setUp();
     $this->adminUser = $this->drupalCreateUser(array('administer permissions', 'administer users'));
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
diff --git a/core/modules/views/src/Tests/Plugin/DisplayPageWebTest.php b/core/modules/views/src/Tests/Plugin/DisplayPageWebTest.php
index 31bfd3c..dddd4ce 100644
--- a/core/modules/views/src/Tests/Plugin/DisplayPageWebTest.php
+++ b/core/modules/views/src/Tests/Plugin/DisplayPageWebTest.php
@@ -33,10 +33,14 @@ class DisplayPageWebTest extends PluginTestBase {
    */
   public static $modules = ['menu_ui', 'block'];
 
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
     parent::setUp();
 
     $this->enableViewsTestModule();
+    $this->drupalPlaceBlock('system_tabs_block');
   }
 
   /**
@@ -53,7 +57,7 @@ public function testArguments() {
 
     $this->drupalGet('test_route_with_argument/1');
     $this->assertResponse(200);
-    $this->assertCacheContexts(['languages:language_interface', 'theme', 'url']);
+    $this->assertCacheContexts(['languages:language_interface', 'route.name', 'theme', 'url']);
     $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/src/Tests/Wizard/WizardTestBase.php b/core/modules/views/src/Tests/Wizard/WizardTestBase.php
index f610a5f..85f79e8 100644
--- a/core/modules/views/src/Tests/Wizard/WizardTestBase.php
+++ b/core/modules/views/src/Tests/Wizard/WizardTestBase.php
@@ -27,6 +27,7 @@ protected function setUp() {
     // Create and log in a user with administer views permission.
     $views_admin = $this->drupalCreateUser(array('administer views', 'administer blocks', 'bypass node access', 'access user profiles', 'view all revisions'));
     $this->drupalLogin($views_admin);
+    $this->drupalPlaceBlock('system_local_actions_block');
   }
 
 }
diff --git a/core/modules/views_ui/src/Tests/SettingsTest.php b/core/modules/views_ui/src/Tests/SettingsTest.php
index 262fc7a..62acd9d 100644
--- a/core/modules/views_ui/src/Tests/SettingsTest.php
+++ b/core/modules/views_ui/src/Tests/SettingsTest.php
@@ -22,6 +22,14 @@ class SettingsTest extends UITestBase {
   protected $adminUser;
 
   /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->drupalPlaceBlock('system_tabs_block');
+  }
+
+  /**
    * Tests the settings for the edit ui.
    */
   function testEditUI() {
diff --git a/core/profiles/minimal/config/install/block.block.stark_local_actions.yml b/core/profiles/minimal/config/install/block.block.stark_local_actions.yml
new file mode 100644
index 0000000..aed4cad
--- /dev/null
+++ b/core/profiles/minimal/config/install/block.block.stark_local_actions.yml
@@ -0,0 +1,18 @@
+id: stark_local_actions
+theme: stark
+weight: -10
+status: true
+langcode: en
+region: content
+plugin: system_local_actions_block
+settings:
+  id: system_local_actions_block
+  label: Primary admin actions
+  provider: system
+  label_display: '0'
+dependencies:
+  module:
+    - system
+  theme:
+    - stark
+visibility: {  }
diff --git a/core/profiles/minimal/config/install/block.block.stark_tabs.yml b/core/profiles/minimal/config/install/block.block.stark_tabs.yml
new file mode 100644
index 0000000..1c2cff3c
--- /dev/null
+++ b/core/profiles/minimal/config/install/block.block.stark_tabs.yml
@@ -0,0 +1,18 @@
+id: bartik_tabs
+theme: stark
+weight: -20
+status: true
+langcode: en
+region: content
+plugin: system_tabs_block
+settings:
+  id: system_tabs_block
+  label: Tabs
+  provider: system
+  label_display: '0'
+dependencies:
+  module:
+    - system
+  theme:
+    - stark
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.bartik_help.yml b/core/profiles/standard/config/install/block.block.bartik_help.yml
index 88c4f1a..4142788 100644
--- a/core/profiles/standard/config/install/block.block.bartik_help.yml
+++ b/core/profiles/standard/config/install/block.block.bartik_help.yml
@@ -1,9 +1,9 @@
 id: bartik_help
 theme: bartik
-weight: 0
+weight: -10
 status: true
 langcode: en
-region: help
+region: content
 plugin: help_block
 settings:
   id: help_block
diff --git a/core/profiles/standard/config/install/block.block.bartik_local_actions.yml b/core/profiles/standard/config/install/block.block.bartik_local_actions.yml
new file mode 100644
index 0000000..42aef3f
--- /dev/null
+++ b/core/profiles/standard/config/install/block.block.bartik_local_actions.yml
@@ -0,0 +1,18 @@
+id: bartik_local_actions
+theme: bartik
+weight: -20
+status: true
+langcode: en
+region: content
+plugin: system_local_actions_block
+settings:
+  id: system_local_actions_block
+  label: Primary admin actions
+  provider: system
+  label_display: '0'
+dependencies:
+  module:
+    - system
+  theme:
+    - bartik
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.bartik_tabs.yml b/core/profiles/standard/config/install/block.block.bartik_tabs.yml
new file mode 100644
index 0000000..9913335
--- /dev/null
+++ b/core/profiles/standard/config/install/block.block.bartik_tabs.yml
@@ -0,0 +1,18 @@
+id: bartik_tabs
+theme: bartik
+weight: -30
+status: true
+langcode: en
+region: content
+plugin: system_tabs_block
+settings:
+  id: system_tabs_block
+  label: Tabs
+  provider: system
+  label_display: '0'
+dependencies:
+  module:
+    - system
+  theme:
+    - bartik
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.seven_local_actions.yml b/core/profiles/standard/config/install/block.block.seven_local_actions.yml
new file mode 100644
index 0000000..9577c20
--- /dev/null
+++ b/core/profiles/standard/config/install/block.block.seven_local_actions.yml
@@ -0,0 +1,18 @@
+id: seven_local_actions
+theme: seven
+weight: -10
+status: true
+langcode: en
+region: content
+plugin: system_local_actions_block
+settings:
+  id: system_local_actions_block
+  label: Primary admin actions
+  provider: system
+  label_display: '0'
+dependencies:
+  module:
+    - system
+  theme:
+    - seven
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.seven_page_primary_tabs.yml b/core/profiles/standard/config/install/block.block.seven_page_primary_tabs.yml
new file mode 100644
index 0000000..078f15b
--- /dev/null
+++ b/core/profiles/standard/config/install/block.block.seven_page_primary_tabs.yml
@@ -0,0 +1,20 @@
+id: seven_primary_tabs
+theme: seven
+weight: 0
+status: true
+langcode: en
+region: header
+plugin: system_tabs_block
+settings:
+  id: system_tabs_block
+  label: Primary tabs
+  provider: system
+  label_display: '0'
+  primary: true
+  secondary: false
+dependencies:
+  module:
+    - system
+  theme:
+    - seven
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.seven_page_seconadry_tabs.yml b/core/profiles/standard/config/install/block.block.seven_page_seconadry_tabs.yml
new file mode 100644
index 0000000..a7e2350
--- /dev/null
+++ b/core/profiles/standard/config/install/block.block.seven_page_seconadry_tabs.yml
@@ -0,0 +1,20 @@
+id: seven_secondary_tabs
+theme: seven
+weight: 0
+status: true
+langcode: en
+region: pre_content
+plugin: system_tabs_block
+settings:
+  id: system_tabs_block
+  label: Secondary tabs
+  provider: system
+  label_display: '0'
+  primary: false
+  secondary: true
+dependencies:
+  module:
+    - system
+  theme:
+    - seven
+visibility: {  }
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php
index 85731c7..24a09f2 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php
@@ -10,6 +10,8 @@
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\Component\Plugin\Factory\FactoryInterface;
 use Drupal\Core\Access\AccessManagerInterface;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Access\AccessResultForbidden;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\Language;
@@ -109,10 +111,11 @@ protected function setUp() {
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
     $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
 
+    $access_result = new AccessResultForbidden();
     $this->accessManager = $this->getMock('Drupal\Core\Access\AccessManagerInterface');
     $this->accessManager->expects($this->any())
       ->method('checkNamedRoute')
-      ->will($this->returnValue(FALSE));
+      ->willReturn($access_result);
     $this->account = $this->getMock('Drupal\Core\Session\AccountInterface');
     $this->discovery = $this->getMock('Drupal\Component\Plugin\Discovery\DiscoveryInterface');
     $this->factory = $this->getMock('Drupal\Component\Plugin\Factory\FactoryInterface');
@@ -202,7 +205,7 @@ public function getActionsForRouteProvider() {
             'url' => Url::fromRoute('test_route_2'),
             'localized_options' => '',
           ),
-          '#access' => FALSE,
+          '#access' => AccessResult::forbidden(),
           '#weight' => 0,
         ),
       ),
@@ -236,7 +239,7 @@ public function getActionsForRouteProvider() {
             'url' => Url::fromRoute('test_route_2'),
             'localized_options' => '',
           ),
-          '#access' => FALSE,
+          '#access' => AccessResult::forbidden(),
           '#weight' => 0,
         ),
       ),
@@ -271,7 +274,7 @@ public function getActionsForRouteProvider() {
             'url' => Url::fromRoute('test_route_2'),
             'localized_options' => '',
           ),
-          '#access' => FALSE,
+          '#access' => AccessResult::forbidden(),
           '#weight' => 1,
         ),
         'plugin_id_2' => array(
@@ -281,7 +284,7 @@ public function getActionsForRouteProvider() {
             'url' => Url::fromRoute('test_route_3'),
             'localized_options' => '',
           ),
-          '#access' => FALSE,
+          '#access' => AccessResult::forbidden(),
           '#weight' => 0,
         ),
       ),
@@ -318,7 +321,7 @@ public function getActionsForRouteProvider() {
             'url' => Url::fromRoute('test_route_2', ['test1']),
             'localized_options' => '',
           ),
-          '#access' => FALSE,
+          '#access' => AccessResult::forbidden(),
           '#weight' => 1,
         ),
         'plugin_id_2' => array(
@@ -328,7 +331,7 @@ public function getActionsForRouteProvider() {
             'url' => Url::fromRoute('test_route_2', ['test2']),
             'localized_options' => '',
           ),
-          '#access' => FALSE,
+          '#access' => AccessResult::forbidden(),
           '#weight' => 0,
         ),
       ),
diff --git a/core/themes/bartik/bartik.info.yml b/core/themes/bartik/bartik.info.yml
index fb8f2fb..68cc5fe 100644
--- a/core/themes/bartik/bartik.info.yml
+++ b/core/themes/bartik/bartik.info.yml
@@ -18,7 +18,6 @@ regions:
   header: Header
   primary_menu: 'Primary menu'
   secondary_menu: 'Secondary menu'
-  help: Help
   page_top: 'Page top'
   page_bottom: 'Page bottom'
   highlighted: Highlighted
diff --git a/core/themes/bartik/bartik.libraries.yml b/core/themes/bartik/bartik.libraries.yml
index 009a67f..41cb460 100644
--- a/core/themes/bartik/bartik.libraries.yml
+++ b/core/themes/bartik/bartik.libraries.yml
@@ -19,7 +19,7 @@ global-styling:
       css/components/form.css: {}
       css/components/forum.css: {}
       css/components/header.css: {}
-      css/components/region-help.css: {}
+      css/components/help.css: {}
       css/components/item-list.css: {}
       css/components/list-group.css: {}
       css/components/node-preview.css: {}
diff --git a/core/themes/bartik/css/components/help.css b/core/themes/bartik/css/components/help.css
new file mode 100644
index 0000000..c606992
--- /dev/null
+++ b/core/themes/bartik/css/components/help.css
@@ -0,0 +1,10 @@
+/**
+ * @file
+ * Styles for the help block.
+ */
+
+.block-help {
+  border: 1px solid #d3d7d9;
+  padding: 0 1.5em;
+  margin-bottom: 30px;
+}
diff --git a/core/themes/bartik/css/components/region-help.css b/core/themes/bartik/css/components/region-help.css
deleted file mode 100644
index 3c16b0d..0000000
--- a/core/themes/bartik/css/components/region-help.css
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * @file
- * Styles for the help region.
- */
-
-.region-help {
-  border: 1px solid #d3d7d9;
-  padding: 0 1.5em;
-  margin-bottom: 30px;
-}
diff --git a/core/themes/bartik/templates/page.html.twig b/core/themes/bartik/templates/page.html.twig
index d1ba506..f3da885 100644
--- a/core/themes/bartik/templates/page.html.twig
+++ b/core/themes/bartik/templates/page.html.twig
@@ -38,10 +38,6 @@
  * - title: The page title, for use in the actual content.
  * - title_suffix: Additional output populated by modules, intended to be
  *   displayed after the main title tag that appears in the template.
- * - tabs: Tabs linking to any sub-pages beneath the current page (e.g., the
- *   view and edit tabs when displaying a node).
- * - action_links: Actions local to the page, such as "Add menu" on the menu
- *   administration interface.
  * - node: Fully loaded node, if there is an automatically-loaded node
  *   associated with the page and the node ID is the second argument in the
  *   page's path (e.g. node/12345 and node/12345/revisions, but not
@@ -53,7 +49,6 @@
  * - page.primary_menu: Items for the primary menu region.
  * - page.secondary_menu: Items for the secondary menu region.
  * - page.featured_top: Items for the featured top region.
- * - page.help: Dynamic help text, mostly for admin pages.
  * - page.content: The main content of the current page.
  * - page.sidebar_first: Items for the first sidebar.
  * - page.sidebar_second: Items for the second sidebar.
@@ -136,15 +131,6 @@
               </h1>
             {% endif %}
             {{ title_suffix }}
-            {% if tabs %}
-              <nav class="tabs" role="navigation" aria-label="{{ 'Tabs'|t }}">
-                {{ tabs }}
-              </nav>
-            {% endif %}
-            {{ page.help }}
-            {% if action_links %}
-              <ul class="action-links">{{ action_links }}</ul>
-            {% endif %}
             {{ page.content }}
           </section>
         </main>
diff --git a/core/themes/classy/templates/block/block--system-local-actions-block.html.twig b/core/themes/classy/templates/block/block--system-local-actions-block.html.twig
new file mode 100644
index 0000000..2a0f5c4
--- /dev/null
+++ b/core/themes/classy/templates/block/block--system-local-actions-block.html.twig
@@ -0,0 +1,12 @@
+{% extends "@block/block.html.twig" %}
+{#
+/**
+ * @file
+ * Theme override for local actions (primary admin actions.)
+ */
+#}
+{% block content %}
+  {% if content %}
+    <nav class="action-links">{{ content }}</nav>
+  {% endif %}
+{% endblock %}
diff --git a/core/themes/classy/templates/block/block--system-tabs-block.html.twig b/core/themes/classy/templates/block/block--system-tabs-block.html.twig
new file mode 100644
index 0000000..a191c60
--- /dev/null
+++ b/core/themes/classy/templates/block/block--system-tabs-block.html.twig
@@ -0,0 +1,14 @@
+{% extends "@block/block.html.twig" %}
+{#
+/**
+ * @file
+ * Theme override for tabs.
+ */
+#}
+{% block content %}
+  {% if content %}
+    <nav class="tabs" role="navigation" aria-label="{{ 'Tabs'|t }}">
+      {{ content }}
+    </nav>
+  {% endif %}
+{% endblock %}
diff --git a/core/themes/classy/templates/layout/page.html.twig b/core/themes/classy/templates/layout/page.html.twig
index 57533c8..8c81950 100644
--- a/core/themes/classy/templates/layout/page.html.twig
+++ b/core/themes/classy/templates/layout/page.html.twig
@@ -31,10 +31,6 @@
  * - title: The page title, for use in the actual content.
  * - title_suffix: Additional output populated by modules, intended to be
  *   displayed after the main title tag that appears in the template.
- * - tabs: Tabs linking to any sub-pages beneath the current page (e.g., the
- *   view and edit tabs when displaying a node).
- * - action_links: Actions local to the page, such as "Add menu" on the menu
- *   administration interface.
  * - node: Fully loaded node, if there is an automatically-loaded node
  *   associated with the page and the node ID is the second argument in the
  *   page's path (e.g. node/12345 and node/12345/revisions, but not
@@ -109,13 +105,6 @@
         <h1>{{ title }}</h1>
       {% endif %}
       {{ title_suffix }}
-
-      {{ tabs }}
-
-      {% if action_links %}
-        <nav class="action-links">{{ action_links }}</nav>
-      {% endif %}
-
       {{ page.content }}
     </div>{# /.layout-content #}
 
diff --git a/core/themes/seven/seven.info.yml b/core/themes/seven/seven.info.yml
index 99a3ec1..1427896 100644
--- a/core/themes/seven/seven.info.yml
+++ b/core/themes/seven/seven.info.yml
@@ -14,12 +14,14 @@ stylesheets-remove:
 quickedit_stylesheets:
   - css/components/quickedit.css
 regions:
-  content: Content
+  header: 'Header'
+  pre_content: 'Pre-content'
+  breadcrumb: Breadcrumb
   highlighted: Highlighted
   help: Help
+  content: Content
   page_top: 'Page top'
   page_bottom: 'Page bottom'
   sidebar_first: 'First sidebar'
-  breadcrumb: Breadcrumb
 regions_hidden:
   - sidebar_first
diff --git a/core/themes/seven/seven.theme b/core/themes/seven/seven.theme
index b508d94..fc677de 100644
--- a/core/themes/seven/seven.theme
+++ b/core/themes/seven/seven.theme
@@ -25,18 +25,6 @@ function seven_preprocess_html(&$variables) {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for page templates.
- */
-function seven_preprocess_page(&$variables) {
-  $variables['primary_local_tasks'] = $variables['tabs'];
-  unset($variables['primary_local_tasks']['#secondary']);
-  $variables['secondary_local_tasks'] = array(
-    '#theme' => 'menu_local_tasks',
-    '#secondary' => isset($variables['tabs']['#secondary']) ? $variables['tabs']['#secondary'] : '',
-  );
-}
-
-/**
  * Implements hook_pre_render_HOOK() for menu-local-tasks templates.
  *
  * Use preprocess hook to set #attached to child elements
diff --git a/core/themes/seven/templates/page.html.twig b/core/themes/seven/templates/page.html.twig
index 1c36be5..ac26a51 100644
--- a/core/themes/seven/templates/page.html.twig
+++ b/core/themes/seven/templates/page.html.twig
@@ -32,24 +32,18 @@
  * - title: The page title, for use in the actual content.
  * - title_suffix: Additional output populated by modules, intended to be
  *   displayed after the main title tag that appears in the template.
- * - tabs: Tabs linking to any sub-pages beneath the current page (e.g., the
- *   view and edit tabs when displaying a node).
- * - action_links: Actions local to the page, such as "Add menu" on the menu
- *   administration interface.
  * - node: Fully loaded node, if there is an automatically-loaded node
  *   associated with the page and the node ID is the second argument in the
  *   page's path (e.g. node/12345 and node/12345/revisions, but not
  *   comment/reply/12345).
  *
  * Regions:
- * - page.page_top: Items for the header region.
+ * - page.header: Items for the header region.
+ * - page.pre_content: Items for the pre-content region.
+ * - page.breadcrumb: Items for the breadcrumb region.
  * - page.highlighted: Items for the highlighted region.
  * - page.help: Dynamic help text, mostly for admin pages.
  * - page.content: The main content of the current page.
- * - page.sidebar_first: Items for the first sidebar.
- * - page.sidebar_second: Items for the second sidebar.
- * - page.page_bottom: Items for the footer region.
- * - page.breadcrumb: Items for the breadcrumb region.
  *
  * @see template_preprocess_page()
  * @see seven_preprocess_page()
@@ -63,19 +57,13 @@
         <h1 class="page-title">{{ title }}</h1>
       {% endif %}
       {{ title_suffix }}
-      {% if primary_local_tasks %}
-        {{ primary_local_tasks }}
-      {% endif %}
+      {{ page.header }}
     </div>
   </header>
 
   <div class="layout-container">
-    {% if secondary_local_tasks %}
-      <div class="tabs-secondary clearfix" role="navigation">{{ secondary_local_tasks }}</div>
-    {% endif %}
-
+    {{ page.pre_content }}
     {{ page.breadcrumb }}
-
     <main class="page-content clearfix" role="main">
       <div class="visually-hidden"><a id="main-content" tabindex="-1"></a></div>
       {{ page.highlighted }}
@@ -84,11 +72,6 @@
           {{ page.help }}
         </div>
       {% endif %}
-      {% if action_links %}
-        <ul class="action-links">
-          {{ action_links }}
-        </ul>
-      {% endif %}
       {{ page.content }}
     </main>
 
