diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index cf0e3dd..9951202 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -6,7 +6,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Timer;
 use Drupal\Component\Utility\Unicode;
-use Drupal\Component\Utility\UrlValidator;
+use Drupal\Component\Utility\Url;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 61b63be..f872750 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -3,7 +3,7 @@
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Tags;
-use Drupal\Component\Utility\UrlValidator;
+use Drupal\Component\Utility\Url;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Language\Language;
@@ -439,56 +439,14 @@ function drupal_get_feeds($delimiter = "\n") {
  *
  * @return
  *   An array containing query parameters, which can be used for url().
+ *
+ * @deprecated as of Drupal 8.0. Use Url::filterQueryParameters() instead.
  */
 function drupal_get_query_parameters(array $query = NULL, array $exclude = array(), $parent = '') {
-  // Set defaults, if none given.
   if (!isset($query)) {
-    $query = $_GET;
-  }
-  // If $exclude is empty, there is nothing to filter.
-  if (empty($exclude)) {
-    return $query;
-  }
-  elseif (!$parent) {
-    $exclude = array_flip($exclude);
-  }
-
-  $params = array();
-  foreach ($query as $key => $value) {
-    $string_key = ($parent ? $parent . '[' . $key . ']' : $key);
-    if (isset($exclude[$string_key])) {
-      continue;
-    }
-
-    if (is_array($value)) {
-      $params[$key] = drupal_get_query_parameters($value, $exclude, $string_key);
-    }
-    else {
-      $params[$key] = $value;
-    }
-  }
-
-  return $params;
-}
-
-/**
- * Splits a URL-encoded query string into an array.
- *
- * @param $query
- *   The query string to split.
- *
- * @return
- *   An array of URL decoded couples $param_name => $value.
- */
-function drupal_get_query_array($query) {
-  $result = array();
-  if (!empty($query)) {
-    foreach (explode('&', $query) as $param) {
-      $param = explode('=', $param);
-      $result[$param[0]] = isset($param[1]) ? rawurldecode($param[1]) : '';
-    }
+    $query = Drupal::request()->query->all();
   }
-  return $result;
+  return Url::filterQueryParameters($query, $exclude, $parent);
 }
 
 /**
@@ -496,12 +454,13 @@ function drupal_get_query_array($query) {
  *
  * @see \Drupal\Core\Routing\PathBasedGeneratorInterface::httpBuildQuery()
  * @see drupal_get_query_parameters()
- * @deprecated as of Drupal 8.0. Use
- *   Drupal::urlGenerator()->httpBuildQuery() instead.
+ * @deprecated as of Drupal 8.0. Use Url::buildQuery() instead.
  * @ingroup php_wrappers
+ *
+ * @deprecated as of Drupal 8.0. Use Url::buildQuery() instead.
  */
 function drupal_http_build_query(array $query, $parent = '') {
-  return Drupal::urlGenerator()->httpBuildQuery($query, $parent);
+  return Url::buildQuery($query, $parent);
 }
 
 /**
@@ -526,12 +485,13 @@ function drupal_get_destination() {
     return $destination;
   }
 
-  if (isset($_GET['destination'])) {
-    $destination = array('destination' => $_GET['destination']);
+  $query = Drupal::request()->query;
+  if ($query->has('destination')) {
+    $destination = array('destination' => $query->get('destination'));
   }
   else {
     $path = current_path();
-    $query = Drupal::urlGenerator()->httpBuildQuery(drupal_get_query_parameters());
+    $query = Url::buildQuery(Url::filterQueryParameters($query->all()));
     if ($query != '') {
       $path .= '?' . $query;
     }
@@ -572,46 +532,11 @@ function drupal_get_destination() {
  *
  * @see url()
  * @ingroup php_wrappers
+ *
+ * @deprecated as of Drupal 8.0. Use Url::parse() instead.
  */
 function drupal_parse_url($url) {
-  $options = array(
-    'path' => NULL,
-    'query' => array(),
-    'fragment' => '',
-  );
-
-  // External URLs: not using parse_url() here, so we do not have to rebuild
-  // the scheme, host, and path without having any use for it.
-  if (strpos($url, '://') !== FALSE) {
-    // Split off everything before the query string into 'path'.
-    $parts = explode('?', $url);
-    $options['path'] = $parts[0];
-    // If there is a query string, transform it into keyed query parameters.
-    if (isset($parts[1])) {
-      $query_parts = explode('#', $parts[1]);
-      parse_str($query_parts[0], $options['query']);
-      // Take over the fragment, if there is any.
-      if (isset($query_parts[1])) {
-        $options['fragment'] = $query_parts[1];
-      }
-    }
-  }
-  // Internal URLs.
-  else {
-    // parse_url() does not support relative URLs, so make it absolute. E.g. the
-    // relative URL "foo/bar:1" isn't properly parsed.
-    $parts = parse_url('http://example.com/' . $url);
-    // Strip the leading slash that was just added.
-    $options['path'] = substr($parts['path'], 1);
-    if (isset($parts['query'])) {
-      parse_str($parts['query'], $options['query']);
-    }
-    if (isset($parts['fragment'])) {
-      $options['fragment'] = $parts['fragment'];
-    }
-  }
-
-  return $options;
+  return Url::parse($url);
 }
 
 /**
@@ -624,9 +549,11 @@ function drupal_parse_url($url) {
  *
  * @param $path
  *   The Drupal path to encode.
+ *
+ * @deprecated as of Drupal 8.0. Use Url::encodePath() instead.
  */
 function drupal_encode_path($path) {
-  return str_replace('%2F', '/', rawurlencode($path));
+  return Url::encodePath($path);
 }
 
 /**
@@ -637,20 +564,11 @@ function drupal_encode_path($path) {
  *
  * @return
  *   TRUE if the URL has the same domain and base path.
+ *
+ * @deprecated as of Drupal 8.0. Use Url::externalIsLocal() instead.
  */
 function _external_url_is_local($url) {
-  $url_parts = parse_url($url);
-  $base_host = parse_url($GLOBALS['base_url'], PHP_URL_HOST);
-
-  if (!isset($url_parts['path'])) {
-    return ($url_parts['host'] == $base_host);
-  }
-  else {
-    // When comparing base paths, we need a trailing slash to make sure a
-    // partial URL match isn't occuring. Since base_path() always returns with
-    // a trailing slash, we don't need to add the trailing slash here.
-    return ($url_parts['host'] == $base_host && stripos($url_parts['path'], base_path()) === 0);
-  }
+  return Url::externalIsLocal($url, base_path());
 }
 
 /**
@@ -704,12 +622,12 @@ function valid_email_address($mail) {
  * @return
  *   TRUE if the URL is in a valid format.
  *
- * @see \Drupal\Component\Utility\UrlValidator::isValid()
+ * @see \Drupal\Component\Utility\Url::isValid()
  *
- * @deprecated as of Drupal 8.0. Use UrlValidator::isValid() instead.
+ * @deprecated as of Drupal 8.0. Use Url::isValid() instead.
  */
 function valid_url($url, $absolute = FALSE) {
-  return UrlValidator::isValid($url, $absolute);
+  return Url::isValid($url, $absolute);
 }
 
 /**
@@ -794,7 +712,7 @@ function valid_number_step($value, $step, $offset = 0.0) {
  * @see \Drupal\Component\Utility\Url::stripDangerousProtocols()
  */
 function drupal_strip_dangerous_protocols($uri) {
-  return UrlValidator::stripDangerousProtocols($uri);
+  return Url::stripDangerousProtocols($uri);
 }
 
 /**
@@ -816,7 +734,7 @@ function drupal_strip_dangerous_protocols($uri) {
  * @see \Drupal\Component\Utility\String::checkPlain()
  */
 function check_url($uri) {
-  return String::checkPlain(UrlValidator::stripDangerousProtocols($uri));
+  return String::checkPlain(Url::stripDangerousProtocols($uri));
 }
 
 /**
@@ -884,7 +802,7 @@ function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite',
  * @see \Drupal\Component\Utility\Url::filterBadProtocol()
  */
 function filter_xss_bad_protocol($string) {
-  return UrlValidator::filterBadProtocol($string);
+  return Url::filterBadProtocol($string);
 }
 
 /**
@@ -1312,11 +1230,7 @@ function url($path = NULL, array $options = array()) {
  *   Boolean TRUE or FALSE, where TRUE indicates an external path.
  */
 function url_is_external($path) {
-  $colonpos = strpos($path, ':');
-  // Avoid calling drupal_strip_dangerous_protocols() if there is any
-  // slash (/), hash (#) or question_mark (?) before the colon (:)
-  // occurrence - if any - as this would clearly mean it is not a URL.
-  return $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path;
+  return Url::isExternal($path);
 }
 
 /**
@@ -4011,7 +3925,7 @@ function _drupal_bootstrap_code() {
     // of allowed protocols for these cases.
     $allowed_protocols = array('http', 'https');
   }
-  UrlValidator::setAllowedProtocols($allowed_protocols);
+  Url::setAllowedProtocols($allowed_protocols);
 }
 
 /**
diff --git a/core/lib/Drupal/Component/Utility/Url.php b/core/lib/Drupal/Component/Utility/Url.php
new file mode 100644
index 0000000..cdc01c8
--- /dev/null
+++ b/core/lib/Drupal/Component/Utility/Url.php
@@ -0,0 +1,353 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\Utility\Url.
+ */
+
+namespace Drupal\Component\Utility;
+
+/**
+ * Helper class URL based methods.
+ */
+class Url {
+
+  /**
+   * The list of allowed protocols.
+   *
+   * @var array
+   */
+  protected static $allowedProtocols = array('http', 'https');
+
+  /**
+   * Parses an array into a valid, rawurlencoded query string.
+   *
+   *
+   * rawurlencode() is RFC3986 compliant, and as a consequence RFC3987
+   * compliant. The latter defines the required format of "URLs" in HTML5.
+   * urlencode() is almost the same as rawurlencode(), except that it encodes
+   * spaces as "+" instead of "%20". This makes its result non compliant to
+   * RFC3986 and as a consequence non compliant to RFC3987 and as a consequence
+   * not valid as a "URL" in HTML5.
+   *
+   * @todo Remove this function once PHP 5.4 is required as we can use just
+   *   http_build_query() directly.
+   *
+   * @param array $query
+   *   The query parameter array to be processed, e.g. $_GET.
+   * @param string $parent
+   *   Internal use only. Used to build the $query array key for nested items.
+   *
+   * @return string
+   *   A rawurlencoded string which can be used as or appended to the URL query
+   *   string.
+   *
+   * @ingroup php_wrappers
+   */
+  public static function buildQuery(array $query, $parent = '') {
+    $params = array();
+
+    foreach ($query as $key => $value) {
+      $key = ($parent ? $parent . '[' . rawurlencode($key) . ']' : rawurlencode($key));
+
+      // Recurse into children.
+      if (is_array($value)) {
+        $params[] = static::buildQuery($value, $key);
+      }
+      // If a query parameter value is NULL, only append its key.
+      elseif (!isset($value)) {
+        $params[] = $key;
+      }
+      else {
+        // For better readability of paths in query strings, we decode slashes.
+        $params[] = $key . '=' . str_replace('%2F', '/', rawurlencode($value));
+      }
+    }
+
+    return implode('&', $params);
+  }
+
+  /**
+   * Filters a URL query parameter array to remove unwanted elements.
+   *
+   * @param array $query
+   *   An array to be processed.
+   * @param array $exclude
+   *   (optional) A list of $query array keys to remove. Use "parent[child]" to
+   *   exclude nested items.
+   * @param string $parent
+   *   Internal use only. Used to build the $query array key for nested items.
+   *
+   * @return
+   *   An array containing query parameters.
+   */
+  public static function filterQueryParameters(array $query, array $exclude = array(), $parent = '') {
+    // If $exclude is empty, there is nothing to filter.
+    if (empty($exclude)) {
+      return $query;
+    }
+    elseif (!$parent) {
+      $exclude = array_flip($exclude);
+    }
+
+    $params = array();
+    foreach ($query as $key => $value) {
+      $string_key = ($parent ? $parent . '[' . $key . ']' : $key);
+      if (isset($exclude[$string_key])) {
+        continue;
+      }
+
+      if (is_array($value)) {
+        $params[$key] = static::filterQueryParameters($value, $exclude, $string_key);
+      }
+      else {
+        $params[$key] = $value;
+      }
+    }
+
+    return $params;
+  }
+
+  /**
+   * Parses a system URL string into an associative array.
+   *
+   * This function should only be used for URLs that have been generated by the
+   * system. It should not be used for URLs that come from external sources, or
+   * URLs that link to external resources.
+   *
+   * The returned array contains a 'path' that may be passed separately to url().
+   * For example:
+   * @code
+   *   $options = Url::parse($_GET['destination']);
+   *   $my_url = url($options['path'], $options);
+   *   $my_link = l('Example link', $options['path'], $options);
+   * @endcode
+   *
+   * @param string $url
+   *   The URL string to parse, f.e. $_GET['destination'].
+   *
+   * @return
+   *   An associative array containing the keys:
+   *   - 'path': The path of the URL. If the given $url is external, this includes
+   *     the scheme and host.
+   *   - 'query': An array of query parameters of $url, if existent.
+   *   - 'fragment': The fragment of $url, if existent.
+   *
+   * @ingroup php_wrappers
+   */
+  public static function parse($url) {
+    $options = array(
+      'path' => NULL,
+      'query' => array(),
+      'fragment' => '',
+    );
+
+    // External URLs: not using parse_url() here, so we do not have to rebuild
+    // the scheme, host, and path without having any use for it.
+    if (strpos($url, '://') !== FALSE) {
+      // Split off everything before the query string into 'path'.
+      $parts = explode('?', $url);
+      $options['path'] = $parts[0];
+      // If there is a query string, transform it into keyed query parameters.
+      if (isset($parts[1])) {
+        $query_parts = explode('#', $parts[1]);
+        parse_str($query_parts[0], $options['query']);
+        // Take over the fragment, if there is any.
+        if (isset($query_parts[1])) {
+          $options['fragment'] = $query_parts[1];
+        }
+      }
+    }
+    // Internal URLs.
+    else {
+      // parse_url() does not support relative URLs, so make it absolute. E.g. the
+      // relative URL "foo/bar:1" isn't properly parsed.
+      $parts = parse_url('http://example.com/' . $url);
+      // Strip the leading slash that was just added.
+      $options['path'] = substr($parts['path'], 1);
+      if (isset($parts['query'])) {
+        parse_str($parts['query'], $options['query']);
+      }
+      if (isset($parts['fragment'])) {
+        $options['fragment'] = $parts['fragment'];
+      }
+    }
+
+    return $options;
+  }
+
+  /**
+   * Encodes a Drupal path for use in a URL.
+   *
+   * For aesthetic reasons slashes are not escaped.
+   *
+   * @param string $path
+   *   The Drupal path to encode.
+   *
+   * @return string
+   *   The encoded path.
+   */
+  public static function encodePath($path) {
+    return str_replace('%2F', '/', rawurlencode($path));
+  }
+
+  /**
+   * Returns whether a path is external to Drupal (e.g. http://example.com).
+   *
+   * If a path cannot be assessed by Drupal's menu handler, then we must
+   * treat it as potentially insecure.
+   *
+   * @param string $path
+   *   The internal path or external URL being linked to, such as "node/34" or
+   *   "http://example.com/foo".
+   *
+   * @return bool
+   *   TRUE or FALSE, where TRUE indicates an external path.
+   */
+  public static function isExternal($path) {
+    $colonpos = strpos($path, ':');
+    // Avoid calling stripDangerousProtocols() if there is any
+    // slash (/), hash (#) or question_mark (?) before the colon (:)
+    // occurrence - if any - as this would clearly mean it is not a URL.
+    return $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && static::stripDangerousProtocols($path) == $path;
+  }
+
+  /**
+   * Determines if an external URL points to this installation.
+   *
+   * @param string $url
+   *   A string containing an external URL, such as "http://example.com/foo".
+   * @param string $base_url
+   *   The base URL string to check against, such as "http://example.com/"
+   *
+   * @return
+   *   TRUE if the URL has the same domain and base path.
+   */
+  public static function externalIsLocal($url, $base_url) {
+    $url_parts = parse_url($url);
+    $base_host = parse_url($base_url, PHP_URL_HOST);
+
+    if (!isset($url_parts['path'])) {
+      return ($url_parts['host'] == $base_host);
+    }
+    else {
+      // When comparing base paths, we need a trailing slash to make sure a
+      // partial URL match isn't occuring. Since base_path() always returns with
+      // a trailing slash, we don't need to add the trailing slash here.
+      return ($url_parts['host'] == $base_host && stripos($url_parts['path'], $base_url) === 0);
+    }
+  }
+
+  /**
+   * Processes an HTML attribute value and strips dangerous protocols from URLs.
+   *
+   * @param string $string
+   *   The string with the attribute value.
+   *
+   * @return string
+   *   Cleaned up and HTML-escaped version of $string.
+   */
+  public static function filterBadProtocol($string) {
+    // Get the plain text representation of the attribute value (i.e. its
+    // meaning).
+    $string = String::decodeEntities($string);
+    return String::checkPlain(static::stripDangerousProtocols($string));
+  }
+
+  /**
+   * Sets the allowed protocols.
+   *
+   * @param array $protocols
+   *   An array of protocols, for example http, https and irc.
+   */
+  public static function setAllowedProtocols(array $protocols = array()) {
+    static::$allowedProtocols = $protocols;
+  }
+
+  /**
+   * Strips dangerous protocols (e.g. 'javascript:') from a URI.
+   *
+   * This function must be called for all URIs within user-entered input prior
+   * to being output to an HTML attribute value. It is often called as part of
+   * check_url() or Drupal\Component\Utility\Xss::filter(), but those functions
+   * return an HTML-encoded string, so this function can be called independently
+   * when the output needs to be a plain-text string for passing to t(), l(),
+   * Drupal\Core\Template\Attribute, or another function that will call
+   * \Drupal\Component\Utility\String::checkPlain() separately.
+   *
+   * @param string $uri
+   *   A plain-text URI that might contain dangerous protocols.
+   *
+   * @return string
+   *   A plain-text URI stripped of dangerous protocols. As with all plain-text
+   *   strings, this return value must not be output to an HTML page without
+   *   being sanitized first. However, it can be passed to functions
+   *   expecting plain-text strings.
+   */
+  public static function stripDangerousProtocols($uri) {
+    $allowed_protocols = array_flip(static::$allowedProtocols);
+
+    // Iteratively remove any invalid protocol found.
+    do {
+      $before = $uri;
+      $colonpos = strpos($uri, ':');
+      if ($colonpos > 0) {
+        // We found a colon, possibly a protocol. Verify.
+        $protocol = substr($uri, 0, $colonpos);
+        // If a colon is preceded by a slash, question mark or hash, it cannot
+        // possibly be part of the URL scheme. This must be a relative URL, which
+        // inherits the (safe) protocol of the base document.
+        if (preg_match('![/?#]!', $protocol)) {
+          break;
+        }
+        // Check if this is a disallowed protocol. Per RFC2616, section 3.2.3
+        // (URI Comparison) scheme comparison must be case-insensitive.
+        if (!isset($allowed_protocols[strtolower($protocol)])) {
+          $uri = substr($uri, $colonpos + 1);
+        }
+      }
+    } while ($before != $uri);
+
+    return $uri;
+  }
+
+  /**
+   * Verifies the syntax of the given URL.
+   *
+   * This function should only be used on actual URLs. It should not be used for
+   * Drupal menu paths, which can contain arbitrary characters.
+   * Valid values per RFC 3986.
+   *
+   * @param string $url
+   *   The URL to verify.
+   * @param bool $absolute
+   *   Whether the URL is absolute (beginning with a scheme such as "http:").
+   *
+   * @return bool
+   *   TRUE if the URL is in a valid format, FALSE otherwise.
+   */
+  public static function isValid($url, $absolute = FALSE) {
+    if ($absolute) {
+      return (bool) preg_match("
+        /^                                                      # Start at the beginning of the text
+        (?:ftp|https?|feed):\/\/                                # Look for ftp, http, https or feed schemes
+        (?:                                                     # Userinfo (optional) which is typically
+          (?:(?:[\w\.\-\+!$&'\(\)*\+,;=]|%[0-9a-f]{2})+:)*      # a username or a username and password
+          (?:[\w\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})+@          # combination
+        )?
+        (?:
+          (?:[a-z0-9\-\.]|%[0-9a-f]{2})+                        # A domain name or a IPv4 address
+          |(?:\[(?:[0-9a-f]{0,4}:)*(?:[0-9a-f]{0,4})\])         # or a well formed IPv6 address
+        )
+        (?::[0-9]+)?                                            # Server port number (optional)
+        (?:[\/|\?]
+          (?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})   # The path and query (optional)
+        *)?
+      $/xi", $url);
+    }
+    else {
+      return (bool) preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url);
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Component/Utility/UrlValidator.php b/core/lib/Drupal/Component/Utility/UrlValidator.php
deleted file mode 100644
index 4772373..0000000
--- a/core/lib/Drupal/Component/Utility/UrlValidator.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Component\Utility\UrlValidator.
- */
-
-namespace Drupal\Component\Utility;
-
-/**
- * Helper class to support filtering bad protocols from an url.
- */
-class UrlValidator {
-
-  /**
-   * The list of allowed protocols.
-   *
-   * @var array
-   */
-  protected static $allowedProtocols = array('http', 'https');
-
-  /**
-   * Processes an HTML attribute value and strips dangerous protocols from URLs.
-   *
-   * @param string $string
-   *   The string with the attribute value.
-   *
-   * @return string
-   *   Cleaned up and HTML-escaped version of $string.
-   */
-  public static function filterBadProtocol($string) {
-    // Get the plain text representation of the attribute value (i.e. its meaning).
-    $string = String::decodeEntities($string);
-
-    return String::checkPlain(static::stripDangerousProtocols($string));
-  }
-
-  /**
-   * Sets the allowed protocols.
-   *
-   * @param array $protocols
-   *   An array of protocols, for example http, https and irc.
-   */
-  public static function setAllowedProtocols(array $protocols = array()) {
-    static::$allowedProtocols = $protocols;
-  }
-
-  /**
-   * Strips dangerous protocols (e.g. 'javascript:') from a URI.
-   *
-   * This function must be called for all URIs within user-entered input prior
-   * to being output to an HTML attribute value. It is often called as part of
-   * check_url() or filter_xss(), but those functions return an HTML-encoded
-   * string, so this function can be called independently when the output needs to
-   * be a plain-text string for passing to t(), l(),
-   * Drupal\Core\Template\Attribute, or another function that will call
-   * check_plain() separately.
-   *
-   * @param string $uri
-   *   A plain-text URI that might contain dangerous protocols.
-   *
-   * @return string
-   *   A plain-text URI stripped of dangerous protocols. As with all plain-text
-   *   strings, this return value must not be output to an HTML page without
-   *   check_plain() being called on it. However, it can be passed to functions
-   *   expecting plain-text strings.
-   *
-   * @see check_url()
-   */
-  public static function stripDangerousProtocols($uri) {
-    $allowed_protocols = array_flip(static::$allowedProtocols);
-
-    // Iteratively remove any invalid protocol found.
-    do {
-      $before = $uri;
-      $colonpos = strpos($uri, ':');
-      if ($colonpos > 0) {
-        // We found a colon, possibly a protocol. Verify.
-        $protocol = substr($uri, 0, $colonpos);
-        // If a colon is preceded by a slash, question mark or hash, it cannot
-        // possibly be part of the URL scheme. This must be a relative URL, which
-        // inherits the (safe) protocol of the base document.
-        if (preg_match('![/?#]!', $protocol)) {
-          break;
-        }
-        // Check if this is a disallowed protocol. Per RFC2616, section 3.2.3
-        // (URI Comparison) scheme comparison must be case-insensitive.
-        if (!isset($allowed_protocols[strtolower($protocol)])) {
-          $uri = substr($uri, $colonpos + 1);
-        }
-      }
-    } while ($before != $uri);
-
-    return $uri;
-  }
-
-  /**
-   * Verifies the syntax of the given URL.
-   *
-   * This function should only be used on actual URLs. It should not be used for
-   * Drupal menu paths, which can contain arbitrary characters.
-   * Valid values per RFC 3986.
-   *
-   * @param string $url
-   *   The URL to verify.
-   * @param bool $absolute
-   *   Whether the URL is absolute (beginning with a scheme such as "http:").
-   *
-   * @return bool
-   *   TRUE if the URL is in a valid format.
-   */
-  public static function isValid($url, $absolute = FALSE) {
-    if ($absolute) {
-      return (bool) preg_match("
-        /^                                                      # Start at the beginning of the text
-        (?:ftp|https?|feed):\/\/                                # Look for ftp, http, https or feed schemes
-        (?:                                                     # Userinfo (optional) which is typically
-          (?:(?:[\w\.\-\+!$&'\(\)*\+,;=]|%[0-9a-f]{2})+:)*      # a username or a username and password
-          (?:[\w\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})+@          # combination
-        )?
-        (?:
-          (?:[a-z0-9\-\.]|%[0-9a-f]{2})+                        # A domain name or a IPv4 address
-          |(?:\[(?:[0-9a-f]{0,4}:)*(?:[0-9a-f]{0,4})\])         # or a well formed IPv6 address
-        )
-        (?::[0-9]+)?                                            # Server port number (optional)
-        (?:[\/|\?]
-          (?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})   # The path and query (optional)
-        *)?
-      $/xi", $url);
-    }
-    else {
-      return (bool) preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url);
-    }
-  }
-
-}
diff --git a/core/lib/Drupal/Component/Utility/Xss.php b/core/lib/Drupal/Component/Utility/Xss.php
index a1f1956..8d0d257 100644
--- a/core/lib/Drupal/Component/Utility/Xss.php
+++ b/core/lib/Drupal/Component/Utility/Xss.php
@@ -225,7 +225,7 @@ protected static function attributes($attributes) {
         case 2:
           // Attribute value, a URL after href= for instance.
           if (preg_match('/^"([^"]*)"(\s+|$)/', $attributes, $match)) {
-            $thisval = UrlValidator::filterBadProtocol($match[1]);
+            $thisval = Url::filterBadProtocol($match[1]);
 
             if (!$skip) {
               $attributes_array[] = "$attribute_name=\"$thisval\"";
@@ -237,7 +237,7 @@ protected static function attributes($attributes) {
           }
 
           if (preg_match("/^'([^']*)'(\s+|$)/", $attributes, $match)) {
-            $thisval = UrlValidator::filterBadProtocol($match[1]);
+            $thisval = Url::filterBadProtocol($match[1]);
 
             if (!$skip) {
               $attributes_array[] = "$attribute_name='$thisval'";
@@ -248,7 +248,7 @@ protected static function attributes($attributes) {
           }
 
           if (preg_match("%^([^\s\"']+)(\s+|$)%", $attributes, $match)) {
-            $thisval = UrlValidator::filterBadProtocol($match[1]);
+            $thisval = Url::filterBadProtocol($match[1]);
 
             if (!$skip) {
               $attributes_array[] = "$attribute_name=\"$thisval\"";
diff --git a/core/lib/Drupal/Core/Routing/UrlGenerator.php b/core/lib/Drupal/Core/Routing/UrlGenerator.php
index 04c2c17..972058a 100644
--- a/core/lib/Drupal/Core/Routing/UrlGenerator.php
+++ b/core/lib/Drupal/Core/Routing/UrlGenerator.php
@@ -16,7 +16,7 @@
 use Symfony\Cmf\Component\Routing\ProviderBasedGenerator;
 
 use Drupal\Component\Utility\Settings;
-use Drupal\Component\Utility\UrlValidator;
+use Drupal\Component\Utility\Url;
 use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
 
@@ -83,7 +83,7 @@ public function __construct(RouteProviderInterface $provider, OutboundPathProces
     $this->pathProcessor = $path_processor;
     $this->mixedModeSessions = $settings->get('mixed_mode_sessions', FALSE);
     $allowed_protocols = $config->get('system.filter')->get('protocols') ?: array('http', 'https');
-    UrlValidator::setAllowedProtocols($allowed_protocols);
+    Url::setAllowedProtocols($allowed_protocols);
   }
 
   /**
@@ -112,7 +112,7 @@ public function generate($name, $parameters = array(), $absolute = FALSE) {
     if ($name instanceof SymfonyRoute) {
       $route = $name;
     }
-    elseif (null === $route = $this->provider->getRouteByName($name, $parameters)) {
+    elseif (NULL === $route = $this->provider->getRouteByName($name, $parameters)) {
       throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name));
     }
 
@@ -239,7 +239,7 @@ public function generateFromPath($path = NULL, $options = array()) {
       // that would require another function call, and performance inside url() is
       // critical.
       $colonpos = strpos($path, ':');
-      $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && UrlValidator::stripDangerousProtocols($path) == $path);
+      $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && Url::stripDangerousProtocols($path) == $path);
     }
 
     if (isset($options['fragment']) && $options['fragment'] !== '') {
@@ -257,7 +257,7 @@ public function generateFromPath($path = NULL, $options = array()) {
       }
       // Append the query.
       if ($options['query']) {
-        $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . $this->httpBuildQuery($options['query']);
+        $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . Url::buildQuery($options['query']);
       }
       if (isset($options['https']) && $this->mixedModeSessions) {
         if ($options['https'] === TRUE) {
@@ -300,7 +300,7 @@ public function generateFromPath($path = NULL, $options = array()) {
     $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];
 
     $path = str_replace('%2F', '/', rawurlencode($prefix . $path));
-    $query = $options['query'] ? ('?' . $this->httpBuildQuery($options['query'])) : '';
+    $query = $options['query'] ? ('?' . Url::buildQuery($options['query'])) : '';
     return $base . $options['script'] . $path . $query . $options['fragment'];
   }
 
@@ -326,47 +326,6 @@ public function setScriptPath($path) {
   }
 
   /**
-   * Parses an array into a valid, rawurlencoded query string.
-   *
-   * This differs from http_build_query() as we need to rawurlencode() (instead of
-   * urlencode()) all query parameters.
-   *
-   * @param $query
-   *   The query parameter array to be processed, e.g. $_GET.
-   * @param $parent
-   *   Internal use only. Used to build the $query array key for nested items.
-   *
-   * @return
-   *   A rawurlencoded string which can be used as or appended to the URL query
-   *   string.
-   *
-   * @see drupal_get_query_parameters()
-   * @ingroup php_wrappers
-   */
-  public function httpBuildQuery(array $query, $parent = '') {
-    $params = array();
-
-    foreach ($query as $key => $value) {
-      $key = ($parent ? $parent . '[' . rawurlencode($key) . ']' : rawurlencode($key));
-
-      // Recurse into children.
-      if (is_array($value)) {
-        $params[] = $this->httpBuildQuery($value, $key);
-      }
-      // If a query parameter value is NULL, only append its key.
-      elseif (!isset($value)) {
-        $params[] = $key;
-      }
-      else {
-        // For better readability of paths in query strings, we decode slashes.
-        $params[] = $key . '=' . str_replace('%2F', '/', rawurlencode($value));
-      }
-    }
-
-    return implode('&', $params);
-  }
-
-  /**
    * Passes the path to a processor manager to allow alterations.
    */
   protected function processPath($path, &$options = array()) {
diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module
index 75e3ed2..9280a7b 100644
--- a/core/modules/contextual/contextual.module
+++ b/core/modules/contextual/contextual.module
@@ -347,7 +347,8 @@ function _contextual_id_to_links($id) {
   foreach ($contexts as $context) {
     list($module, $parent_path, $path_args, $metadata_raw) = explode(':', $context);
     $path_args = explode('/', $path_args);
-    $metadata = drupal_get_query_array($metadata_raw);
+    $metadata = array();
+    parse_str($metadata_raw, $metadata);
     $contextual_links[$module] = array($parent_path, $path_args, $metadata);
   }
   return $contextual_links;
diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc
index 82bf713..a9f007d 100644
--- a/core/modules/language/language.negotiation.inc
+++ b/core/modules/language/language.negotiation.inc
@@ -500,7 +500,9 @@ function language_url_rewrite_session(&$path, &$options) {
   // user language preference even with cookies disabled.
   if ($query_rewrite) {
     if (is_string($options['query'])) {
-      $options['query'] = drupal_get_query_array($options['query']);
+      $query = array();
+      parse_str($options['query'], $query);
+      $options['query'] = $query;
     }
     if (!isset($options['query'][$query_param])) {
       $options['query'][$query_param] = $query_value;
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php
index fa0d4c5..86d1683 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php
@@ -236,7 +236,8 @@ public function validate(array $form, array &$form_state) {
     if (!url_is_external($menu_link->link_path)) {
       $parsed_link = parse_url($menu_link->link_path);
       if (isset($parsed_link['query'])) {
-        $menu_link->options['query'] = drupal_get_query_array($parsed_link['query']);
+        $menu_link->options['query'] = array();
+        parse_str($parsed_link['query'], $menu_link->options['query']);
       }
       else {
         // Use unset() rather than setting to empty string
diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php
index b6e1ae7..249afd4 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php
@@ -197,16 +197,6 @@ function testDrupalGetQueryParameters() {
   }
 
   /**
-   * Tests drupal_http_build_query().
-   */
-  function testDrupalHttpBuildQuery() {
-    $this->assertEqual(drupal_http_build_query(array('a' => ' &#//+%20@۞')), 'a=%20%26%23//%2B%2520%40%DB%9E', 'Value was properly encoded.');
-    $this->assertEqual(drupal_http_build_query(array(' &#//+%20@۞' => 'a')), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', 'Key was properly encoded.');
-    $this->assertEqual(drupal_http_build_query(array('a' => '1', 'b' => '2', 'c' => '3')), 'a=1&b=2&c=3', 'Multiple values were properly concatenated.');
-    $this->assertEqual(drupal_http_build_query(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo')), 'a[b]=2&a[c]=3&d=foo', 'Nested array was properly encoded.');
-  }
-
-  /**
    * Tests drupal_parse_url().
    */
   function testDrupalParseUrl() {
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
index b07d763..ac95d5b 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
@@ -9,7 +9,7 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Unicode;
-use Drupal\Component\Utility\UrlValidator;
+use Drupal\Component\Utility\Url;
 use Drupal\Component\Utility\Xss;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\Plugin\views\PluginBase;
@@ -221,7 +221,7 @@ public function sanitizeValue($value, $type = NULL) {
         $value = Xss::filterAdmin($value);
         break;
       case 'url':
-        $value = String::checkPlain(UrlValidator::stripDangerousProtocols($value));
+        $value = String::checkPlain(Url::stripDangerousProtocols($value));
         break;
       default:
         $value = String::checkPlain($value);
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php
index 386eda0..fda023a 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php
@@ -1365,7 +1365,8 @@ protected function renderAsLink($alter, $text, $tokens) {
 
     if (isset($url['query'])) {
       $path = strtr($path, array('?' . $url['query'] => ''));
-      $query = drupal_get_query_array($url['query']);
+      $query = array();
+      parse_str($url['query'], $query);
       // Remove query parameters that were assigned a query string replacement
       // token for which there is no value available.
       foreach ($query as $param => $val) {
@@ -1421,7 +1422,9 @@ protected function renderAsLink($alter, $text, $tokens) {
       // convert back to an array form for l().
       $options['query'] = drupal_http_build_query($alter['query']);
       $options['query'] = strtr($options['query'], $tokens);
-      $options['query'] = drupal_get_query_array($options['query']);
+      $query = array();
+      parse_str($options['query'], $query);
+      $options['query'] = $query;
     }
     if (isset($alter['alias'])) {
       // Alias is a boolean field, so no token.
diff --git a/core/tests/Drupal/Tests/Core/Common/UrlValidatorTest.php b/core/tests/Drupal/Tests/Component/Utility/UrlTest.php
similarity index 70%
rename from core/tests/Drupal/Tests/Core/Common/UrlValidatorTest.php
rename to core/tests/Drupal/Tests/Component/Utility/UrlTest.php
index 105d716..769a10d 100644
--- a/core/tests/Drupal/Tests/Core/Common/UrlValidatorTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/UrlTest.php
@@ -2,30 +2,63 @@
 
 /**
  * @file
- * Contains \Drupal\Tests\Core\Common\UrlValidatorTest.
+ * Contains \Drupal\Tests\Component\Utility\UrlTest.
  */
 
-namespace Drupal\Tests\Core\Common;
+namespace Drupal\Tests\Component\Utility;
 
+
+use Drupal\Component\Utility\Url;
 use Drupal\Component\Utility\String;
-use Drupal\Component\Utility\UrlValidator;
 use Drupal\Tests\UnitTestCase;
 
 /**
- * Tests URL validation by valid_url().
+ * Tests the http query methods.
+ *
+ * @see \Drupal\Component\Utility\Url
  */
-class UrlValidatorTest extends UnitTestCase {
+class UrlTest extends UnitTestCase {
 
   public static function getInfo() {
     return array(
-      'name' => 'URL validation',
-      'description' => 'Tests URL validation by valid_url()',
-      'group' => 'Common',
+      'name' => t('Url Tests'),
+      'description' => t('Tests the Url utility class.'),
+      'group' => t('Path API'),
+    );
+  }
+
+  /**
+   * Provides test data for testBuildQuery().
+   *
+   * @return array
+   */
+  public function providerTestBuildQuery() {
+    return array(
+      array(array('a' => ' &#//+%20@۞'), 'a=%20%26%23//%2B%2520%40%DB%9E', 'Value was properly encoded.'),
+      array(array(' &#//+%20@۞' => 'a'), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', 'Key was properly encoded.'),
+      array(array('a' => '1', 'b' => '2', 'c' => '3'), 'a=1&b=2&c=3', 'Multiple values were properly concatenated.'),
+      array(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo'), 'a[b]=2&a[c]=3&d=foo', 'Nested array was properly encoded.'),
     );
   }
 
   /**
-   * Data provider for absolute URLs.
+   * Tests Url::buildQuery().
+   *
+   * @param array $query
+   *   The array of query parameters.
+   * @param string $expected
+   *   The expected query string.
+   * @param string $message
+   *   The assertion message.
+   *
+   * @dataProvider providerTestBuildQuery
+   */
+  public function testBuildQuery($query, $expected, $message) {
+    $this->assertEquals(Url::buildQuery($query), $expected, $message);
+  }
+
+  /**
+   * Data provider for testValidAbsolute().
    */
   public function providerTestValidAbsoluteData() {
     $urls = array(
@@ -64,12 +97,12 @@ public function providerTestValidAbsoluteData() {
    */
   public function testValidAbsolute($url, $scheme) {
     $test_url = $scheme . '://' . $url;
-    $valid_url = UrlValidator::isValid($test_url, TRUE);
+    $valid_url = Url::isValid($test_url, TRUE);
     $this->assertTrue($valid_url, String::format('@url is a valid URL.', array('@url' => $test_url)));
   }
 
   /**
-   * Provides invalid absolute URLs.
+   * Provides data for testInvalidAbsolute().
    */
   public function providerTestInvalidAbsolute() {
     $data = array(
@@ -92,12 +125,12 @@ public function providerTestInvalidAbsolute() {
    */
   public function testInvalidAbsolute($url, $scheme) {
     $test_url = $scheme . '://' . $url;
-    $valid_url = UrlValidator::isValid($test_url, TRUE);
+    $valid_url = Url::isValid($test_url, TRUE);
     $this->assertFalse($valid_url, String::format('@url is NOT a valid URL.', array('@url' => $test_url)));
   }
 
   /**
-   * Provides valid relative URLs
+   * Provides data for testValidRelative().
    */
   public function providerTestValidRelativeData() {
     $data = array(
@@ -123,12 +156,12 @@ public function providerTestValidRelativeData() {
    */
   public function testValidRelative($url, $prefix) {
     $test_url = $prefix . $url;
-    $valid_url = Urlvalidator::isValid($test_url);
+    $valid_url = Url::isValid($test_url);
     $this->assertTrue($valid_url, String::format('@url is a valid URL.', array('@url' => $test_url)));
   }
 
   /**
-   * Provides invalid relative URLs.
+   * Provides data for testInvalidRelative().
    */
   public function providerTestInvalidRelativeData() {
     $data = array(
@@ -151,7 +184,7 @@ public function providerTestInvalidRelativeData() {
    */
   public function testInvalidRelative($url, $prefix) {
     $test_url = $prefix . $url;
-    $valid_url = UrlValidator::isValid($test_url);
+    $valid_url = Url::isValid($test_url);
     $this->assertFalse($valid_url, String::format('@url is NOT a valid URL.', array('@url' => $test_url)));
   }
 
@@ -195,5 +228,4 @@ protected function dataEnhanceWithPrefix(array $urls) {
     return $data;
   }
 
-
 }
diff --git a/core/tests/Drupal/Tests/Component/Utility/XssTest.php b/core/tests/Drupal/Tests/Component/Utility/XssTest.php
index e87cd79..2614c5d 100644
--- a/core/tests/Drupal/Tests/Component/Utility/XssTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/XssTest.php
@@ -8,7 +8,7 @@
 namespace Drupal\Tests\Component\Utility;
 
 use Drupal\Component\Utility\String;
-use Drupal\Component\Utility\UrlValidator;
+use Drupal\Component\Utility\Url;
 use Drupal\Component\Utility\Xss;
 use Drupal\Tests\UnitTestCase;
 
@@ -53,7 +53,7 @@ protected function setUp() {
       'webcal',
       'rtsp',
     );
-    UrlValidator::setAllowedProtocols($allowed_protocols);
+    Url::setAllowedProtocols($allowed_protocols);
   }
 
   /**
