diff --git a/core/modules/contextual/js/contextual.es6.js b/core/modules/contextual/js/contextual.es6.js
index 558ea10..87a7ad7 100644
--- a/core/modules/contextual/js/contextual.es6.js
+++ b/core/modules/contextual/js/contextual.es6.js
@@ -160,6 +160,9 @@
         ids.push($(this).attr('data-contextual-id'));
       });
 
+      // Remove duplicates from the IDs array.
+      ids = _.uniq(ids);
+
       // Update all contextual links placeholders whose HTML is cached.
       var uncachedIDs = _.filter(ids, function initIfCached(contextualID) {
         var html = storage.getItem('Drupal.contextual.' + contextualID);
diff --git a/core/modules/contextual/js/contextual.js b/core/modules/contextual/js/contextual.js
index 9093d97..a8c8c22 100644
--- a/core/modules/contextual/js/contextual.js
+++ b/core/modules/contextual/js/contextual.js
@@ -101,6 +101,8 @@
         ids.push($(this).attr('data-contextual-id'));
       });
 
+      ids = _.uniq(ids);
+
       var uncachedIDs = _.filter(ids, function initIfCached(contextualID) {
         var html = storage.getItem('Drupal.contextual.' + contextualID);
         if (html && html.length) {
@@ -147,4 +149,4 @@
   Drupal.theme.contextualTrigger = function () {
     return '<button class="trigger visually-hidden focusable" type="button"></button>';
   };
-})(jQuery, Drupal, drupalSettings, _, Backbone, window.JSON, window.sessionStorage);
\ No newline at end of file
+})(jQuery, Drupal, drupalSettings, _, Backbone, window.JSON, window.sessionStorage);
diff --git a/core/modules/views/src/Element/View.php b/core/modules/views/src/Element/View.php
index 43d1058..8a585cc 100644
--- a/core/modules/views/src/Element/View.php
+++ b/core/modules/views/src/Element/View.php
@@ -80,15 +80,6 @@ public static function preRenderViewElement($element) {
         if (isset($element['view_build']['#title'])) {
           $element['#title'] = &$element['view_build']['#title'];
         }
-
-        if (empty($view->display_handler->getPluginDefinition()['returns_response'])) {
-          // views_add_contextual_links() needs the following information in
-          // order to be attached to the view.
-          $element['#view_id'] = $view->storage->id();
-          $element['#view_display_show_admin_links'] = $view->getShowAdminLinks();
-          $element['#view_display_plugin_id'] = $view->display_handler->getPluginId();
-          views_add_contextual_links($element, 'view', $view->current_display);
-        }
       }
       if (empty($view->display_handler->getPluginDefinition()['returns_response'])) {
         $element['#attributes']['class'][] = 'views-element-container';
diff --git a/core/modules/views/src/Plugin/Block/ViewsBlock.php b/core/modules/views/src/Plugin/Block/ViewsBlock.php
index 34738cd..a065799 100644
--- a/core/modules/views/src/Plugin/Block/ViewsBlock.php
+++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
@@ -49,10 +49,6 @@ public function build() {
     // entry for the view output by passing FALSE, because we're going to cache
     // the whole block instead.
     if ($output = $this->view->buildRenderable($this->displayID, array_values($args), FALSE)) {
-      // Before returning the block output, convert it to a renderable array
-      // with contextual links.
-      $this->addContextualLinks($output);
-
       // Block module expects to get a final render array, without another
       // top-level #pre_render callback. So, here we make sure that Views'
       // #pre_render callback has already been applied.
diff --git a/core/modules/views/src/Plugin/Block/ViewsBlockBase.php b/core/modules/views/src/Plugin/Block/ViewsBlockBase.php
index 4e3020c..2a02e3e 100644
--- a/core/modules/views/src/Plugin/Block/ViewsBlockBase.php
+++ b/core/modules/views/src/Plugin/Block/ViewsBlockBase.php
@@ -186,6 +186,8 @@ public function blockSubmit($form, FormStateInterface $form_state) {
    * @param string $block_type
    *   The type of the block. If it's 'block' it's a regular views display,
    *   but 'exposed_filter' exist as well.
+   *
+   * @deprecated Contextual links applied via DisplayPluginBase.
    */
   protected function addContextualLinks(&$output, $block_type = 'block') {
     // Do not add contextual links to an empty block.
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index 3744fea..03c41c3 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -2339,6 +2339,9 @@ public function buildRenderable(array $args = [], $cache = TRUE) {
       '#embed' => FALSE,
       '#view' => $this->view,
       '#cache_properties' => ['#view_id', '#view_display_show_admin_links', '#view_display_plugin_id'],
+      '#view_display_show_admin_links' => $this->view->getShowAdminLinks(),
+      '#view_display_plugin_id' => $this->display['display_plugin'],
+      '#view_id' => $this->view->storage->id(),
     ];
 
     // When something passes $cache = FALSE, they're asking us not to create our
@@ -2360,6 +2363,9 @@ public function buildRenderable(array $args = [], $cache = TRUE) {
       unset($this->view->element['#cache']['keys']);
     }
 
+    // Attach the contextual links directly to the view.
+    views_add_contextual_links($this->view->element, "block", $this->display['id']);
+
     return $this->view->element;
   }
 
