diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php
index e09545f..dfe960e 100644
--- a/core/modules/views/src/Plugin/views/PluginBase.php
+++ b/core/modules/views/src/Plugin/views/PluginBase.php
@@ -319,13 +319,68 @@ public function globalTokenReplace($string = '', array $options = array()) {
     return \Drupal::token()->replace($string, array('view' => $this->view), $options);
   }
 
+
+  /**
+   * Indicates whether the value has a potential Views argument token.
+   *
+   * @param string $value
+   *   The string to check
+   *
+   * @return bool
+   *   TRUE if the string has a potential Views argument token.
+   *
+   * @see \Drupal\views\Plugin\views\display\DisplayPluginBase::getArgumentTokens()
+   */
+  public function hasArgumentToken($value) {
+    // If the string definitely does not contain a token, return FALSE
+    // immediately for performance.
+    if ((strpos($value, '!') === FALSE) && (strpos($value, '%') === FALSE)) {
+      return FALSE;
+    }
+    // Otherwise, scan for valid token patterns.
+    return preg_match('/[!%]\d+/', $value);
+  }
+
+  /**
+   * Indicates whether the value has a potential Views row-level token.
+   *
+   * @param string $value
+   *   The string to check
+   *
+   * @return bool
+   *   TRUE if the string has a potential row token.
+   *
+   * @see \Drupal\views\Plugin\views\field\FieldPluginBase::getRenderTokens()
+   */
+  public function hasRenderToken($value) {
+    // If the string definitely does not contain a token, return FALSE
+    // immediately for performance.
+    if (strpos($value, '{{') === FALSE) {
+      return FALSE;
+    }
+    // Otherwise, scan for valid token patterns.
+    // Match any non-empty string between {{ and }} except another {{.
+    return preg_match('/\{\{[^(\{\{)]+\}\}/', $value);
+  }
+
+  /**
+   * Indicates whether the value has a potential Views token.
+   *
+   * @param string $value
+   *   The string to check
+   *
+   * @return bool
+   *   TRUE if the string has any token pattern.
+   */
+  public function hasToken($value) {
+    return ($this->hasArgumentToken($value) || $this->hasRenderToken($value));
+  }
+
   /**
-   * Replaces Views' tokens in a given string. It is the responsibility of the
-   * calling function to ensure $text and $token replacements are sanitized.
+   * Replaces Views' tokens in a given string.
    *
-   * This used to be a simple strtr() scattered throughout the code. Some Views
-   * tokens, such as arguments (e.g.: %1 or !1), still use the old format so we
-   * handle those as well as the new Twig-based tokens (e.g.: {{ field_name }})
+   * It is the responsibility of the calling function to ensure $text and
+   * $token replacements are sanitized.
    *
    * @param $text
    *   String with possible tokens.
@@ -333,6 +388,11 @@ public function globalTokenReplace($string = '', array $options = array()) {
    *   Array of token => replacement_value items.
    *
    * @return String
+   *   The text with the tokens replaced.
+   *
+   * @see \Drupal\views\Plugin\views\PluginBase::hasToken()
+   * @see \Drupal\views\Plugin\views\display\DisplayPluginBase::getArgumentTokens()
+   * @see \Drupal\views\Plugin\views\field\FieldPluginBase::getRenderTokens()
    */
   protected function viewsTokenReplace($text, $tokens) {
     if (empty($tokens)) {
@@ -344,8 +404,9 @@ protected function viewsTokenReplace($text, $tokens) {
     $twig_tokens = array();
     $other_tokens = array();
     foreach ($tokens as $token => $replacement) {
-      if (strpos($token, '{{') !== FALSE) {
+      if ($this->hasRenderToken($token)) {
         // Twig wants a token replacement array stripped of curly-brackets.
+        // @todo Do this with the regex instead.
         $token = trim(str_replace(array('{', '}'), '', $token));
         $twig_tokens[$token] = $replacement;
       }
diff --git a/core/modules/views/src/Plugin/views/area/TokenizeAreaPluginBase.php b/core/modules/views/src/Plugin/views/area/TokenizeAreaPluginBase.php
index bf6fbf6..4da1200 100644
--- a/core/modules/views/src/Plugin/views/area/TokenizeAreaPluginBase.php
+++ b/core/modules/views/src/Plugin/views/area/TokenizeAreaPluginBase.php
@@ -106,7 +106,7 @@ public function tokenForm(&$form, FormStateInterface $form_state) {
    */
   public function tokenizeValue($value) {
     if ($this->options['tokenize']) {
-      $value = $this->view->style_plugin->tokenizeValue($value, 0);
+      $value = $this->view->getStyle()->tokenizeValue($value, 0);
     }
     // As we add the globalTokenForm() we also should replace the token here.
     return $this->globalTokenReplace($value);
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index 61c9e84..e0f33cb 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -1053,7 +1053,7 @@ public function optionLink($text, $section, $class = '', $title = '') {
    * This function is similar to views_handler_field::getRenderTokens()
    * but without fields tokens.
    */
-  public function getArgumentsTokens() {
+  public function getArgumentTokens() {
     $tokens = array();
     if (!empty($this->view->build_info['substitutions'])) {
       $tokens = $this->view->build_info['substitutions'];
@@ -2131,7 +2131,7 @@ public function renderMoreLink() {
       $path = $this->getPath();
 
       if ($this->getOption('link_display') == 'custom_url' && $override_path = $this->getOption('link_url')) {
-        $tokens = $this->getArgumentsTokens();
+        $tokens = $this->getArgumentTokens();
         $path = $this->viewsTokenReplace($override_path, $tokens);
       }
 
diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
index 74c8620..520af7c 100644
--- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
+++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
@@ -331,7 +331,7 @@ public function elementClasses($row_index = NULL) {
    * {@inheritdoc}
    */
   public function tokenizeValue($value, $row_index = NULL) {
-    if (strpos($value, '{{') !== FALSE || strpos($value, '!') !== FALSE || strpos($value, '%') !== FALSE) {
+    if ($this->view->getStyle()->hasRenderToken($value) || $this->view->getStyle()->hasArgumentToken($value)) {
       $fake_item = array(
         'alter_text' => TRUE,
         'text' => $value,
@@ -1468,22 +1468,7 @@ protected function renderAsLink($alter, $text, $tokens) {
    * {@inheritdoc}
    */
   public function getRenderTokens($item) {
-    $tokens = array();
-    if (!empty($this->view->build_info['substitutions'])) {
-      $tokens = $this->view->build_info['substitutions'];
-    }
-    $count = 0;
-    foreach ($this->view->display_handler->getHandlers('argument') as $arg => $handler) {
-      $token = '%' . ++$count;
-      if (!isset($tokens[$token])) {
-        $tokens[$token] = '';
-      }
-
-      // Use strip tags as there should never be HTML in the path.
-      // However, we need to preserve special characters like " that
-      // were removed by String::checkPlain().
-      $tokens['!' . $count] = isset($this->view->args[$count - 1]) ? strip_tags(String::decodeEntities($this->view->args[$count - 1])) : '';
-    }
+    $tokens = $this->view->display_handler->getArgumentTokens();
 
     // Get flattened set of tokens for any array depth in query parameters.
     $tokens += $this->getTokenValuesRecursive($this->view->getRequest()->query->all());
diff --git a/core/modules/views/src/Plugin/views/style/StylePluginBase.php b/core/modules/views/src/Plugin/views/style/StylePluginBase.php
index 9c229da..14389ad 100644
--- a/core/modules/views/src/Plugin/views/style/StylePluginBase.php
+++ b/core/modules/views/src/Plugin/views/style/StylePluginBase.php
@@ -184,20 +184,6 @@ function usesFields() {
   }
 
   /**
-   * Return TRUE if this style uses tokens.
-   *
-   * Used to ensure we don't fetch tokens when not needed for performance.
-   */
-  public function usesTokens() {
-    if ($this->usesRowClass()) {
-      $class = $this->options['row_class'];
-      if (strpos($class, '{{') !== FALSE || strpos($class, '!') !== FALSE || strpos($class, '%') !== FALSE) {
-        return TRUE;
-      }
-    }
-  }
-
-  /**
    * Return TRUE if this style enables field labels by default.
    *
    * @return bool
@@ -228,7 +214,7 @@ public function getRowClass($row_index) {
    * Take a value and apply token replacement logic to it.
    */
   public function tokenizeValue($value, $row_index) {
-    if (strpos($value, '{{') !== FALSE || strpos($value, '!') !== FALSE || strpos($value, '%') !== FALSE) {
+    if ($this->hasArgumentToken($value) || $this->hasRenderToken($value)) {
       // Row tokens might be empty, for example for node row style.
       $tokens = isset($this->rowTokens[$row_index]) ? $this->rowTokens[$row_index] : array();
       if (!empty($this->view->build_info['substitutions'])) {
@@ -239,7 +225,6 @@ public function tokenizeValue($value, $row_index) {
     }
     return $value;
   }
-
   /**
    * Should the output of the style plugin be rendered even if it's a empty view.
    */
diff --git a/core/modules/views/tests/src/Unit/PluginBaseTest.php b/core/modules/views/tests/src/Unit/PluginBaseTest.php
index 9c180a6..240f36a 100644
--- a/core/modules/views/tests/src/Unit/PluginBaseTest.php
+++ b/core/modules/views/tests/src/Unit/PluginBaseTest.php
@@ -320,4 +320,103 @@ public function providerTestFilterByDefinedOptions() {
     return $data;
   }
 
+  /**
+   * @covers ::hasArgumentToken
+   * @dataProvider providerHasArgumentToken
+   */
+  public function testHasArgumentToken($value, $return) {
+    $this->assertEquals($this->testHelperPlugin->hasArgumentToken($value), $return);
+  }
+
+  /**
+   * Data provider for testHasArgumentToken().
+   *
+   * @return array
+   */
+  public function providerHasArgumentToken() {
+    return array(
+      array('%', FALSE),
+      array('!', FALSE),
+      array('!', FALSE),
+      array('% 1', FALSE),
+      array('! 1', FALSE),
+      array('!1', TRUE),
+      array('%1', TRUE),
+      array(' !12345 ', TRUE),
+      array(' %54321 ', TRUE),
+      array('%elephant', FALSE),
+      array('!elephant', FALSE),
+      array('[old_token]', FALSE),
+      array('[other:token]', FALSE),
+      array('{{render_token}}', FALSE),
+    );
+  }
+
+  /**
+   * @covers ::hasRenderToken
+   * @dataProvider providerHasRenderToken
+   */
+  public function testHasRenderToken($value, $return) {
+    $this->assertEquals($this->testHelperPlugin->hasRenderToken($value), $return);
+  }
+
+  /**
+   * Data provider for testHasRenderToken().
+   *
+   * @return array
+   */
+  public function providerHasRenderToken() {
+    return array(
+      array('%1', FALSE),
+      array('!1', FALSE),
+      array('{', FALSE),
+      array('{{', FALSE),
+      array('}', FALSE),
+      array('}}', FALSE),
+      array('{{}}', FALSE),
+      array('{{{{}}', FALSE),
+      array('{{ elephant }}', TRUE),
+      array(' {{elephant_render_token_1}} ', TRUE),
+    );
+  }
+
+  /**
+   * @covers ::hasToken
+   * @dataProvider providerHasToken
+   */
+  public function testHasToken($value, $return) {
+    $this->assertEquals($this->testHelperPlugin->hasToken($value), $return);
+  }
+
+  /**
+   * Data provider for testHasToken().
+   *
+   * @return array
+   */
+  public function providerHasToken() {
+    return array(
+      array('%', FALSE),
+      array('!', FALSE),
+      array('!', FALSE),
+      array('% 1', FALSE),
+      array('! 1', FALSE),
+      array('!1', TRUE),
+      array('%1', TRUE),
+      array(' !12345 ', TRUE),
+      array(' %54321 ', TRUE),
+      array('%elephant', FALSE),
+      array('!elephant', FALSE),
+      array('{', FALSE),
+      array('{{', FALSE),
+      array('}', FALSE),
+      array('}}', FALSE),
+      array('{{}}', FALSE),
+      array('{{{{}}', FALSE),
+      array('{{ elephant }}', TRUE),
+      array(' {{elephant_render_token_1}} ', TRUE),
+      array('[old_token]', FALSE),
+      array('[other:token]', FALSE),
+    );
+  }
+
 }
