Roll googleurl to the latest Chromium upstream

This updates up to Chromium commit c42ff2e06f1bec13abfa625ad629e67b24cf6a66
from Fri Oct 13 15:43:03 2023 +0000

Change-Id: I8bd5bc4438ffa05211e9218c6da906dfe2fb8fba
diff --git a/AUTHORS b/AUTHORS
index 6714ac1..2ae5e98 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -30,18 +30,22 @@
 Adam Bujalski <abujalski@gmail.com>
 Adam Kallai <kadam@inf.u-szeged.hu>
 Adam Labuda <a.labuda@samsung.com>
+Anguluri Aravind Kumar <ar.kumar@samsung.com>
 Adam Roben <adam@github.com>
 Adam Treat <adam.treat@samsung.com>
 Adam Yi <i@adamyi.com>
 Addanki Gandhi Kishor <kishor.ag@samsung.com>
 Adenilson Cavalcanti <a.cavalcanti@samsung.com>
 Aditi Singh <a20.singh@samsung.com>
+Aditya Agarwal <ad.agarwal@samsung.com>
 Aditya Bhargava <heuristicist@gmail.com>
+Aditya Sharma <a2.sharma@samsung.com>
 Adrian Belgun <adrian.belgun@intel.com>
 Adrian Ratiu <adrian.ratiu@collabora.corp-partner.google.com>
 Adrià Vilanova Martínez <me@avm99963.com>
 Ahmed Elwasefi <a.m.elwasefi@gmail.com>
 Ahmet Emir Ercin <ahmetemiremir@gmail.com>
+Aidarbek Suleimenov <suleimenov.aidarbek@gmail.com>
 Aiden Grossman <aidengrossmanpso@gmail.com>
 Ajay Berwal <a.berwal@samsung.com>
 Ajay Berwal <ajay.berwal@samsung.com>
@@ -323,6 +327,7 @@
 Debadree Chatterjee <debadree333@gmail.com>
 Debashish Samantaray <d.samantaray@samsung.com>
 Debug Wang <debugwang@tencent.com>
+Deep Shah <deep.shah@samsung.com>
 Deepak Dilip Borade <deepak.db@samsung.com>
 Deepak Mittal <deepak.m1@samsung.com>
 Deepak Mohan <hop2deep@gmail.com>
@@ -359,6 +364,7 @@
 Donna Wu <donna.wu@intel.com>
 Douglas F. Turner <doug.turner@gmail.com>
 Drew Blaisdell <drew.blaisdell@gmail.com>
+Dushyant Kant Sharma <dush.sharma@samsung.com>
 Dustin Doloff <doloffd@amazon.com>
 Ebrahim Byagowi <ebrahim@gnu.org>
 Ebrahim Byagowi <ebraminio@gmail.com>
@@ -441,6 +447,7 @@
 Girish Kumar M <mck.giri@samsung.com>
 Gitanshu Mehndiratta <g.mehndiratt@samsung.com>
 Giuseppe Iuculano <giuseppe@iuculano.it>
+Gloam <gaoqingguang@kuaishou.com>
 Gnanasekar Somanathan <gnanasekar.s@samsung.com>
 Gordana Cmiljanovic <gordana.cmiljanovic@imgtec.com>
 Goutham Jagannatha <wrm364@motorola.com>
@@ -471,12 +478,14 @@
 Harshit Pal <harshitp12345@gmail.com>
 Hassan Salehe Matar <hassansalehe@gmail.com>
 Hautio Kari <khautio@gmail.com>
+He Qi <heqi899@gmail.com>
 Heejin R. Chung <heejin.r.chung@samsung.com>
 Heeyoun Lee <heeyoun.lee@samsung.com>
 Henrique de Carvalho <decarv.henrique@gmail.com>
 Henrique Limas <henrique.ramos.limas@gmail.com>
 Henry Lim <henry@limhenry.xyz>
 Hikari Fujimoto <hikari.p.fujimoto@gmail.com>
+Himadri Agrawal <h2.agrawal@samsung.com>
 Himanshu Joshi <h.joshi@samsung.com>
 Himanshu Nayak <himanshu.nayak@amd.corp-partner.google.com>
 Hiroki Oshima <hiroki.oshima@gmail.com>
@@ -547,6 +556,7 @@
 Jaewon Choi <jaewon.james.choi@gmail.com>
 Jaewon Jung <jw.jung@navercorp.com>
 Jaeyong Bae <jdragon.bae@gmail.com>
+Jagadesh P <jagadeshjai1999@gmail.com>
 Jagdish Chourasia <jagdish.c@samsung.com>
 Jaime Soriano Pastor <jsorianopastor@gmail.com>
 Jake Helfert <jake@helfert.us>
@@ -772,6 +782,7 @@
 Lauri Oherd <lauri.oherd@gmail.com>
 Lavar Askew <open.hyperion@gmail.com>
 Le Hoang Quyen <le.hoang.q@gmail.com>
+Leena Kaushik <l1.kaushik@samsung.com>
 Legend Lee <guanxian.li@intel.com>
 Leith Bade <leith@leithalweapon.geek.nz>
 Lei Gao <leigao@huawei.com>
@@ -874,6 +885,7 @@
 Matthias Reitinger <reimarvin@gmail.com>
 Matthieu Rigolot <matthieu.rigolot@gmail.com>
 Matthieu Vlad Hauglustaine <matt.hauglustaine@gmail.com>
+Max Coplan <mchcopl@gmail.com>
 Max Karolinskiy <max@brave.com>
 Max Perepelitsyn <pph34r@gmail.com>
 Max Schmitt <max@schmitt.mx>
@@ -945,6 +957,7 @@
 Nagarjuna Atluri <nagarjuna.a@samsung.com>
 Naiem Shaik <naiem.shaik@gmail.com>
 Naman Kumar Narula <namankumarnarula@gmail.com>
+Naman Yadav <naman.yadav@samsung.com>
 Naoki Takano <takano.naoki@gmail.com>
 Naoto Ono <onoto1998@gmail.com>
 Nathan Mitchell <nathaniel.v.mitchell@gmail.com>
@@ -980,6 +993,7 @@
 Olivier Tilloy <olivier+chromium@tilloy.net>
 Olli Raula (Old name Olli Syrjälä) <olli.raula@intel.com>
 Omar Sandoval <osandov@osandov.com>
+Orko Garai <orko.garai@gmail.com>
 Owen Shaw <owenpshaw@gmail.com>
 Owen Yuwono <owenyuwono@gmail.com>
 Palash Verma <palashverma47@gmail.com>
@@ -1047,6 +1061,7 @@
 Preeti Nayak <preeti.nayak@samsung.com>
 Pritam Nikam <pritam.nikam@samsung.com>
 Puttaraju R <puttaraju.r@samsung.com>
+Punith Nayak <npunith125@gmail.com>
 Qi Tiezheng <qitiezheng@360.cn>
 Qi Yang <qi1988.yang@samsung.com>
 Qiang Zeng <zengqiang1@huawei.com>
@@ -1074,6 +1089,7 @@
 Ravi Nanjundappa <nravi.n@samsung.com>
 Ravi Phaneendra Kasibhatla <r.kasibhatla@samsung.com>
 Ravi Phaneendra Kasibhatla <ravi.kasibhatla@motorola.com>
+Ravindra Kumar <ravindra.k2@samsung.com>
 Raviraj Sitaram <raviraj.p.sitaram@intel.com>
 Rebecca Chang Swee Fun <rebecca.chang@starfivetech.com>
 Reda Tawfik <redatawfik@noogler.google.com>
@@ -1130,6 +1146,7 @@
 Saikrishna Arcot <saiarcot895@gmail.com>
 Sajal Khandelwal <skhandelwa22@bloomberg.net>
 Sajeesh Sidharthan <sajeesh.sidharthan@amd.corp-partner.google.com>
+Sakib Shabir <s1.tantray@samsung.com>
 Saksham Mittal <gotlouemail@gmail.com>
 Salvatore Iovene <salvatore.iovene@intel.com>
 Sam James <sam@gentoo.org>
@@ -1203,10 +1220,12 @@
 Shirish S <shirish.s@amd.com>
 Shiva Kumar <shiva.k1@samsung.com>
 Shivakumar JM <shiva.jm@samsung.com>
+Shiyi Zou <shiyi.zou@intel.com>
 Shobhit Goel <shobhit.goel@samsung.com>
 Shouqun Liu <liushouqun@xiaomi.com>
 Shouqun Liu <shouqun.liu@intel.com>
 Shreeram Kushwaha <shreeram.k@samsung.com>
+Shrey Patel <shrey1patel2@gmail.com>
 Shreyas Gopal <shreyas.g@samsung.com>
 Shreyas VA <v.a.shreyas@gmail.com>
 Shubham Agrawal <shubag@amazon.com>
@@ -1217,10 +1236,12 @@
 Siddharth Shankar <funkysidd@gmail.com>
 Simeon Kuran <simeon.kuran@gmail.com>
 Simon Arlott <simon.arlott@gmail.com>
+Simon Cadman <simon@cadman.uk>
 Simon Jackson <simon.jackson@sonocent.com>
 Simon La Macchia <smacchia@amazon.com>
 Siva Kumar Gunturi <siva.gunturi@samsung.com>
 Slava Aseev <nullptrnine@gmail.com>
+Smriti Singh <s01.singh@samsung.com>
 Sohom Datta <sohom.datta@learner.manipal.edu>
 Sohom Datta <dattasohom1@gmail.com>
 Song Fangzhen <songfangzhen@bytedance.com>
@@ -1576,5 +1597,6 @@
 Whist Technologies <*@whist.com>
 Xperi Corporation <*@xperi.com>
 Yandex LLC <*@yandex-team.ru>
+Zoho Corporation <*@zohocorp.com>
 # Please DO NOT APPEND here. See comments at the top of the file.
 # END organizations section.
diff --git a/base/compiler_specific.h b/base/compiler_specific.h
index e85da5c..5f7c31e 100644
--- a/base/compiler_specific.h
+++ b/base/compiler_specific.h
@@ -374,6 +374,20 @@
 #define TRIVIAL_ABI
 #endif
 
+// Detect whether a type is trivially relocatable, ie. a move-and-destroy
+// sequence can replaced with memmove(). This can be used to optimise the
+// implementation of containers. This is automatically true for types that were
+// defined with TRIVIAL_ABI such as scoped_refptr.
+//
+// See also:
+//   https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p1144r8.html
+//   https://clang.llvm.org/docs/LanguageExtensions.html#:~:text=__is_trivially_relocatable
+#if defined(__clang__) && HAS_BUILTIN(__is_trivially_relocatable)
+#define IS_TRIVIALLY_RELOCATABLE(t) __is_trivially_relocatable(t)
+#else
+#define IS_TRIVIALLY_RELOCATABLE(t) false
+#endif
+
 // Marks a member function as reinitializing a moved-from variable.
 // See also
 // https://clang.llvm.org/extra/clang-tidy/checks/bugprone-use-after-move.html#reinitialization
diff --git a/base/containers/contiguous_iterator.h b/base/containers/contiguous_iterator.h
index 7f06432..8311b50 100644
--- a/base/containers/contiguous_iterator.h
+++ b/base/containers/contiguous_iterator.h
@@ -40,8 +40,13 @@
 // `static_assert(is_trivial_v<value_type>)` inside libc++'s std::basic_string.
 template <typename T>
 struct IsStringIter
-    : std::conjunction<std::is_trivial<iter_value_t<T>>, IsStringIterImpl<T>> {
-};
+    : std::conjunction<
+          std::disjunction<std::is_same<iter_value_t<T>, char>,
+                           std::is_same<iter_value_t<T>, wchar_t>,
+
+                           std::is_same<iter_value_t<T>, char16_t>,
+                           std::is_same<iter_value_t<T>, char32_t>>,
+          IsStringIterImpl<T>> {};
 
 // An iterator to std::array is contiguous.
 // Reference: https://wg21.link/array.overview#1
diff --git a/base/containers/span.h b/base/containers/span.h
index 83069d6..09f6b6f 100644
--- a/base/containers/span.h
+++ b/base/containers/span.h
@@ -27,7 +27,9 @@
 // [views.constants]
 constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max();
 
-template <typename T, size_t Extent = dynamic_extent>
+template <typename T,
+          size_t Extent = dynamic_extent,
+          typename InternalPtrType = T*>
 class span;
 
 namespace internal {
@@ -233,7 +235,7 @@
 // appropriate make_span() utility functions are provided.
 
 // [span], class template span
-template <typename T, size_t Extent>
+template <typename T, size_t Extent, typename InternalPtrType>
 class GSL_POINTER span : public internal::ExtentStorage<Extent> {
  private:
   using ExtentStorage = internal::ExtentStorage<Extent>;
@@ -434,11 +436,11 @@
 
   // [span.iter], span iterator support
   constexpr iterator begin() const noexcept {
-    return iterator(data_, data_ + size());
+    return iterator(data(), data() + size());
   }
 
   constexpr iterator end() const noexcept {
-    return iterator(data_, data_ + size(), data_ + size());
+    return iterator(data(), data() + size(), data() + size());
   }
 
   constexpr reverse_iterator rbegin() const noexcept {
@@ -452,13 +454,38 @@
  private:
   // This field is not a raw_ptr<> because it was filtered by the rewriter
   // for: #constexpr-ctor-field-initializer, #global-scope, #union
-  RAW_PTR_EXCLUSION T* data_;
+  InternalPtrType data_;
 };
 
 // span<T, Extent>::extent can not be declared inline prior to C++17, hence this
 // definition is required.
-template <class T, size_t Extent>
-constexpr size_t span<T, Extent>::extent;
+template <class T, size_t Extent, typename InternalPtrType>
+constexpr size_t span<T, Extent, InternalPtrType>::extent;
+
+template <typename It,
+          typename T = std::remove_reference_t<iter_reference_t<It>>>
+span(It, StrictNumeric<size_t>) -> span<T>;
+
+template <typename It,
+          typename End,
+          typename = std::enable_if_t<!std::is_convertible_v<End, size_t>>,
+          typename T = std::remove_reference_t<iter_reference_t<It>>>
+span(It, End) -> span<T>;
+
+template <typename T, size_t N>
+span(T (&)[N]) -> span<T, N>;
+
+template <typename T, size_t N>
+span(std::array<T, N>&) -> span<T, N>;
+
+template <typename T, size_t N>
+span(const std::array<T, N>&) -> span<const T, N>;
+
+template <typename Container,
+          typename T = std::remove_pointer_t<
+              decltype(std::data(std::declval<Container>()))>,
+          size_t X = internal::Extent<Container>::value>
+span(Container&&) -> span<T, X>;
 
 // [span.objectrep], views of object representation
 template <typename T, size_t X>
diff --git a/base/memory/raw_ptr_exclusion.h b/base/memory/raw_ptr_exclusion.h
index 3ce1d60..e4d355d 100644
--- a/base/memory/raw_ptr_exclusion.h
+++ b/base/memory/raw_ptr_exclusion.h
@@ -8,6 +8,6 @@
 // Although `raw_ptr` is part of the standalone PA distribution, it is
 // easier to use the shorter path in `//base/memory`. We retain this
 // facade header for ease of typing.
-#include "base/allocator/partition_allocator/pointers/raw_ptr_exclusion.h"  // IWYU pragma: export
+#include "base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_exclusion.h"  // IWYU pragma: export
 
 #endif  // BASE_MEMORY_RAW_PTR_EXCLUSION_H_
diff --git a/base/numerics/safe_conversions.h b/base/numerics/safe_conversions.h
index 3e04bf4..7f12916 100644
--- a/base/numerics/safe_conversions.h
+++ b/base/numerics/safe_conversions.h
@@ -357,8 +357,18 @@
 using SizeT = StrictNumeric<size_t>;
 
 // floating -> integral conversions that saturate and thus can actually return
-// an integral type.  In most cases, these should be preferred over the std::
-// versions.
+// an integral type.
+//
+// Generally, what you want is saturated_cast<Dst>(std::nearbyint(x)), which
+// rounds correctly according to IEEE-754 (round to nearest, ties go to nearest
+// even number; this avoids bias). If your code is performance-critical
+// and you are sure that you will never overflow, you can use std::lrint()
+// or std::llrint(), which return a long or long long directly.
+//
+// Below are convenience functions around similar patterns, except that
+// they round in nonstandard directions and will generally be slower.
+
+// Rounds towards negative infinity (i.e., down).
 template <typename Dst = int,
           typename Src,
           typename = std::enable_if_t<std::is_integral<Dst>::value &&
@@ -366,6 +376,8 @@
 Dst ClampFloor(Src value) {
   return saturated_cast<Dst>(std::floor(value));
 }
+
+// Rounds towards positive infinity (i.e., up).
 template <typename Dst = int,
           typename Src,
           typename = std::enable_if_t<std::is_integral<Dst>::value &&
@@ -373,13 +385,22 @@
 Dst ClampCeil(Src value) {
   return saturated_cast<Dst>(std::ceil(value));
 }
+
+// Rounds towards nearest integer, with ties away from zero.
+// This means that 0.5 will be rounded to 1 and 1.5 will be rounded to 2.
+// Similarly, -0.5 will be rounded to -1 and -1.5 will be rounded to -2.
+//
+// This is normally not what you want accuracy-wise (it introduces a small bias
+// away from zero), and it is not the fastest option, but it is frequently what
+// existing code expects. Compare with saturated_cast<Dst>(std::nearbyint(x))
+// or std::lrint(x), which would round 0.5 and -0.5 to 0 but 1.5 to 2 and
+// -1.5 to -2.
 template <typename Dst = int,
           typename Src,
           typename = std::enable_if_t<std::is_integral<Dst>::value &&
                                       std::is_floating_point<Src>::value>>
 Dst ClampRound(Src value) {
-  const Src rounded =
-      (value >= 0.0f) ? std::floor(value + 0.5f) : std::ceil(value - 0.5f);
+  const Src rounded = std::round(value);
   return saturated_cast<Dst>(rounded);
 }
 
diff --git a/base/strings/safe_sprintf_unittest.cc b/base/strings/safe_sprintf_unittest.cc
index af52d1c..9086e52 100644
--- a/base/strings/safe_sprintf_unittest.cc
+++ b/base/strings/safe_sprintf_unittest.cc
@@ -12,7 +12,7 @@
 #include <limits>
 #include <memory>
 
-
+#include "base/allocator/partition_allocator/src/partition_alloc/partition_alloc_config.h"
 #include "polyfills/base/check_op.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/base/strings/string_piece_unittest.cc b/base/strings/string_piece_unittest.cc
index a6fcbbb..30819bb 100644
--- a/base/strings/string_piece_unittest.cc
+++ b/base/strings/string_piece_unittest.cc
@@ -684,7 +684,14 @@
   }
 }
 
-#if defined(_LIBCPP_ENABLE_ASSERTIONS)
+// Chromium development assumes StringPiece (which is std::string_view) is
+// implemented with an STL that enables hardening checks. We treat bugs that
+// trigger one of these conditions as functional rather than security bugs. If
+// this test fails on some embedder, it should not be disabled. Instead, the
+// embedder should fix their STL or build configuration to enable corresponding
+// hardening checks.
+//
+// See https://chromium.googlesource.com/chromium/src/+/main/docs/security/faq.md#indexing-a-container-out-of-bounds-hits-a-libcpp_verbose_abort_is-this-a-security-bug
 TEST(StringPieceTest, OutOfBoundsDeath) {
   {
     constexpr StringPiece piece;
@@ -726,7 +733,6 @@
   int length = -1;
   ASSERT_DEATH_IF_SUPPORTED({ StringPiece piece("hello", length); }, "");
 }
-#endif  // defined(_LIBCPP_ENABLE_ASSERTIONS)
 
 TEST(StringPieceTest, ConstexprData) {
   {
diff --git a/base/strings/stringprintf.cc b/base/strings/stringprintf.cc
index 1913fdc..6720744 100644
--- a/base/strings/stringprintf.cc
+++ b/base/strings/stringprintf.cc
@@ -16,100 +16,6 @@
 
 namespace gurl_base {
 
-namespace {
-
-// Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter
-// is the size of the buffer. These return the number of characters in the
-// formatted string excluding the NUL terminator. If the buffer is not
-// large enough to accommodate the formatted string without truncation, they
-// return the number of characters that would be in the fully-formatted string
-// (vsnprintf, and vswprintf on Windows), or -1 (vswprintf on POSIX platforms).
-inline int vsnprintfT(char* buffer,
-                      size_t buf_size,
-                      const char* format,
-                      va_list argptr) {
-  return gurl_base::vsnprintf(buffer, buf_size, format, argptr);
-}
-
-#if BUILDFLAG(IS_WIN)
-inline int vsnprintfT(wchar_t* buffer,
-                      size_t buf_size,
-                      const wchar_t* format,
-                      va_list argptr) {
-  return gurl_base::vswprintf(buffer, buf_size, format, argptr);
-}
-#endif
-
-// Templatized backend for StringPrintF/StringAppendF. This does not finalize
-// the va_list, the caller is expected to do that.
-template <class CharT>
-static void StringAppendVT(std::basic_string<CharT>* dst,
-                           const CharT* format,
-                           va_list ap) {
-  // First try with a small fixed size buffer.
-  // This buffer size should be kept in sync with StringUtilTest.GrowBoundary
-  // and StringUtilTest.StringPrintfBounds.
-  CharT stack_buf[1024];
-
-  va_list ap_copy;
-  va_copy(ap_copy, ap);
-
-  gurl_base::ScopedClearLastError last_error;
-  int result = vsnprintfT(stack_buf, std::size(stack_buf), format, ap_copy);
-  va_end(ap_copy);
-
-  if (result >= 0 && static_cast<size_t>(result) < std::size(stack_buf)) {
-    // It fit.
-    dst->append(stack_buf, static_cast<size_t>(result));
-    return;
-  }
-
-  // Repeatedly increase buffer size until it fits.
-  size_t mem_length = std::size(stack_buf);
-  while (true) {
-    if (result < 0) {
-#if BUILDFLAG(IS_WIN)
-      // On Windows, vsnprintfT always returns the number of characters in a
-      // fully-formatted string, so if we reach this point, something else is
-      // wrong and no amount of buffer-doubling is going to fix it.
-      return;
-#else
-      if (errno != 0 && errno != EOVERFLOW)
-        return;
-      // Try doubling the buffer size.
-      mem_length *= 2;
-#endif
-    } else {
-      // We need exactly "result + 1" characters.
-      mem_length = static_cast<size_t>(result) + 1;
-    }
-
-    if (mem_length > 32 * 1024 * 1024) {
-      // That should be plenty, don't try anything larger.  This protects
-      // against huge allocations when using vsnprintfT implementations that
-      // return -1 for reasons other than overflow without setting errno.
-      GURL_DLOG(WARNING) << "Unable to printf the requested string due to size.";
-      return;
-    }
-
-    std::vector<CharT> mem_buf(mem_length);
-
-    // NOTE: You can only use a va_list once.  Since we're in a while loop, we
-    // need to make a new copy each time so we don't use up the original.
-    va_copy(ap_copy, ap);
-    result = vsnprintfT(&mem_buf[0], mem_length, format, ap_copy);
-    va_end(ap_copy);
-
-    if ((result >= 0) && (static_cast<size_t>(result) < mem_length)) {
-      // It fit.
-      dst->append(&mem_buf[0], static_cast<size_t>(result));
-      return;
-    }
-  }
-}
-
-}  // namespace
-
 std::string StringPrintf(const char* format, ...) {
   va_list ap;
   va_start(ap, format);
@@ -119,17 +25,6 @@
   return result;
 }
 
-#if BUILDFLAG(IS_WIN)
-std::wstring StringPrintf(const wchar_t* format, ...) {
-  va_list ap;
-  va_start(ap, format);
-  std::wstring result;
-  StringAppendV(&result, format, ap);
-  va_end(ap);
-  return result;
-}
-#endif
-
 std::string StringPrintV(const char* format, va_list ap) {
   std::string result;
   StringAppendV(&result, format, ap);
@@ -143,23 +38,68 @@
   va_end(ap);
 }
 
-#if BUILDFLAG(IS_WIN)
-void StringAppendF(std::wstring* dst, const wchar_t* format, ...) {
-  va_list ap;
-  va_start(ap, format);
-  StringAppendV(dst, format, ap);
-  va_end(ap);
-}
-#endif
-
 void StringAppendV(std::string* dst, const char* format, va_list ap) {
-  StringAppendVT(dst, format, ap);
-}
+  // First try with a small fixed size buffer.
+  // This buffer size should be kept in sync with StringUtilTest.GrowBoundary
+  // and StringUtilTest.StringPrintfBounds.
+  char stack_buf[1024];
 
+  va_list ap_copy;
+  va_copy(ap_copy, ap);
+
+  gurl_base::ScopedClearLastError last_error;
+  int result = vsnprintf(stack_buf, std::size(stack_buf), format, ap_copy);
+  va_end(ap_copy);
+
+  if (result >= 0 && static_cast<size_t>(result) < std::size(stack_buf)) {
+    // It fit.
+    dst->append(stack_buf, static_cast<size_t>(result));
+    return;
+  }
+
+  // Repeatedly increase buffer size until it fits.
+  size_t mem_length = std::size(stack_buf);
+  while (true) {
+    if (result < 0) {
 #if BUILDFLAG(IS_WIN)
-void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) {
-  StringAppendVT(dst, format, ap);
-}
+      // On Windows, vsnprintf always returns the number of characters in a
+      // fully-formatted string, so if we reach this point, something else is
+      // wrong and no amount of buffer-doubling is going to fix it.
+      return;
+#else
+      if (errno != 0 && errno != EOVERFLOW) {
+        return;
+      }
+      // Try doubling the buffer size.
+      mem_length *= 2;
 #endif
+    } else {
+      // We need exactly "result + 1" characters.
+      mem_length = static_cast<size_t>(result) + 1;
+    }
+
+    if (mem_length > 32 * 1024 * 1024) {
+      // That should be plenty, don't try anything larger.  This protects
+      // against huge allocations when using vsnprintf implementations that
+      // return -1 for reasons other than overflow without setting errno.
+      GURL_DLOG(WARNING) << "Unable to printf the requested string due to size.";
+      return;
+    }
+
+    std::vector<char> mem_buf(mem_length);
+
+    // NOTE: You can only use a va_list once.  Since we're in a while loop, we
+    // need to make a new copy each time so we don't use up the original.
+    va_copy(ap_copy, ap);
+    result = vsnprintf(&mem_buf[0], mem_length, format, ap_copy);
+    va_end(ap_copy);
+
+    if ((result >= 0) && (static_cast<size_t>(result) < mem_length)) {
+      // It fit.
+      dst->append(&mem_buf[0], static_cast<size_t>(result));
+      return;
+    }
+  }
+}
 
 }  // namespace base
diff --git a/base/strings/stringprintf.h b/base/strings/stringprintf.h
index 5266a13..99d04e8 100644
--- a/base/strings/stringprintf.h
+++ b/base/strings/stringprintf.h
@@ -11,20 +11,12 @@
 
 #include "polyfills/base/base_export.h"
 #include "base/compiler_specific.h"
-#include "build/build_config.h"
 
 namespace gurl_base {
 
 // Return a C++ string given printf-like input.
 [[nodiscard]] BASE_EXPORT std::string StringPrintf(const char* format, ...)
     PRINTF_FORMAT(1, 2);
-#if BUILDFLAG(IS_WIN)
-// Note: Unfortunately compile time checking of the format string for UTF-16
-// strings is not supported by any compiler, thus these functions should be used
-// carefully and sparingly. Also applies to StringAppendV below.
-[[nodiscard]] BASE_EXPORT std::wstring StringPrintf(const wchar_t* format, ...)
-    WPRINTF_FORMAT(1, 2);
-#endif
 
 // Return a C++ string given vprintf-like input.
 [[nodiscard]] BASE_EXPORT std::string StringPrintV(const char* format,
@@ -34,20 +26,11 @@
 // Append result to a supplied string.
 BASE_EXPORT void StringAppendF(std::string* dst, const char* format, ...)
     PRINTF_FORMAT(2, 3);
-#if BUILDFLAG(IS_WIN)
-BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...)
-    WPRINTF_FORMAT(2, 3);
-#endif
 
 // Lower-level routine that takes a va_list and appends to a specified
 // string.  All other routines are just convenience wrappers around it.
 BASE_EXPORT void StringAppendV(std::string* dst, const char* format, va_list ap)
     PRINTF_FORMAT(2, 0);
-#if BUILDFLAG(IS_WIN)
-BASE_EXPORT void StringAppendV(std::wstring* dst,
-                               const wchar_t* format,
-                               va_list ap) WPRINTF_FORMAT(2, 0);
-#endif
 
 }  // namespace base
 
diff --git a/base/strings/stringprintf_unittest.cc b/base/strings/stringprintf_unittest.cc
index 45fa9c6..981c45c 100644
--- a/base/strings/stringprintf_unittest.cc
+++ b/base/strings/stringprintf_unittest.cc
@@ -35,45 +35,24 @@
 
 TEST(StringPrintfTest, StringPrintfMisc) {
   EXPECT_EQ("123hello w", StringPrintf("%3d%2s %1c", 123, "hello", 'w'));
-#if BUILDFLAG(IS_WIN)
-  EXPECT_EQ(L"123hello w", StringPrintf(L"%3d%2ls %1lc", 123, L"hello", 'w'));
-#endif
 }
 
 TEST(StringPrintfTest, StringAppendfEmptyString) {
   std::string value("Hello");
   StringAppendF(&value, "%s", "");
   EXPECT_EQ("Hello", value);
-
-#if BUILDFLAG(IS_WIN)
-  std::wstring valuew(L"Hello");
-  StringAppendF(&valuew, L"%ls", L"");
-  EXPECT_EQ(L"Hello", valuew);
-#endif
 }
 
 TEST(StringPrintfTest, StringAppendfString) {
   std::string value("Hello");
   StringAppendF(&value, " %s", "World");
   EXPECT_EQ("Hello World", value);
-
-#if BUILDFLAG(IS_WIN)
-  std::wstring valuew(L"Hello");
-  StringAppendF(&valuew, L" %ls", L"World");
-  EXPECT_EQ(L"Hello World", valuew);
-#endif
 }
 
 TEST(StringPrintfTest, StringAppendfInt) {
   std::string value("Hello");
   StringAppendF(&value, " %d", 123);
   EXPECT_EQ("Hello 123", value);
-
-#if BUILDFLAG(IS_WIN)
-  std::wstring valuew(L"Hello");
-  StringAppendF(&valuew, L" %d", 123);
-  EXPECT_EQ(L"Hello 123", valuew);
-#endif
 }
 
 // Make sure that lengths exactly around the initial buffer size are handled
@@ -93,11 +72,6 @@
     src[kSrcLen - i] = 0;
     std::string out;
     EXPECT_EQ(src, StringPrintf("%s", src));
-
-#if BUILDFLAG(IS_WIN)
-    srcw[kSrcLen - i] = 0;
-    EXPECT_EQ(srcw, StringPrintf(L"%ls", srcw));
-#endif
   }
 }
 
@@ -126,12 +100,6 @@
   std::string out;
   StringAppendVTestHelper(&out, "%d foo %s", 1, "bar");
   EXPECT_EQ("1 foo bar", out);
-
-#if BUILDFLAG(IS_WIN)
-  std::wstring outw;
-  StringAppendVTestHelper(&outw, L"%d foo %ls", 1, L"bar");
-  EXPECT_EQ(L"1 foo bar", outw);
-#endif
 }
 
 // Test the boundary condition for the size of the string_util's
@@ -150,16 +118,6 @@
   EXPECT_EQ(src, StringPrintf("%s", src));
 }
 
-#if BUILDFLAG(IS_WIN)
-TEST(StringPrintfTest, Invalid) {
-  wchar_t invalid[2];
-  invalid[0] = 0xffff;
-  invalid[1] = 0;
-
-  EXPECT_EQ(invalid, StringPrintf(L"%ls", invalid));
-}
-#endif
-
 // Test that StringPrintf and StringAppendV do not change errno.
 TEST(StringPrintfTest, StringPrintfErrno) {
   errno = 1;
diff --git a/base/strings/sys_string_conversions_unittest.cc b/base/strings/sys_string_conversions_unittest.cc
index 0e3dfa1..5d898aa 100644
--- a/base/strings/sys_string_conversions_unittest.cc
+++ b/base/strings/sys_string_conversions_unittest.cc
@@ -25,7 +25,8 @@
   EXPECT_EQ("Hello, world", SysWideToUTF8(L"Hello, world"));
   EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToUTF8(L"\x4f60\x597d"));
 
-  // >16 bits
+  // A value outside of the BMP and therefore not representable with one UTF-16
+  // code unit.
   EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToUTF8(kSysWideOldItalicLetterA));
 
   // Error case. When Windows finds a UTF-16 character going off the end of
@@ -52,7 +53,9 @@
 TEST(SysStrings, SysUTF8ToWide) {
   EXPECT_EQ(L"Hello, world", SysUTF8ToWide("Hello, world"));
   EXPECT_EQ(L"\x4f60\x597d", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5\xbd"));
-  // >16 bits
+
+  // A value outside of the BMP and therefore not representable with one UTF-16
+  // code unit.
   EXPECT_EQ(kSysWideOldItalicLetterA, SysUTF8ToWide("\xF0\x90\x8C\x80"));
 
   // Error case. When Windows finds an invalid UTF-8 character, it just skips
@@ -83,7 +86,8 @@
   EXPECT_EQ("Hello, world", SysWideToNativeMB(L"Hello, world"));
   EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToNativeMB(L"\x4f60\x597d"));
 
-  // >16 bits
+  // A value outside of the BMP and therefore not representable with one UTF-16
+  // code unit.
   EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToNativeMB(kSysWideOldItalicLetterA));
 
   // Error case. When Windows finds a UTF-16 character going off the end of
@@ -114,7 +118,9 @@
 #endif
   EXPECT_EQ(L"Hello, world", SysNativeMBToWide("Hello, world"));
   EXPECT_EQ(L"\x4f60\x597d", SysNativeMBToWide("\xe4\xbd\xa0\xe5\xa5\xbd"));
-  // >16 bits
+
+  // A value outside of the BMP and therefore not representable with one UTF-16
+  // code unit.
   EXPECT_EQ(kSysWideOldItalicLetterA, SysNativeMBToWide("\xF0\x90\x8C\x80"));
 
   // Error case. When Windows finds an invalid UTF-8 character, it just skips
diff --git a/base/strings/utf_string_conversion_utils.cc b/base/strings/utf_string_conversion_utils.cc
index 149a6ee..d7bbe62 100644
--- a/base/strings/utf_string_conversion_utils.cc
+++ b/base/strings/utf_string_conversion_utils.cc
@@ -11,13 +11,13 @@
 
 // CountUnicodeCharacters ------------------------------------------------------
 
-absl::optional<size_t> CountUnicodeCharacters(const char16_t* src,
-                                              size_t src_len,
+absl::optional<size_t> CountUnicodeCharacters(std::string_view text,
                                               size_t limit) {
   base_icu::UChar32 unused = 0;
   size_t count = 0;
-  for (size_t index = 0; count < limit && index < src_len; ++count, ++index) {
-    if (!ReadUnicodeCharacter(src, src_len, &index, &unused)) {
+  for (size_t index = 0; count < limit && index < text.size();
+       ++count, ++index) {
+    if (!ReadUnicodeCharacter(text.data(), text.size(), &index, &unused)) {
       return absl::nullopt;
     }
   }
diff --git a/base/strings/utf_string_conversion_utils.h b/base/strings/utf_string_conversion_utils.h
index 638a59f..183a7a6 100644
--- a/base/strings/utf_string_conversion_utils.h
+++ b/base/strings/utf_string_conversion_utils.h
@@ -11,7 +11,9 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <limits>
 #include <string>
+#include <string_view>
 
 #include "polyfills/base/base_export.h"
 #include "base/third_party/icu/icu_utf.h"
@@ -43,10 +45,9 @@
 // CountUnicodeCharacters ------------------------------------------------------
 
 // Returns the number of Unicode characters in `text`, up to the supplied
-// `limit`, if `text` contains valid UTF-16. Returns `nullopt` otherwise.
+// `limit`, if `text` contains valid UTF-8. Returns `nullopt` otherwise.
 BASE_EXPORT absl::optional<size_t> CountUnicodeCharacters(
-    const char16_t* src,
-    size_t src_len,
+    std::string_view text,
     size_t limit = std::numeric_limits<size_t>::max());
 
 // ReadUnicodeCharacter --------------------------------------------------------
diff --git a/base/strings/utf_string_conversion_utils_unittest.cc b/base/strings/utf_string_conversion_utils_unittest.cc
index 4e700dc..10f9201 100644
--- a/base/strings/utf_string_conversion_utils_unittest.cc
+++ b/base/strings/utf_string_conversion_utils_unittest.cc
@@ -12,19 +12,24 @@
 namespace gurl_base {
 
 TEST(UtfStringConversionUtilsTest, CountUnicodeCharacters) {
-  struct TestCase {
-    std::u16string value;
+  const struct TestCase {
+    std::string value;
     size_t limit;
     absl::optional<size_t> count;
   } test_cases[] = {
-      {u"", 0, 0},           {u"abc", 1, 1},
-      {u"abc", 3, 3},        {u"abc", 0, 0},
-      {u"abc", 4, 3},        {u"abc\U0001F4A9", 4, 4},
-      {u"\U0001F4A9", 1, 1}, {{1, 0xD801u}, 5, absl::nullopt},
+      {"", 0, 0},
+      {"abc", 1, 1},
+      {"abc", 3, 3},
+      {"abc", 0, 0},
+      {"abc", 4, 3},
+      // The casts and u8 string literals are needed here so that we don't
+      // trigger linter errors about invalid ascii values.
+      {reinterpret_cast<const char*>(u8"abc\U0001F4A9"), 4, 4},
+      {reinterpret_cast<const char*>(u8"\U0001F4A9"), 1, 1},
+      {{1, static_cast<char>(-1)}, 5, absl::nullopt},
   };
   for (const auto& test_case : test_cases) {
-    EXPECT_EQ(CountUnicodeCharacters(test_case.value.data(),
-                                     test_case.value.length(), test_case.limit),
+    EXPECT_EQ(CountUnicodeCharacters(test_case.value, test_case.limit),
               test_case.count);
   }
 }
diff --git a/base/strings/utf_string_conversions.h b/base/strings/utf_string_conversions.h
index 1ee702c..d7f1ce7 100644
--- a/base/strings/utf_string_conversions.h
+++ b/base/strings/utf_string_conversions.h
@@ -67,27 +67,24 @@
 
 // The conversion functions in this file should not be used to convert string
 // literals. Instead, the corresponding prefixes (e.g. u"" for UTF16 or L"" for
-// Wide) should be used. Deleting the overloads here catches these cases at
-// compile time.
+// Wide) should be used. Catch those cases with overloads that assert at compile
+// time.
 template <size_t N>
-std::u16string WideToUTF16(const wchar_t (&str)[N]) {
+[[noreturn]] std::u16string WideToUTF16(const wchar_t (&str)[N]) {
   static_assert(AlwaysFalse<decltype(N)>,
-                "Error: Use the u\"...\" prefix instead.");
-  return std::u16string();
+                "Error: Use u\"...\" to create a std::u16string literal.");
 }
 
 template <size_t N>
-std::u16string UTF8ToUTF16(const char (&str)[N]) {
+[[noreturn]] std::u16string UTF8ToUTF16(const char (&str)[N]) {
   static_assert(AlwaysFalse<decltype(N)>,
-                "Error: Use the u\"...\" prefix instead.");
-  return std::u16string();
+                "Error: Use u\"...\" to create a std::u16string literal.");
 }
 
 template <size_t N>
-std::u16string ASCIIToUTF16(const char (&str)[N]) {
+[[noreturn]] std::u16string ASCIIToUTF16(const char (&str)[N]) {
   static_assert(AlwaysFalse<decltype(N)>,
-                "Error: Use the u\"...\" prefix instead.");
-  return std::u16string();
+                "Error: Use u\"...\" to create a std::u16string literal.");
 }
 
 // Mutable character arrays are usually only populated during runtime. Continue
diff --git a/copy.bara.sky b/copy.bara.sky
index 72fde7e..45c9192 100644
--- a/copy.bara.sky
+++ b/copy.bara.sky
@@ -113,7 +113,9 @@
     core.replace("base/trace_event/base_tracing.h", "polyfills/third_party/perfetto/include/perfetto/tracing/traced_value.h"),
     core.replace("base/trace_event/base_tracing_forward.h", "polyfills/third_party/perfetto/include/perfetto/tracing/traced_value.h"),
     core.replace("#include \"base/strings/string_number_conversions_win.h\"", ""),
-    core.replace("#include \"base/allocator/partition_allocator/partition_alloc_config.h\"", ""),
+    # Patch out C++20 feature use
+    core.replace("                           std::is_same<iter_value_t<T>, char8_t>,", ""),
+    #core.replace("#include \"base/allocator/partition_allocator/partition_alloc_config.h\"", ""),
 
     # Use system ICU.
     core.replace(
diff --git a/url/gurl.cc b/url/gurl.cc
index dea8c7a..f905498 100644
--- a/url/gurl.cc
+++ b/url/gurl.cc
@@ -9,12 +9,12 @@
 #include <algorithm>
 #include <memory>
 #include <ostream>
+#include <string_view>
 #include <utility>
 
 #include "polyfills/base/check_op.h"
 #include "base/no_destructor.h"
 #include "polyfills/base/notreached.h"
-#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "polyfills/third_party/perfetto/include/perfetto/tracing/traced_value.h"
 #include "polyfills/base/trace_event/memory_usage_estimator.h"
@@ -43,11 +43,11 @@
   other.parsed_ = url::Parsed();
 }
 
-GURL::GURL(gurl_base::StringPiece url_string) {
+GURL::GURL(std::string_view url_string) {
   InitCanonical(url_string, true);
 }
 
-GURL::GURL(gurl_base::StringPiece16 url_string) {
+GURL::GURL(std::u16string_view url_string) {
   InitCanonical(url_string, true);
 }
 
@@ -174,7 +174,7 @@
 }
 
 // Note: code duplicated below (it's inconvenient to use a template here).
-GURL GURL::Resolve(gurl_base::StringPiece relative) const {
+GURL GURL::Resolve(std::string_view relative) const {
   // Not allowed for invalid URLs.
   if (!is_valid_)
     return GURL();
@@ -200,7 +200,7 @@
 }
 
 // Note: code duplicated above (it's inconvenient to use a template here).
-GURL GURL::Resolve(gurl_base::StringPiece16 relative) const {
+GURL GURL::Resolve(std::u16string_view relative) const {
   // Not allowed for invalid URLs.
   if (!is_valid_)
     return GURL();
@@ -355,7 +355,7 @@
   return IsAboutUrl(url::kAboutSrcdocPath);
 }
 
-bool GURL::SchemeIs(gurl_base::StringPiece lower_ascii_scheme) const {
+bool GURL::SchemeIs(std::string_view lower_ascii_scheme) const {
   GURL_DCHECK(gurl_base::IsStringASCII(lower_ascii_scheme));
   GURL_DCHECK(gurl_base::ToLowerASCII(lower_ascii_scheme) == lower_ascii_scheme);
 
@@ -378,7 +378,7 @@
   return SchemeIsCryptographic(scheme_piece());
 }
 
-bool GURL::SchemeIsCryptographic(gurl_base::StringPiece lower_ascii_scheme) {
+bool GURL::SchemeIsCryptographic(std::string_view lower_ascii_scheme) {
   GURL_DCHECK(gurl_base::IsStringASCII(lower_ascii_scheme));
   GURL_DCHECK(gurl_base::ToLowerASCII(lower_ascii_scheme) == lower_ascii_scheme);
 
@@ -413,13 +413,13 @@
   return ComponentString(file_component);
 }
 
-gurl_base::StringPiece GURL::PathForRequestPiece() const {
+std::string_view GURL::PathForRequestPiece() const {
   GURL_DCHECK(parsed_.path.is_nonempty())
       << "Canonical path for requests should be non-empty";
   if (parsed_.ref.is_valid()) {
     // Clip off the reference when it exists. The reference starts after the
     // #-sign, so we have to subtract one to also remove it.
-    return gurl_base::StringPiece(spec_).substr(
+    return std::string_view(spec_).substr(
         parsed_.path.begin, parsed_.ref.begin - parsed_.path.begin - 1);
   }
   // Compute the actual path length, rather than depending on the spec's
@@ -429,7 +429,7 @@
   if (parsed_.query.is_valid())
     path_len = parsed_.query.end() - parsed_.path.begin;
 
-  return gurl_base::StringPiece(spec_).substr(parsed_.path.begin, path_len);
+  return std::string_view(spec_).substr(parsed_.path.begin, path_len);
 }
 
 std::string GURL::PathForRequest() const {
@@ -440,7 +440,7 @@
   return std::string(HostNoBracketsPiece());
 }
 
-gurl_base::StringPiece GURL::HostNoBracketsPiece() const {
+std::string_view GURL::HostNoBracketsPiece() const {
   // If host looks like an IPv6 literal, strip the square brackets.
   url::Component h(parsed_.host);
   if (h.len >= 2 && spec_[h.begin] == '[' && spec_[h.end() - 1] == ']') {
@@ -454,9 +454,9 @@
   return std::string(GetContentPiece());
 }
 
-gurl_base::StringPiece GURL::GetContentPiece() const {
+std::string_view GURL::GetContentPiece() const {
   if (!is_valid_)
-    return gurl_base::StringPiece();
+    return std::string_view();
   url::Component content_component = parsed_.GetContent();
   if (!SchemeIs(url::kJavaScriptScheme) && parsed_.ref.is_valid())
     content_component.len -= parsed_.ref.len + 1;
@@ -472,7 +472,7 @@
   return *empty_gurl;
 }
 
-bool GURL::DomainIs(gurl_base::StringPiece canonical_domain) const {
+bool GURL::DomainIs(std::string_view canonical_domain) const {
   if (!is_valid_)
     return false;
 
@@ -486,8 +486,8 @@
   int ref_position = parsed_.CountCharactersBefore(url::Parsed::REF, true);
   int ref_position_other =
       other.parsed_.CountCharactersBefore(url::Parsed::REF, true);
-  return gurl_base::StringPiece(spec_).substr(0, ref_position) ==
-         gurl_base::StringPiece(other.spec_).substr(0, ref_position_other);
+  return std::string_view(spec_).substr(0, ref_position) ==
+         std::string_view(other.spec_).substr(0, ref_position_other);
 }
 
 void GURL::Swap(GURL* other) {
@@ -503,7 +503,7 @@
          (parsed_.inner_parsed() ? sizeof(url::Parsed) : 0);
 }
 
-bool GURL::IsAboutUrl(gurl_base::StringPiece allowed_path) const {
+bool GURL::IsAboutUrl(std::string_view allowed_path) const {
   if (!SchemeIs(url::kAboutScheme))
     return false;
 
@@ -514,8 +514,8 @@
 }
 
 // static
-bool GURL::IsAboutPath(gurl_base::StringPiece actual_path,
-                       gurl_base::StringPiece allowed_path) {
+bool GURL::IsAboutPath(std::string_view actual_path,
+                       std::string_view allowed_path) {
   if (!gurl_base::StartsWith(actual_path, allowed_path))
     return false;
 
@@ -549,22 +549,22 @@
   return !(x == y);
 }
 
-bool operator==(const GURL& x, const gurl_base::StringPiece& spec) {
+bool operator==(const GURL& x, std::string_view spec) {
   GURL_DCHECK_EQ(GURL(spec).possibly_invalid_spec(), spec)
       << "Comparisons of GURLs and strings must ensure as a precondition that "
          "the string is fully canonicalized.";
   return x.possibly_invalid_spec() == spec;
 }
 
-bool operator==(const gurl_base::StringPiece& spec, const GURL& x) {
+bool operator==(std::string_view spec, const GURL& x) {
   return x == spec;
 }
 
-bool operator!=(const GURL& x, const gurl_base::StringPiece& spec) {
+bool operator!=(const GURL& x, std::string_view spec) {
   return !(x == spec);
 }
 
-bool operator!=(const gurl_base::StringPiece& spec, const GURL& x) {
+bool operator!=(std::string_view spec, const GURL& x) {
   return !(x == spec);
 }
 
diff --git a/url/gurl.h b/url/gurl.h
index 1b29989..931c803 100644
--- a/url/gurl.h
+++ b/url/gurl.h
@@ -10,11 +10,11 @@
 #include <iosfwd>
 #include <memory>
 #include <string>
+#include <string_view>
 
 #include "polyfills/base/component_export.h"
 #include "polyfills/base/debug/alias.h"
 #include "base/debug/crash_logging.h"
-#include "base/strings/string_piece.h"
 #include "polyfills/third_party/perfetto/include/perfetto/tracing/traced_value.h"
 #include "url/third_party/mozilla/url_parse.h"
 #include "url/url_canon.h"
@@ -46,8 +46,8 @@
 // will know to escape this and produce the desired result.
 class COMPONENT_EXPORT(URL) GURL {
  public:
-  typedef url::StringPieceReplacements<char> Replacements;
-  typedef url::StringPieceReplacements<char16_t> ReplacementsW;
+  using Replacements = url::StringViewReplacements<char>;
+  using ReplacementsW = url::StringViewReplacements<char16_t>;
 
   // Creates an empty, invalid URL.
   GURL();
@@ -57,9 +57,9 @@
   GURL(const GURL& other);
   GURL(GURL&& other) noexcept;
 
-  // The strings to this contructor should be UTF-8 / UTF-16.
-  explicit GURL(gurl_base::StringPiece url_string);
-  explicit GURL(gurl_base::StringPiece16 url_string);
+  // The strings to this constructor should be UTF-8 / UTF-16.
+  explicit GURL(std::string_view url_string);
+  explicit GURL(std::u16string_view url_string);
 
   // Constructor for URLs that have already been parsed and canonicalized. This
   // is used for conversions from KURL, for example. The caller must supply all
@@ -152,8 +152,8 @@
   //
   // It is an error to resolve a URL relative to an invalid URL. The result
   // will be the empty URL.
-  GURL Resolve(gurl_base::StringPiece relative) const;
-  GURL Resolve(gurl_base::StringPiece16 relative) const;
+  GURL Resolve(std::string_view relative) const;
+  GURL Resolve(std::u16string_view relative) const;
 
   // Creates a new GURL by replacing the current URL's components with the
   // supplied versions. See the Replacements class in url_canon.h for more.
@@ -238,7 +238,7 @@
   // Returns true if the given parameter (should be lower-case ASCII to match
   // the canonicalized scheme) is the scheme for this URL. Do not include a
   // colon.
-  bool SchemeIs(gurl_base::StringPiece lower_ascii_scheme) const;
+  bool SchemeIs(std::string_view lower_ascii_scheme) const;
 
   // Returns true if the scheme is "http" or "https".
   bool SchemeIsHTTPOrHTTPS() const;
@@ -267,7 +267,7 @@
   bool SchemeIsCryptographic() const;
 
   // As above, but static. Parameter should be lower-case ASCII.
-  static bool SchemeIsCryptographic(gurl_base::StringPiece lower_ascii_scheme);
+  static bool SchemeIsCryptographic(std::string_view lower_ascii_scheme);
 
   // Returns true if the scheme is "blob".
   bool SchemeIsBlob() const {
@@ -286,7 +286,7 @@
   // It is an error to get the content of an invalid URL: the result will be an
   // empty string.
   std::string GetContent() const;
-  gurl_base::StringPiece GetContentPiece() const;
+  std::string_view GetContentPiece() const;
 
   // Returns true if the hostname is an IP address. Note: this function isn't
   // as cheap as a simple getter because it re-parses the hostname to verify.
@@ -297,7 +297,7 @@
   std::string scheme() const {
     return ComponentString(parsed_.scheme);
   }
-  gurl_base::StringPiece scheme_piece() const {
+  std::string_view scheme_piece() const {
     return ComponentStringPiece(parsed_.scheme);
   }
 
@@ -305,7 +305,7 @@
   std::string username() const {
     return ComponentString(parsed_.username);
   }
-  gurl_base::StringPiece username_piece() const {
+  std::string_view username_piece() const {
     return ComponentStringPiece(parsed_.username);
   }
 
@@ -313,7 +313,7 @@
   std::string password() const {
     return ComponentString(parsed_.password);
   }
-  gurl_base::StringPiece password_piece() const {
+  std::string_view password_piece() const {
     return ComponentStringPiece(parsed_.password);
   }
 
@@ -327,7 +327,7 @@
   std::string host() const {
     return ComponentString(parsed_.host);
   }
-  gurl_base::StringPiece host_piece() const {
+  std::string_view host_piece() const {
     return ComponentStringPiece(parsed_.host);
   }
 
@@ -338,7 +338,7 @@
   std::string port() const {
     return ComponentString(parsed_.port);
   }
-  gurl_base::StringPiece port_piece() const {
+  std::string_view port_piece() const {
     return ComponentStringPiece(parsed_.port);
   }
 
@@ -348,7 +348,7 @@
   std::string path() const {
     return ComponentString(parsed_.path);
   }
-  gurl_base::StringPiece path_piece() const {
+  std::string_view path_piece() const {
     return ComponentStringPiece(parsed_.path);
   }
 
@@ -357,7 +357,7 @@
   std::string query() const {
     return ComponentString(parsed_.query);
   }
-  gurl_base::StringPiece query_piece() const {
+  std::string_view query_piece() const {
     return ComponentStringPiece(parsed_.query);
   }
 
@@ -367,7 +367,7 @@
   std::string ref() const {
     return ComponentString(parsed_.ref);
   }
-  gurl_base::StringPiece ref_piece() const {
+  std::string_view ref_piece() const {
     return ComponentStringPiece(parsed_.ref);
   }
 
@@ -389,14 +389,14 @@
   std::string PathForRequest() const;
 
   // Returns the same characters as PathForRequest(), avoiding a copy.
-  gurl_base::StringPiece PathForRequestPiece() const;
+  std::string_view PathForRequestPiece() const;
 
   // Returns the host, excluding the square brackets surrounding IPv6 address
   // literals. This can be useful for passing to getaddrinfo().
   std::string HostNoBrackets() const;
 
   // Returns the same characters as HostNoBrackets(), avoiding a copy.
-  gurl_base::StringPiece HostNoBracketsPiece() const;
+  std::string_view HostNoBracketsPiece() const;
 
   // Returns true if this URL's host matches or is in the same domain as
   // the given input string. For example, if the hostname of the URL is
@@ -409,7 +409,7 @@
   // This call is more efficient than getting the host and checking whether the
   // host has the specific domain or not because no copies or object
   // constructions are done.
-  bool DomainIs(gurl_base::StringPiece canonical_domain) const;
+  bool DomainIs(std::string_view canonical_domain) const;
 
   // Checks whether or not two URLs differ only in the ref (the part after
   // the # character).
@@ -440,8 +440,8 @@
   size_t EstimateMemoryUsage() const;
 
   // Helper used by GURL::IsAboutUrl and KURL::IsAboutURL.
-  static bool IsAboutPath(gurl_base::StringPiece actual_path,
-                          gurl_base::StringPiece allowed_path);
+  static bool IsAboutPath(std::string_view actual_path,
+                          std::string_view allowed_path);
 
   void WriteIntoTrace(perfetto::TracedValue context) const;
 
@@ -460,17 +460,17 @@
   void InitializeFromCanonicalSpec();
 
   // Helper used by IsAboutBlank and IsAboutSrcdoc.
-  bool IsAboutUrl(gurl_base::StringPiece allowed_path) const;
+  bool IsAboutUrl(std::string_view allowed_path) const;
 
   // Returns the substring of the input identified by the given component.
   std::string ComponentString(const url::Component& comp) const {
     return std::string(ComponentStringPiece(comp));
   }
-  gurl_base::StringPiece ComponentStringPiece(const url::Component& comp) const {
+  std::string_view ComponentStringPiece(const url::Component& comp) const {
     if (comp.is_empty())
-      return gurl_base::StringPiece();
-    return gurl_base::StringPiece(spec_).substr(static_cast<size_t>(comp.begin),
-                                           static_cast<size_t>(comp.len));
+      return std::string_view();
+    return std::string_view(spec_).substr(static_cast<size_t>(comp.begin),
+                                          static_cast<size_t>(comp.len));
   }
 
   void ProcessFileSystemURLAfterReplaceComponents();
@@ -501,13 +501,13 @@
 // url == GURL(spec) where |spec| is known (i.e. constants). This is to prevent
 // needlessly re-parsing |spec| into a temporary GURL.
 COMPONENT_EXPORT(URL)
-bool operator==(const GURL& x, const gurl_base::StringPiece& spec);
+bool operator==(const GURL& x, std::string_view spec);
 COMPONENT_EXPORT(URL)
-bool operator==(const gurl_base::StringPiece& spec, const GURL& x);
+bool operator==(std::string_view spec, const GURL& x);
 COMPONENT_EXPORT(URL)
-bool operator!=(const GURL& x, const gurl_base::StringPiece& spec);
+bool operator!=(const GURL& x, std::string_view spec);
 COMPONENT_EXPORT(URL)
-bool operator!=(const gurl_base::StringPiece& spec, const GURL& x);
+bool operator!=(std::string_view spec, const GURL& x);
 
 // DEBUG_ALIAS_FOR_GURL(var_name, url) copies |url| into a new stack-allocated
 // variable named |<var_name>|.  This helps ensure that the value of |url| gets
diff --git a/url/gurl_abstract_tests.h b/url/gurl_abstract_tests.h
index d787d3e..6ef976c 100644
--- a/url/gurl_abstract_tests.h
+++ b/url/gurl_abstract_tests.h
@@ -11,7 +11,7 @@
 // by parametrizing the tests with a class that has to expose the following
 // members:
 //   using UrlType = ...;
-//   static UrlType CreateUrlFromString(gurl_base::StringPiece s);
+//   static UrlType CreateUrlFromString(std::string_view s);
 //   static bool IsAboutBlank(const UrlType& url);
 //   static bool IsAboutSrcdoc(const UrlType& url);
 template <typename TUrlTraits>
@@ -23,7 +23,7 @@
   // avoid hitting: explicit qualification required to use member 'IsAboutBlank'
   // from dependent base class.
   using UrlType = typename TUrlTraits::UrlType;
-  UrlType CreateUrlFromString(gurl_base::StringPiece s) {
+  UrlType CreateUrlFromString(std::string_view s) {
     return TUrlTraits::CreateUrlFromString(s);
   }
   bool IsAboutBlank(const UrlType& url) {
diff --git a/url/gurl_unittest.cc b/url/gurl_unittest.cc
index c6be656..36dd969 100644
--- a/url/gurl_unittest.cc
+++ b/url/gurl_unittest.cc
@@ -838,8 +838,8 @@
 
   GURL url_with_escape_chars("https://www.,.test");
   EXPECT_TRUE(url_with_escape_chars.is_valid());
-  EXPECT_EQ(url_with_escape_chars.host(), "www.%2C.test");
-  EXPECT_TRUE(url_with_escape_chars.DomainIs("%2C.test"));
+  EXPECT_EQ(url_with_escape_chars.host(), "www.,.test");
+  EXPECT_TRUE(url_with_escape_chars.DomainIs(",.test"));
 }
 
 TEST(GURLTest, DomainIsTerminatingDotBehavior) {
@@ -1167,7 +1167,7 @@
  public:
   using UrlType = GURL;
 
-  static UrlType CreateUrlFromString(gurl_base::StringPiece s) { return GURL(s); }
+  static UrlType CreateUrlFromString(std::string_view s) { return GURL(s); }
   static bool IsAboutBlank(const UrlType& url) { return url.IsAboutBlank(); }
   static bool IsAboutSrcdoc(const UrlType& url) { return url.IsAboutSrcdoc(); }
 
diff --git a/url/origin.cc b/url/origin.cc
index 0274f34..45017ea 100644
--- a/url/origin.cc
+++ b/url/origin.cc
@@ -9,6 +9,7 @@
 #include <algorithm>
 #include <ostream>
 #include <string>
+#include <string_view>
 #include <tuple>
 #include <utility>
 
@@ -20,7 +21,6 @@
 #include "base/debug/crash_logging.h"
 #include "base/pickle.h"
 #include "base/strings/strcat.h"
-#include "base/strings/string_piece.h"
 #include "polyfills/third_party/perfetto/include/perfetto/tracing/traced_value.h"
 #include "polyfills/base/trace_event/memory_usage_estimator.h"
 #include "base/unguessable_token.h"
@@ -79,8 +79,8 @@
 
 // static
 absl::optional<Origin> Origin::UnsafelyCreateTupleOriginWithoutNormalization(
-    gurl_base::StringPiece scheme,
-    gurl_base::StringPiece host,
+    std::string_view scheme,
+    std::string_view host,
     uint16_t port) {
   SchemeHostPort tuple(std::string(scheme), std::string(host), port,
                        SchemeHostPort::CHECK_CANONICALIZATION);
@@ -91,8 +91,8 @@
 
 // static
 absl::optional<Origin> Origin::UnsafelyCreateOpaqueOriginWithoutNormalization(
-    gurl_base::StringPiece precursor_scheme,
-    gurl_base::StringPiece precursor_host,
+    std::string_view precursor_scheme,
+    std::string_view precursor_host,
     uint16_t precursor_port,
     const Origin::Nonce& nonce) {
   SchemeHostPort precursor(std::string(precursor_scheme),
@@ -249,7 +249,7 @@
   return url.scheme() == tuple_.scheme();
 }
 
-bool Origin::DomainIs(gurl_base::StringPiece canonical_domain) const {
+bool Origin::DomainIs(std::string_view canonical_domain) const {
   return !opaque() && url::DomainIs(tuple_.host(), canonical_domain);
 }
 
diff --git a/url/origin.h b/url/origin.h
index a49cbca..d364454 100644
--- a/url/origin.h
+++ b/url/origin.h
@@ -9,32 +9,24 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 
 #include "polyfills/base/component_export.h"
 #include "polyfills/base/debug/alias.h"
 #include "base/debug/crash_logging.h"
 #include "base/gtest_prod_util.h"
-#include "base/strings/string_piece_forward.h"
 #include "base/strings/string_util.h"
 #include "polyfills/third_party/perfetto/include/perfetto/tracing/traced_value.h"
 #include "base/unguessable_token.h"
 #include "build/build_config.h"
 #include "build/buildflag.h"
+#include "build/robolectric_buildflags.h"
 #include "absl/types/optional.h"
 #include "url/scheme_host_port.h"
 
-#if BUILDFLAG(IS_ANDROID)
-#include <jni.h>
-
-namespace gurl_base {
-namespace android {
-template <typename>
-class ScopedJavaLocalRef;
-template <typename>
-class JavaRef;
-}  // namespace android
-}  // namespace base
-#endif  // BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_ROBOLECTRIC)
+#include "base/android/jni_android.h"
+#endif
 
 class GURL;
 
@@ -199,8 +191,8 @@
   // dangerous recanonicalization); other potential callers should prefer the
   // 'GURL'-based constructor.
   static absl::optional<Origin> UnsafelyCreateTupleOriginWithoutNormalization(
-      gurl_base::StringPiece scheme,
-      gurl_base::StringPiece host,
+      std::string_view scheme,
+      std::string_view host,
       uint16_t port);
 
   // Creates an origin without sanity checking that the host is canonicalized.
@@ -285,7 +277,7 @@
   GURL GetURL() const;
 
   // Same as GURL::DomainIs. If |this| origin is opaque, then returns false.
-  bool DomainIs(gurl_base::StringPiece canonical_domain) const;
+  bool DomainIs(std::string_view canonical_domain) const;
 
   // Allows Origin to be used as a key in STL (for example, a std::set or
   // std::map).
@@ -314,8 +306,8 @@
   // and precursor information.
   std::string GetDebugString(bool include_nonce = true) const;
 
-#if BUILDFLAG(IS_ANDROID)
-  gurl_base::android::ScopedJavaLocalRef<jobject> CreateJavaObject() const;
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_ROBOLECTRIC)
+  gurl_base::android::ScopedJavaLocalRef<jobject> ToJavaObject() const;
   static Origin FromJavaObject(
       const gurl_base::android::JavaRef<jobject>& java_origin);
   static jlong CreateNative(JNIEnv* env,
@@ -334,6 +326,13 @@
   size_t EstimateMemoryUsage() const;
 
  private:
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_ROBOLECTRIC)
+  friend Origin CreateOpaqueOriginForAndroid(
+      const std::string& scheme,
+      const std::string& host,
+      uint16_t port,
+      const gurl_base::UnguessableToken& nonce_token);
+#endif
   friend class blink::SecurityOrigin;
   friend class blink::SecurityOriginTest;
   friend class blink::StorageKey;
@@ -419,8 +418,8 @@
   // back and forth over IPC (as transitioning through GURL would risk
   // potentially dangerous recanonicalization).
   static absl::optional<Origin> UnsafelyCreateOpaqueOriginWithoutNormalization(
-      gurl_base::StringPiece precursor_scheme,
-      gurl_base::StringPiece precursor_host,
+      std::string_view precursor_scheme,
+      std::string_view precursor_host,
       uint16_t precursor_port,
       const Nonce& nonce);
 
diff --git a/url/origin_abstract_tests.cc b/url/origin_abstract_tests.cc
index d48a9f5..175abff 100644
--- a/url/origin_abstract_tests.cc
+++ b/url/origin_abstract_tests.cc
@@ -29,7 +29,7 @@
 }
 
 // static
-Origin UrlOriginTestTraits::CreateOriginFromString(gurl_base::StringPiece s) {
+Origin UrlOriginTestTraits::CreateOriginFromString(std::string_view s) {
   return Origin::Create(GURL(s));
 }
 
@@ -40,7 +40,7 @@
 
 // static
 Origin UrlOriginTestTraits::CreateWithReferenceOrigin(
-    gurl_base::StringPiece url,
+    std::string_view url,
     const Origin& reference_origin) {
   return Origin::Resolve(GURL(url), reference_origin);
 }
@@ -94,7 +94,7 @@
 }
 
 // static
-bool UrlOriginTestTraits::IsValidUrl(gurl_base::StringPiece str) {
+bool UrlOriginTestTraits::IsValidUrl(std::string_view str) {
   return GURL(str).is_valid();
 }
 
diff --git a/url/origin_abstract_tests.h b/url/origin_abstract_tests.h
index b89f63f..78483bf 100644
--- a/url/origin_abstract_tests.h
+++ b/url/origin_abstract_tests.h
@@ -6,10 +6,10 @@
 #define URL_ORIGIN_ABSTRACT_TESTS_H_
 
 #include <string>
+#include <string_view>
 #include <type_traits>
 
 #include "base/containers/contains.h"
-#include "base/strings/string_piece.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -28,10 +28,10 @@
   using OriginType = Origin;
 
   // Constructing an origin.
-  static OriginType CreateOriginFromString(gurl_base::StringPiece s);
+  static OriginType CreateOriginFromString(std::string_view s);
   static OriginType CreateUniqueOpaqueOrigin();
   static OriginType CreateWithReferenceOrigin(
-      gurl_base::StringPiece url,
+      std::string_view url,
       const OriginType& reference_origin);
   static OriginType DeriveNewOpaqueOrigin(const OriginType& reference_origin);
 
@@ -51,7 +51,7 @@
   //
   // TODO(lukasza): Consider merging together OriginTraitsBase here and
   // UrlTraitsBase in //url/gurl_abstract_tests.h.
-  static bool IsValidUrl(gurl_base::StringPiece str);
+  static bool IsValidUrl(std::string_view str);
 
   // Only static members = no constructors are needed.
   UrlOriginTestTraits() = delete;
@@ -95,13 +95,13 @@
   // avoid hitting: explicit qualification required to use member 'IsOpaque'
   // from dependent base class.
   using OriginType = typename TOriginTraits::OriginType;
-  OriginType CreateOriginFromString(gurl_base::StringPiece s) {
+  OriginType CreateOriginFromString(std::string_view s) {
     return TOriginTraits::CreateOriginFromString(s);
   }
   OriginType CreateUniqueOpaqueOrigin() {
     return TOriginTraits::CreateUniqueOpaqueOrigin();
   }
-  OriginType CreateWithReferenceOrigin(gurl_base::StringPiece url,
+  OriginType CreateWithReferenceOrigin(std::string_view url,
                                        const OriginType& reference_origin) {
     return TOriginTraits::CreateWithReferenceOrigin(url, reference_origin);
   }
@@ -132,7 +132,7 @@
   std::string Serialize(const OriginType& origin) {
     return TOriginTraits::Serialize(origin);
   }
-  bool IsValidUrl(gurl_base::StringPiece str) {
+  bool IsValidUrl(std::string_view str) {
     return TOriginTraits::IsValidUrl(str);
   }
 
@@ -213,7 +213,7 @@
     VerifyOriginInvariants(origin);
   }
 
-  void TestUniqueOpaqueOrigin(gurl_base::StringPiece test_input) {
+  void TestUniqueOpaqueOrigin(std::string_view test_input) {
     auto origin = this->CreateOriginFromString(test_input);
     this->VerifyUniqueOpaqueOriginInvariants(origin);
 
diff --git a/url/origin_unittest.cc b/url/origin_unittest.cc
index 5f28acc..ead042a 100644
--- a/url/origin_unittest.cc
+++ b/url/origin_unittest.cc
@@ -76,8 +76,8 @@
   // Wrappers around url::Origin methods to expose it to tests.
 
   absl::optional<Origin> UnsafelyCreateOpaqueOriginWithoutNormalization(
-      gurl_base::StringPiece precursor_scheme,
-      gurl_base::StringPiece precursor_host,
+      std::string_view precursor_scheme,
+      std::string_view precursor_host,
       uint16_t precursor_port,
       const Origin::Nonce& nonce) {
     return Origin::UnsafelyCreateOpaqueOriginWithoutNormalization(
@@ -371,8 +371,8 @@
 
 TEST_F(OriginTest, UnsafelyCreateUniqueViaEmbeddedNulls) {
   struct TestCases {
-    gurl_base::StringPiece scheme;
-    gurl_base::StringPiece host;
+    std::string_view scheme;
+    std::string_view host;
     uint16_t port = 80;
   } cases[] = {{{"http\0more", 9}, {"example.com", 11}},
                {{"http\0", 5}, {"example.com", 11}},
diff --git a/url/scheme_host_port.cc b/url/scheme_host_port.cc
index 2db80a8..0b5e867 100644
--- a/url/scheme_host_port.cc
+++ b/url/scheme_host_port.cc
@@ -8,6 +8,7 @@
 #include <string.h>
 
 #include <ostream>
+#include <string_view>
 #include <tuple>
 
 #include "polyfills/base/check_op.h"
@@ -15,7 +16,6 @@
 #include "polyfills/base/notreached.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
 #include "polyfills/base/trace_event/memory_usage_estimator.h"
 #include "url/gurl.h"
 #include "url/third_party/mozilla/url_parse.h"
@@ -28,7 +28,7 @@
 
 namespace {
 
-bool IsCanonicalHost(const gurl_base::StringPiece& host) {
+bool IsCanonicalHost(const std::string_view& host) {
   std::string canon_host;
 
   // Try to canonicalize the host (copy/pasted from net/base. :( ).
@@ -56,8 +56,8 @@
 // ShouldTreatAsOpaqueOrigin in Blink (there might be existing differences in
 // behavior between these 2 layers, but we should avoid introducing new
 // differences).
-bool IsValidInput(const gurl_base::StringPiece& scheme,
-                  const gurl_base::StringPiece& host,
+bool IsValidInput(const std::string_view& scheme,
+                  const std::string_view& host,
                   uint16_t port,
                   SchemeHostPort::ConstructPolicy policy) {
   // Empty schemes are never valid.
@@ -159,8 +159,8 @@
                     << " Port: " << port;
 }
 
-SchemeHostPort::SchemeHostPort(gurl_base::StringPiece scheme,
-                               gurl_base::StringPiece host,
+SchemeHostPort::SchemeHostPort(std::string_view scheme,
+                               std::string_view host,
                                uint16_t port)
     : SchemeHostPort(std::string(scheme),
                      std::string(host),
@@ -171,8 +171,8 @@
   if (!url.is_valid())
     return;
 
-  gurl_base::StringPiece scheme = url.scheme_piece();
-  gurl_base::StringPiece host = url.host_piece();
+  std::string_view scheme = url.scheme_piece();
+  std::string_view host = url.host_piece();
 
   // A valid GURL never returns PORT_INVALID.
   int port = url.EffectiveIntPort();
diff --git a/url/scheme_host_port.h b/url/scheme_host_port.h
index 0798fec..9938824 100644
--- a/url/scheme_host_port.h
+++ b/url/scheme_host_port.h
@@ -8,9 +8,9 @@
 #include <stdint.h>
 
 #include <string>
+#include <string_view>
 
 #include "polyfills/base/component_export.h"
-#include "base/strings/string_piece.h"
 
 class GURL;
 
@@ -84,9 +84,7 @@
   // ports (e.g. 'file').
   //
   // Copies the data in |scheme| and |host|.
-  SchemeHostPort(gurl_base::StringPiece scheme,
-                 gurl_base::StringPiece host,
-                 uint16_t port);
+  SchemeHostPort(std::string_view scheme, std::string_view host, uint16_t port);
 
   // Metadata influencing whether or not the constructor should sanity check
   // host canonicalization.
diff --git a/url/url_canon.h b/url/url_canon.h
index 55fe426..0af495a 100644
--- a/url/url_canon.h
+++ b/url/url_canon.h
@@ -8,6 +8,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <string_view>
+
 #include "polyfills/base/component_export.h"
 #include "polyfills/base/export_template.h"
 #include "polyfills/base/memory/raw_ptr_exclusion.h"
@@ -57,6 +59,11 @@
   // write the data, then use set_size() to declare the new length().
   size_t capacity() const { return buffer_len_; }
 
+  // Returns the contents of the buffer as a string_view.
+  std::basic_string_view<T> view() const {
+    return std::basic_string_view<T>(data(), length());
+  }
+
   // Called by the user of this class to get the output. The output will NOT
   // be NULL-terminated. Call length() to get the
   // length.
@@ -102,6 +109,8 @@
     cur_len_ += str_len;
   }
 
+  void Append(std::basic_string_view<T> str) { Append(str.data(), str.size()); }
+
   void ReserveSizeIfNeeded(size_t estimated_size) {
     // Reserve a bit extra to account for escaped chars.
     if (estimated_size > buffer_len_)
@@ -278,7 +287,7 @@
 //
 // On error, returns false. The output in this case is undefined.
 COMPONENT_EXPORT(URL)
-bool IDNToASCII(const char16_t* src, int src_len, CanonOutputW* output);
+bool IDNToASCII(std::u16string_view src, CanonOutputW* output);
 
 // Piece-by-piece canonicalizers ----------------------------------------------
 //
diff --git a/url/url_canon_filesystemurl.cc b/url/url_canon_filesystemurl.cc
index 0472484..f1a9f1c 100644
--- a/url/url_canon_filesystemurl.cc
+++ b/url/url_canon_filesystemurl.cc
@@ -36,7 +36,7 @@
   // Scheme (known, so we don't bother running it through the more
   // complicated scheme canonicalizer).
   new_parsed->scheme.begin = output->length();
-  output->Append("filesystem:", 11);
+  output->Append("filesystem:");
   new_parsed->scheme.len = 10;
 
   if (!inner_parsed || !inner_parsed->scheme.is_valid())
@@ -46,7 +46,7 @@
   SchemeType inner_scheme_type = SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION;
   if (CompareSchemeComponent(spec, inner_parsed->scheme, url::kFileScheme)) {
     new_inner_parsed.scheme.begin = output->length();
-    output->Append("file://", 7);
+    output->Append("file://");
     new_inner_parsed.scheme.len = 4;
     success &= CanonicalizePath(spec, inner_parsed->path, output,
                                 &new_inner_parsed.path);
diff --git a/url/url_canon_fileurl.cc b/url/url_canon_fileurl.cc
index dae5c4c..5c243f6 100644
--- a/url/url_canon_fileurl.cc
+++ b/url/url_canon_fileurl.cc
@@ -4,7 +4,8 @@
 
 // Functions for canonicalizing "file:" URLs.
 
-#include "base/strings/string_piece.h"
+#include <string_view>
+
 #include "base/strings/string_util.h"
 #include "url/url_canon.h"
 #include "url/url_canon_internal.h"
@@ -18,13 +19,13 @@
 bool IsLocalhost(const char* spec, int begin, int end) {
   if (begin > end)
     return false;
-  return gurl_base::StringPiece(&spec[begin], end - begin) == "localhost";
+  return std::string_view(&spec[begin], end - begin) == "localhost";
 }
 
 bool IsLocalhost(const char16_t* spec, int begin, int end) {
   if (begin > end)
     return false;
-  return gurl_base::StringPiece16(&spec[begin], end - begin) == u"localhost";
+  return std::u16string_view(&spec[begin], end - begin) == u"localhost";
 }
 
 template <typename CHAR>
@@ -134,7 +135,7 @@
   // Scheme (known, so we don't bother running it through the more
   // complicated scheme canonicalizer).
   new_parsed->scheme.begin = output->length();
-  output->Append("file://", 7);
+  output->Append("file://");
   new_parsed->scheme.len = 4;
 
   // If the host is localhost, and the path starts with a Windows drive letter,
diff --git a/url/url_canon_host.cc b/url/url_canon_host.cc
index eacc69f..65118b2 100644
--- a/url/url_canon_host.cc
+++ b/url/url_canon_host.cc
@@ -6,11 +6,14 @@
 #include "polyfills/base/cpu_reduction_experiment.h"
 #include "url/url_canon.h"
 #include "url/url_canon_internal.h"
+#include "url/url_features.h"
 
 namespace url {
 
 namespace {
 
+// clang-format off
+//
 // For reference, here's what IE supports:
 // Key: 0 (disallowed: failure if present in the input)
 //      + (allowed either escaped or unescaped, and unmodified)
@@ -37,19 +40,15 @@
 // I also didn't test if characters affecting HTML parsing are allowed
 // unescaped, e.g. (") or (#), which would indicate the beginning of the path.
 // Surprisingly, space is accepted in the input and always escaped.
-
+//
+// TODO(https://crbug.com/1416013): Remove the above historical reference
+// information once we are 100% standard compliant to the URL Standard.
+//
 // This table lists the canonical version of all characters we allow in the
 // input, with 0 indicating it is disallowed. We use the magic kEscapedHostChar
 // value to indicate that this character should be escaped. We are a little more
 // restrictive than IE, but less restrictive than Firefox.
 //
-// Note that we disallow the % character. We will allow it when part of an
-// escape sequence, of course, but this disallows "%25". Even though IE allows
-// it, allowing it would put us in a funny state. If there was an invalid
-// escape sequence like "%zz", we'll add "%25zz" to the output and fail.
-// Allowing percents means we'll succeed a second time, so validity would change
-// based on how many times you run the canonicalizer. We prefer to always report
-// the same vailidity, so reject this.
 const unsigned char kEsc = 0xff;
 const unsigned char kHostCharLookup[0x80] = {
 // 00-1f: all are invalid
@@ -68,6 +67,27 @@
 //   p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~
     'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',kEsc,kEsc,kEsc,  0 ,  0 };
 
+// The following table is used when kStandardCompliantHostCharLookup feature is
+// enabled. See https://crbug.com/1416013 for details. At present, ' ' (SPACE)
+// and '*' (asterisk) are still non-compliant to the URL Standard.
+const unsigned char kStandardCompliantHostCharLookup[0x80] = {
+// 00-1f: all are invalid
+     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+//  ' '   !    "    #    $    %    &    '    (    )    *    +    ,    -    .    /
+    kEsc,'!', '"',  0,  '$',  0,  '&', '\'','(', ')', kEsc, '+', ',', '-', '.',  0,
+//   0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ?
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';' , 0,  '=',  0,   0,
+//   @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    O
+     0,  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+//   P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _
+    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '[',  0,  ']',  0,  '_',
+//   `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o
+    '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+//   p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~
+    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{',  0, '}',  '~',  0 };
+// clang-format on
+
 // RFC1034 maximum FQDN length.
 constexpr size_t kMaxHostLength = 253;
 
@@ -149,7 +169,12 @@
 
     if (source < 0x80) {
       // We have ASCII input, we can use our lookup table.
-      unsigned char replacement = kHostCharLookup[source];
+      unsigned char replacement;
+      if (url::IsUsingStandardCompliantHostCharacters()) {
+        replacement = kStandardCompliantHostCharLookup[source];
+      } else {
+        replacement = kHostCharLookup[source];
+      }
       if (!replacement) {
         // Invalid character, add it as percent-escaped and mark as failed.
         AppendEscapedChar(source, output);
@@ -189,9 +214,7 @@
   }
 
   StackBufferW wide_output;
-  if (!IDNToASCII(url_escaped_host.data(),
-                  url_escaped_host.length(),
-                  &wide_output)) {
+  if (!IDNToASCII(url_escaped_host.view(), &wide_output)) {
     // Some error, give up. This will write some reasonable looking
     // representation of the string to the output.
     AppendInvalidNarrowString(src, 0, src_len, output);
@@ -381,7 +404,7 @@
     // we just leave it in place.
     if (host_info->IsIPAddress()) {
       output->set_length(output_begin);
-      output->Append(canon_ip.data(), canon_ip.length());
+      output->Append(canon_ip.view());
     }
   } else {
     // Canonicalization failed. Set BROKEN to notify the caller.
diff --git a/url/url_canon_mailtourl.cc b/url/url_canon_mailtourl.cc
index e48b642..cbd4bb4 100644
--- a/url/url_canon_mailtourl.cc
+++ b/url/url_canon_mailtourl.cc
@@ -45,7 +45,7 @@
   // Scheme (known, so we don't bother running it through the more
   // complicated scheme canonicalizer).
   new_parsed->scheme.begin = output->length();
-  output->Append("mailto:", 7);
+  output->Append("mailto:");
   new_parsed->scheme.len = 6;
 
   bool success = true;
diff --git a/url/url_canon_path.cc b/url/url_canon_path.cc
index 7f917db..440da70 100644
--- a/url/url_canon_path.cc
+++ b/url/url_canon_path.cc
@@ -336,8 +336,7 @@
             // the last character of the escape sequence.
             char unescaped_flags = kPathCharLookup[unescaped_value];
 
-            if (!gurl_base::FeatureList::IsEnabled(
-                    url::kDontDecodeAsciiPercentEncodedURLPath) &&
+            if (!url::IsUsingDontDecodeAsciiPercentEncodedURLPath() &&
                 (unescaped_flags & UNESCAPE)) {
               // This escaped value shouldn't be escaped.  Try to copy it.
               unescape_escaped_char = true;
diff --git a/url/url_canon_stdstring.h b/url/url_canon_stdstring.h
index c8f9500..2ddf569 100644
--- a/url/url_canon_stdstring.h
+++ b/url/url_canon_stdstring.h
@@ -10,11 +10,11 @@
 // we have segregated it here.
 
 #include <string>
+#include <string_view>
 
 #include "base/compiler_specific.h"
 #include "polyfills/base/component_export.h"
 #include "polyfills/base/memory/raw_ptr_exclusion.h"
-#include "base/strings/string_piece.h"
 #include "url/url_canon.h"
 
 namespace url {
@@ -55,64 +55,64 @@
 };
 
 // An extension of the Replacements class that allows the setters to use
-// StringPieces (implicitly allowing strings or char*s).
+// string_views (implicitly allowing strings or char*s).
 //
-// The contents of the StringPieces are not copied and must remain valid until
-// the StringPieceReplacements object goes out of scope.
+// The contents of the string_views are not copied and must remain valid until
+// the StringViewReplacements object goes out of scope.
 //
 // In order to make it harder to misuse the API the setters do not accept rvalue
 // references to std::strings.
 // Note: Extra const char* overloads are necessary to break ambiguities that
 // would otherwise exist for char literals.
 template <typename CharT>
-class StringPieceReplacements : public Replacements<CharT> {
+class StringViewReplacements : public Replacements<CharT> {
  private:
   using StringT = std::basic_string<CharT>;
-  using StringPieceT = gurl_base::BasicStringPiece<CharT>;
+  using StringViewT = std::basic_string_view<CharT>;
   using ParentT = Replacements<CharT>;
   using SetterFun = void (ParentT::*)(const CharT*, const Component&);
 
-  void SetImpl(SetterFun fun, StringPieceT str) {
+  void SetImpl(SetterFun fun, StringViewT str) {
     (this->*fun)(str.data(), Component(0, static_cast<int>(str.size())));
   }
 
  public:
   void SetSchemeStr(const CharT* str) { SetImpl(&ParentT::SetScheme, str); }
-  void SetSchemeStr(StringPieceT str) { SetImpl(&ParentT::SetScheme, str); }
+  void SetSchemeStr(StringViewT str) { SetImpl(&ParentT::SetScheme, str); }
   void SetSchemeStr(const StringT&&) = delete;
 
   void SetUsernameStr(const CharT* str) { SetImpl(&ParentT::SetUsername, str); }
-  void SetUsernameStr(StringPieceT str) { SetImpl(&ParentT::SetUsername, str); }
+  void SetUsernameStr(StringViewT str) { SetImpl(&ParentT::SetUsername, str); }
   void SetUsernameStr(const StringT&&) = delete;
   using ParentT::ClearUsername;
 
   void SetPasswordStr(const CharT* str) { SetImpl(&ParentT::SetPassword, str); }
-  void SetPasswordStr(StringPieceT str) { SetImpl(&ParentT::SetPassword, str); }
+  void SetPasswordStr(StringViewT str) { SetImpl(&ParentT::SetPassword, str); }
   void SetPasswordStr(const StringT&&) = delete;
   using ParentT::ClearPassword;
 
   void SetHostStr(const CharT* str) { SetImpl(&ParentT::SetHost, str); }
-  void SetHostStr(StringPieceT str) { SetImpl(&ParentT::SetHost, str); }
+  void SetHostStr(StringViewT str) { SetImpl(&ParentT::SetHost, str); }
   void SetHostStr(const StringT&&) = delete;
   using ParentT::ClearHost;
 
   void SetPortStr(const CharT* str) { SetImpl(&ParentT::SetPort, str); }
-  void SetPortStr(StringPieceT str) { SetImpl(&ParentT::SetPort, str); }
+  void SetPortStr(StringViewT str) { SetImpl(&ParentT::SetPort, str); }
   void SetPortStr(const StringT&&) = delete;
   using ParentT::ClearPort;
 
   void SetPathStr(const CharT* str) { SetImpl(&ParentT::SetPath, str); }
-  void SetPathStr(StringPieceT str) { SetImpl(&ParentT::SetPath, str); }
+  void SetPathStr(StringViewT str) { SetImpl(&ParentT::SetPath, str); }
   void SetPathStr(const StringT&&) = delete;
   using ParentT::ClearPath;
 
   void SetQueryStr(const CharT* str) { SetImpl(&ParentT::SetQuery, str); }
-  void SetQueryStr(StringPieceT str) { SetImpl(&ParentT::SetQuery, str); }
+  void SetQueryStr(StringViewT str) { SetImpl(&ParentT::SetQuery, str); }
   void SetQueryStr(const StringT&&) = delete;
   using ParentT::ClearQuery;
 
   void SetRefStr(const CharT* str) { SetImpl(&ParentT::SetRef, str); }
-  void SetRefStr(StringPieceT str) { SetImpl(&ParentT::SetRef, str); }
+  void SetRefStr(StringViewT str) { SetImpl(&ParentT::SetRef, str); }
   void SetRefStr(const StringT&&) = delete;
   using ParentT::ClearRef;
 
diff --git a/url/url_canon_unittest.cc b/url/url_canon_unittest.cc
index c1b19d2..da4b7b3 100644
--- a/url/url_canon_unittest.cc
+++ b/url/url_canon_unittest.cc
@@ -6,8 +6,8 @@
 
 #include <errno.h>
 #include <stddef.h>
+#include <string_view>
 
-#include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/gtest_util.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -119,12 +119,12 @@
     {0x10FFFF, "\xF4\x8F\xBF\xBF"},
   };
   std::string out_str;
-  for (size_t i = 0; i < std::size(utf_cases); i++) {
+  for (const auto& utf_case : utf_cases) {
     out_str.clear();
     StdStringCanonOutput output(&out_str);
-    AppendUTF8Value(utf_cases[i].input, &output);
+    AppendUTF8Value(utf_case.input, &output);
     output.Complete();
-    EXPECT_EQ(utf_cases[i].output, out_str);
+    EXPECT_EQ(utf_case.output, out_str);
   }
 }
 
@@ -171,27 +171,27 @@
   };
 
   std::string out_str;
-  for (size_t i = 0; i < std::size(utf_cases); i++) {
-    if (utf_cases[i].input8) {
+  for (const auto& utf_case : utf_cases) {
+    if (utf_case.input8) {
       out_str.clear();
       StdStringCanonOutput output(&out_str);
 
-      size_t input_len = strlen(utf_cases[i].input8);
+      size_t input_len = strlen(utf_case.input8);
       bool success = true;
       for (size_t ch = 0; ch < input_len; ch++) {
-        success &= AppendUTF8EscapedChar(utf_cases[i].input8, &ch, input_len,
-                                         &output);
+        success &=
+            AppendUTF8EscapedChar(utf_case.input8, &ch, input_len, &output);
       }
       output.Complete();
-      EXPECT_EQ(utf_cases[i].expected_success, success);
-      EXPECT_EQ(std::string(utf_cases[i].output), out_str);
+      EXPECT_EQ(utf_case.expected_success, success);
+      EXPECT_EQ(std::string(utf_case.output), out_str);
     }
-    if (utf_cases[i].input16) {
+    if (utf_case.input16) {
       out_str.clear();
       StdStringCanonOutput output(&out_str);
 
       std::u16string input_str(
-          test_utils::TruncateWStringToUTF16(utf_cases[i].input16));
+          test_utils::TruncateWStringToUTF16(utf_case.input16));
       size_t input_len = input_str.length();
       bool success = true;
       for (size_t ch = 0; ch < input_len; ch++) {
@@ -199,18 +199,17 @@
                                          &output);
       }
       output.Complete();
-      EXPECT_EQ(utf_cases[i].expected_success, success);
-      EXPECT_EQ(std::string(utf_cases[i].output), out_str);
+      EXPECT_EQ(utf_case.expected_success, success);
+      EXPECT_EQ(std::string(utf_case.output), out_str);
     }
 
-    if (utf_cases[i].input8 && utf_cases[i].input16 &&
-        utf_cases[i].expected_success) {
+    if (utf_case.input8 && utf_case.input16 && utf_case.expected_success) {
       // Check that the UTF-8 and UTF-16 inputs are equivalent.
 
       // UTF-16 -> UTF-8
-      std::string input8_str(utf_cases[i].input8);
+      std::string input8_str(utf_case.input8);
       std::u16string input16_str(
-          test_utils::TruncateWStringToUTF16(utf_cases[i].input16));
+          test_utils::TruncateWStringToUTF16(utf_case.input16));
       EXPECT_EQ(input8_str, gurl_base::UTF16ToUTF8(input16_str));
 
       // UTF-8 -> UTF-16
@@ -242,36 +241,36 @@
 
   std::string out_str;
 
-  for (size_t i = 0; i < std::size(scheme_cases); i++) {
-    int url_len = static_cast<int>(strlen(scheme_cases[i].input));
+  for (const auto& scheme_case : scheme_cases) {
+    int url_len = static_cast<int>(strlen(scheme_case.input));
     Component in_comp(0, url_len);
     Component out_comp;
 
     out_str.clear();
     StdStringCanonOutput output1(&out_str);
-    bool success = CanonicalizeScheme(scheme_cases[i].input, in_comp, &output1,
-                                      &out_comp);
+    bool success =
+        CanonicalizeScheme(scheme_case.input, in_comp, &output1, &out_comp);
     output1.Complete();
 
-    EXPECT_EQ(scheme_cases[i].expected_success, success);
-    EXPECT_EQ(std::string(scheme_cases[i].expected), out_str);
-    EXPECT_EQ(scheme_cases[i].expected_component.begin, out_comp.begin);
-    EXPECT_EQ(scheme_cases[i].expected_component.len, out_comp.len);
+    EXPECT_EQ(scheme_case.expected_success, success);
+    EXPECT_EQ(std::string(scheme_case.expected), out_str);
+    EXPECT_EQ(scheme_case.expected_component.begin, out_comp.begin);
+    EXPECT_EQ(scheme_case.expected_component.len, out_comp.len);
 
     // Now try the wide version.
     out_str.clear();
     StdStringCanonOutput output2(&out_str);
 
-    std::u16string wide_input(gurl_base::UTF8ToUTF16(scheme_cases[i].input));
+    std::u16string wide_input(gurl_base::UTF8ToUTF16(scheme_case.input));
     in_comp.len = static_cast<int>(wide_input.length());
     success = CanonicalizeScheme(wide_input.c_str(), in_comp, &output2,
                                  &out_comp);
     output2.Complete();
 
-    EXPECT_EQ(scheme_cases[i].expected_success, success);
-    EXPECT_EQ(std::string(scheme_cases[i].expected), out_str);
-    EXPECT_EQ(scheme_cases[i].expected_component.begin, out_comp.begin);
-    EXPECT_EQ(scheme_cases[i].expected_component.len, out_comp.len);
+    EXPECT_EQ(scheme_case.expected_success, success);
+    EXPECT_EQ(std::string(scheme_case.expected), out_str);
+    EXPECT_EQ(scheme_case.expected_component.begin, out_comp.begin);
+    EXPECT_EQ(scheme_case.expected_component.len, out_comp.len);
   }
 
   // Test the case where the scheme is declared nonexistent, it should be
@@ -315,23 +314,29 @@
 TEST_P(URLCanonHostTest, Host) {
   bool use_idna_non_transitional = IsUsingIDNA2008NonTransitional();
 
+  // clang-format off
   IPAddressCase host_cases[] = {
       // Basic canonicalization, uppercase should be converted to lowercase.
       {"GoOgLe.CoM", L"GoOgLe.CoM", "google.com", Component(0, 10),
        CanonHostInfo::NEUTRAL, -1, ""},
-      // Spaces and some other characters should be escaped.
-      {"Goo%20 goo%7C|.com", L"Goo%20 goo%7C|.com", "goo%20%20goo%7C%7C.com",
-       Component(0, 22), CanonHostInfo::NEUTRAL, -1, ""},
+      // TODO(https://crbug.com/1416013): Update the test after SPACE is
+      // correctly handled.
+      {"Goo%20 goo.com", L"Goo%20 goo.com", "goo%20%20goo.com",
+       Component(0, 16), CanonHostInfo::NEUTRAL, -1, ""},
+      // TODO(https://crbug.com/1416013): Update the test after ASTERISK is
+      // correctly handled.
+      {"Goo%2a*goo.com", L"Goo%2a*goo.com", "goo%2A%2Agoo.com",
+       Component(0, 16), CanonHostInfo::NEUTRAL, -1, ""},
       // Exciting different types of spaces!
-      {NULL, L"GOO\x00a0\x3000goo.com", "goo%20%20goo.com", Component(0, 16),
+      {nullptr, L"GOO\x00a0\x3000goo.com", "goo%20%20goo.com", Component(0, 16),
        CanonHostInfo::NEUTRAL, -1, ""},
       // Other types of space (no-break, zero-width, zero-width-no-break) are
       // name-prepped away to nothing.
-      {NULL, L"GOO\x200b\x2060\xfeffgoo.com", "googoo.com", Component(0, 10),
+      {nullptr, L"GOO\x200b\x2060\xfeffgoo.com", "googoo.com", Component(0, 10),
        CanonHostInfo::NEUTRAL, -1, ""},
       // Ideographic full stop (full-width period for Chinese, etc.) should be
       // treated as a dot.
-      {NULL,
+      {nullptr,
        L"www.foo\x3002"
        L"bar.com",
        "www.foo.bar.com", Component(0, 15), CanonHostInfo::NEUTRAL, -1, ""},
@@ -437,7 +442,7 @@
       // Fullwidth exclamation mark is disallowed. UTS 46, table 4, row (b)
       // However, we do allow this at the moment because we don't use
       // STD3 rules and canonicalize full-width ASCII to ASCII.
-      {"wow\xef\xbc\x81", L"wow\xff01", "wow%21", Component(0, 6),
+      {"wow\xef\xbc\x81", L"wow\xff01", "wow!", Component(0, 4),
        CanonHostInfo::NEUTRAL, -1, ""},
       // U+2132 (turned capital F) is disallowed. UTS 46, table 4, row (c)
       // Allowed in IDNA 2003, but the mapping changed after Unicode 3.2
@@ -575,7 +580,7 @@
       // before punycode string was created. I.e.
       // if '(' is escaped after punycode is created we would get xn--%28-8tb
       // (incorrect).
-      {"\xd1\x82(", L"\x0442(", "xn--%28-7ed", Component(0, 11),
+      {"\xd1\x82(", L"\x0442(", "xn--(-8tb", Component(0, 9),
        CanonHostInfo::NEUTRAL, -1, ""},
       // Address with all hexadecimal characters with leading number of 1<<32
       // or greater and should return NEUTRAL rather than BROKEN if not all
@@ -597,37 +602,38 @@
       {"xn--m\xc3\xbcnchen", L"xn--m\xfcnchen", "xn--m%C3%BCnchen",
        Component(0, 16), CanonHostInfo::BROKEN, -1, ""},
   };
+  // clang-format on
 
   // CanonicalizeHost() non-verbose.
   std::string out_str;
-  for (size_t i = 0; i < std::size(host_cases); i++) {
+  for (const auto& host_case : host_cases) {
     // Narrow version.
-    if (host_cases[i].input8) {
-      int host_len = static_cast<int>(strlen(host_cases[i].input8));
+    if (host_case.input8) {
+      int host_len = static_cast<int>(strlen(host_case.input8));
       Component in_comp(0, host_len);
       Component out_comp;
 
       out_str.clear();
       StdStringCanonOutput output(&out_str);
 
-      bool success = CanonicalizeHost(host_cases[i].input8, in_comp, &output,
-                                      &out_comp);
+      bool success =
+          CanonicalizeHost(host_case.input8, in_comp, &output, &out_comp);
       output.Complete();
 
-      EXPECT_EQ(host_cases[i].expected_family != CanonHostInfo::BROKEN,
-                success) << "for input: " << host_cases[i].input8;
-      EXPECT_EQ(std::string(host_cases[i].expected), out_str) <<
-                "for input: " << host_cases[i].input8;
-      EXPECT_EQ(host_cases[i].expected_component.begin, out_comp.begin) <<
-                "for input: " << host_cases[i].input8;
-      EXPECT_EQ(host_cases[i].expected_component.len, out_comp.len) <<
-                "for input: " << host_cases[i].input8;
+      EXPECT_EQ(host_case.expected_family != CanonHostInfo::BROKEN, success)
+          << "for input: " << host_case.input8;
+      EXPECT_EQ(std::string(host_case.expected), out_str)
+          << "for input: " << host_case.input8;
+      EXPECT_EQ(host_case.expected_component.begin, out_comp.begin)
+          << "for input: " << host_case.input8;
+      EXPECT_EQ(host_case.expected_component.len, out_comp.len)
+          << "for input: " << host_case.input8;
     }
 
     // Wide version.
-    if (host_cases[i].input16) {
+    if (host_case.input16) {
       std::u16string input16(
-          test_utils::TruncateWStringToUTF16(host_cases[i].input16));
+          test_utils::TruncateWStringToUTF16(host_case.input16));
       int host_len = static_cast<int>(input16.length());
       Component in_comp(0, host_len);
       Component out_comp;
@@ -639,46 +645,43 @@
                                       &out_comp);
       output.Complete();
 
-      EXPECT_EQ(host_cases[i].expected_family != CanonHostInfo::BROKEN,
-                success);
-      EXPECT_EQ(std::string(host_cases[i].expected), out_str);
-      EXPECT_EQ(host_cases[i].expected_component.begin, out_comp.begin);
-      EXPECT_EQ(host_cases[i].expected_component.len, out_comp.len);
+      EXPECT_EQ(host_case.expected_family != CanonHostInfo::BROKEN, success);
+      EXPECT_EQ(std::string(host_case.expected), out_str);
+      EXPECT_EQ(host_case.expected_component.begin, out_comp.begin);
+      EXPECT_EQ(host_case.expected_component.len, out_comp.len);
     }
   }
 
   // CanonicalizeHostVerbose()
-  for (size_t i = 0; i < std::size(host_cases); i++) {
+  for (const auto& host_case : host_cases) {
     // Narrow version.
-    if (host_cases[i].input8) {
-      int host_len = static_cast<int>(strlen(host_cases[i].input8));
+    if (host_case.input8) {
+      int host_len = static_cast<int>(strlen(host_case.input8));
       Component in_comp(0, host_len);
 
       out_str.clear();
       StdStringCanonOutput output(&out_str);
       CanonHostInfo host_info;
 
-      CanonicalizeHostVerbose(host_cases[i].input8, in_comp, &output,
-                              &host_info);
+      CanonicalizeHostVerbose(host_case.input8, in_comp, &output, &host_info);
       output.Complete();
 
-      EXPECT_EQ(host_cases[i].expected_family, host_info.family);
-      EXPECT_EQ(std::string(host_cases[i].expected), out_str);
-      EXPECT_EQ(host_cases[i].expected_component.begin,
-                host_info.out_host.begin);
-      EXPECT_EQ(host_cases[i].expected_component.len, host_info.out_host.len);
-      EXPECT_EQ(std::string(host_cases[i].expected_address_hex),
+      EXPECT_EQ(host_case.expected_family, host_info.family);
+      EXPECT_EQ(std::string(host_case.expected), out_str);
+      EXPECT_EQ(host_case.expected_component.begin, host_info.out_host.begin);
+      EXPECT_EQ(host_case.expected_component.len, host_info.out_host.len);
+      EXPECT_EQ(std::string(host_case.expected_address_hex),
                 BytesToHexString(host_info.address, host_info.AddressLength()));
-      if (host_cases[i].expected_family == CanonHostInfo::IPV4) {
-        EXPECT_EQ(host_cases[i].expected_num_ipv4_components,
+      if (host_case.expected_family == CanonHostInfo::IPV4) {
+        EXPECT_EQ(host_case.expected_num_ipv4_components,
                   host_info.num_ipv4_components);
       }
     }
 
     // Wide version.
-    if (host_cases[i].input16) {
+    if (host_case.input16) {
       std::u16string input16(
-          test_utils::TruncateWStringToUTF16(host_cases[i].input16));
+          test_utils::TruncateWStringToUTF16(host_case.input16));
       int host_len = static_cast<int>(input16.length());
       Component in_comp(0, host_len);
 
@@ -689,21 +692,71 @@
       CanonicalizeHostVerbose(input16.c_str(), in_comp, &output, &host_info);
       output.Complete();
 
-      EXPECT_EQ(host_cases[i].expected_family, host_info.family);
-      EXPECT_EQ(std::string(host_cases[i].expected), out_str);
-      EXPECT_EQ(host_cases[i].expected_component.begin,
-                host_info.out_host.begin);
-      EXPECT_EQ(host_cases[i].expected_component.len, host_info.out_host.len);
-      EXPECT_EQ(std::string(host_cases[i].expected_address_hex),
+      EXPECT_EQ(host_case.expected_family, host_info.family);
+      EXPECT_EQ(std::string(host_case.expected), out_str);
+      EXPECT_EQ(host_case.expected_component.begin, host_info.out_host.begin);
+      EXPECT_EQ(host_case.expected_component.len, host_info.out_host.len);
+      EXPECT_EQ(std::string(host_case.expected_address_hex),
                 BytesToHexString(host_info.address, host_info.AddressLength()));
-      if (host_cases[i].expected_family == CanonHostInfo::IPV4) {
-        EXPECT_EQ(host_cases[i].expected_num_ipv4_components,
+      if (host_case.expected_family == CanonHostInfo::IPV4) {
+        EXPECT_EQ(host_case.expected_num_ipv4_components,
                   host_info.num_ipv4_components);
       }
     }
   }
 }
 
+TEST(URLCanonTest, HostPuncutationChar) {
+  // '%' is not tested here. '%' is used for percent-escaping.
+  const std::string_view allowed_host_chars[] = {
+      "!", "\"", "$", "&", "'", "(", ")", "+", ",",
+      "-", ".",  ";", "=", "_", "`", "{", "}", "~",
+  };
+
+  const std::string_view forbidden_host_chars[] = {
+      "#", "/", ":", "<", ">", "?", "@", "[", "\\", "]", "^", "|",
+  };
+
+  // Standard non-compliant characters which are escaped. See
+  // https://crbug.com/1416013.
+  struct EscapedCharTestCase {
+    std::string_view input;
+    std::string_view expected;
+  } escaped_host_chars[] = {{" ", "%20"}, {"*", "%2A"}};
+
+  for (const std::string_view input : allowed_host_chars) {
+    std::string out_str;
+    Component in_comp(0, input.size());
+    Component out_comp;
+    StdStringCanonOutput output(&out_str);
+    bool success = CanonicalizeHost(input.data(), in_comp, &output, &out_comp);
+    EXPECT_TRUE(success) << "Input: " << input;
+    output.Complete();
+    EXPECT_EQ(out_str, input) << "Input: " << input;
+  }
+
+  for (const std::string_view input : forbidden_host_chars) {
+    std::string out_str;
+    Component in_comp(0, input.size());
+    Component out_comp;
+    StdStringCanonOutput output(&out_str);
+    EXPECT_FALSE(CanonicalizeHost(input.data(), in_comp, &output, &out_comp))
+        << "Input: " << input;
+  }
+
+  for (const auto& c : escaped_host_chars) {
+    std::string out_str;
+    Component in_comp(0, c.input.size());
+    Component out_comp;
+    StdStringCanonOutput output(&out_str);
+    bool success =
+        CanonicalizeHost(c.input.data(), in_comp, &output, &out_comp);
+    EXPECT_TRUE(success) << "Input: " << c.input;
+    output.Complete();
+    EXPECT_EQ(out_str, c.expected) << "Input: " << c.input;
+  }
+}
+
 TEST(URLCanonTest, IPv4) {
   // clang-format off
   IPAddressCase cases[] = {
@@ -1130,34 +1183,30 @@
     {"ftp://me\\mydomain:pass@foo.com/", "", Component(0, -1), Component(0, -1), true},
   };
 
-  for (size_t i = 0; i < std::size(user_info_cases); i++) {
-    int url_len = static_cast<int>(strlen(user_info_cases[i].input));
+  for (const auto& user_info_case : user_info_cases) {
+    int url_len = static_cast<int>(strlen(user_info_case.input));
     Parsed parsed;
-    ParseStandardURL(user_info_cases[i].input, url_len, &parsed);
+    ParseStandardURL(user_info_case.input, url_len, &parsed);
     Component out_user, out_pass;
     std::string out_str;
     StdStringCanonOutput output1(&out_str);
 
-    bool success = CanonicalizeUserInfo(user_info_cases[i].input,
-                                        parsed.username,
-                                        user_info_cases[i].input,
-                                        parsed.password,
-                                        &output1,
-                                        &out_user,
-                                        &out_pass);
+    bool success = CanonicalizeUserInfo(user_info_case.input, parsed.username,
+                                        user_info_case.input, parsed.password,
+                                        &output1, &out_user, &out_pass);
     output1.Complete();
 
-    EXPECT_EQ(user_info_cases[i].expected_success, success);
-    EXPECT_EQ(std::string(user_info_cases[i].expected), out_str);
-    EXPECT_EQ(user_info_cases[i].expected_username.begin, out_user.begin);
-    EXPECT_EQ(user_info_cases[i].expected_username.len, out_user.len);
-    EXPECT_EQ(user_info_cases[i].expected_password.begin, out_pass.begin);
-    EXPECT_EQ(user_info_cases[i].expected_password.len, out_pass.len);
+    EXPECT_EQ(user_info_case.expected_success, success);
+    EXPECT_EQ(std::string(user_info_case.expected), out_str);
+    EXPECT_EQ(user_info_case.expected_username.begin, out_user.begin);
+    EXPECT_EQ(user_info_case.expected_username.len, out_user.len);
+    EXPECT_EQ(user_info_case.expected_password.begin, out_pass.begin);
+    EXPECT_EQ(user_info_case.expected_password.len, out_pass.len);
 
     // Now try the wide version
     out_str.clear();
     StdStringCanonOutput output2(&out_str);
-    std::u16string wide_input(gurl_base::UTF8ToUTF16(user_info_cases[i].input));
+    std::u16string wide_input(gurl_base::UTF8ToUTF16(user_info_case.input));
     success = CanonicalizeUserInfo(wide_input.c_str(),
                                    parsed.username,
                                    wide_input.c_str(),
@@ -1167,12 +1216,12 @@
                                    &out_pass);
     output2.Complete();
 
-    EXPECT_EQ(user_info_cases[i].expected_success, success);
-    EXPECT_EQ(std::string(user_info_cases[i].expected), out_str);
-    EXPECT_EQ(user_info_cases[i].expected_username.begin, out_user.begin);
-    EXPECT_EQ(user_info_cases[i].expected_username.len, out_user.len);
-    EXPECT_EQ(user_info_cases[i].expected_password.begin, out_pass.begin);
-    EXPECT_EQ(user_info_cases[i].expected_password.len, out_pass.len);
+    EXPECT_EQ(user_info_case.expected_success, success);
+    EXPECT_EQ(std::string(user_info_case.expected), out_str);
+    EXPECT_EQ(user_info_case.expected_username.begin, out_user.begin);
+    EXPECT_EQ(user_info_case.expected_username.len, out_user.len);
+    EXPECT_EQ(user_info_case.expected_password.begin, out_pass.begin);
+    EXPECT_EQ(user_info_case.expected_password.len, out_pass.len);
   }
 }
 
@@ -1199,39 +1248,33 @@
     {"80", PORT_UNSPECIFIED, ":80", Component(1, 2), true},
   };
 
-  for (size_t i = 0; i < std::size(port_cases); i++) {
-    int url_len = static_cast<int>(strlen(port_cases[i].input));
+  for (const auto& port_case : port_cases) {
+    int url_len = static_cast<int>(strlen(port_case.input));
     Component in_comp(0, url_len);
     Component out_comp;
     std::string out_str;
     StdStringCanonOutput output1(&out_str);
-    bool success = CanonicalizePort(port_cases[i].input,
-                                    in_comp,
-                                    port_cases[i].default_port,
-                                    &output1,
-                                    &out_comp);
+    bool success = CanonicalizePort(
+        port_case.input, in_comp, port_case.default_port, &output1, &out_comp);
     output1.Complete();
 
-    EXPECT_EQ(port_cases[i].expected_success, success);
-    EXPECT_EQ(std::string(port_cases[i].expected), out_str);
-    EXPECT_EQ(port_cases[i].expected_component.begin, out_comp.begin);
-    EXPECT_EQ(port_cases[i].expected_component.len, out_comp.len);
+    EXPECT_EQ(port_case.expected_success, success);
+    EXPECT_EQ(std::string(port_case.expected), out_str);
+    EXPECT_EQ(port_case.expected_component.begin, out_comp.begin);
+    EXPECT_EQ(port_case.expected_component.len, out_comp.len);
 
     // Now try the wide version
     out_str.clear();
     StdStringCanonOutput output2(&out_str);
-    std::u16string wide_input(gurl_base::UTF8ToUTF16(port_cases[i].input));
-    success = CanonicalizePort(wide_input.c_str(),
-                               in_comp,
-                               port_cases[i].default_port,
-                               &output2,
-                               &out_comp);
+    std::u16string wide_input(gurl_base::UTF8ToUTF16(port_case.input));
+    success = CanonicalizePort(wide_input.c_str(), in_comp,
+                               port_case.default_port, &output2, &out_comp);
     output2.Complete();
 
-    EXPECT_EQ(port_cases[i].expected_success, success);
-    EXPECT_EQ(std::string(port_cases[i].expected), out_str);
-    EXPECT_EQ(port_cases[i].expected_component.begin, out_comp.begin);
-    EXPECT_EQ(port_cases[i].expected_component.len, out_comp.len);
+    EXPECT_EQ(port_case.expected_success, success);
+    EXPECT_EQ(std::string(port_case.expected), out_str);
+    EXPECT_EQ(port_case.expected_component.begin, out_comp.begin);
+    EXPECT_EQ(port_case.expected_component.len, out_comp.len);
   }
 }
 
@@ -1437,34 +1480,34 @@
     {"q=\"asdf\"", L"q=\"asdf\"", "?q=%22asdf%22"},
   };
 
-  for (size_t i = 0; i < std::size(query_cases); i++) {
+  for (const auto& query_case : query_cases) {
     Component out_comp;
 
-    if (query_cases[i].input8) {
-      int len = static_cast<int>(strlen(query_cases[i].input8));
+    if (query_case.input8) {
+      int len = static_cast<int>(strlen(query_case.input8));
       Component in_comp(0, len);
       std::string out_str;
 
       StdStringCanonOutput output(&out_str);
-      CanonicalizeQuery(query_cases[i].input8, in_comp, NULL, &output,
+      CanonicalizeQuery(query_case.input8, in_comp, nullptr, &output,
                         &out_comp);
       output.Complete();
 
-      EXPECT_EQ(query_cases[i].expected, out_str);
+      EXPECT_EQ(query_case.expected, out_str);
     }
 
-    if (query_cases[i].input16) {
+    if (query_case.input16) {
       std::u16string input16(
-          test_utils::TruncateWStringToUTF16(query_cases[i].input16));
+          test_utils::TruncateWStringToUTF16(query_case.input16));
       int len = static_cast<int>(input16.length());
       Component in_comp(0, len);
       std::string out_str;
 
       StdStringCanonOutput output(&out_str);
-      CanonicalizeQuery(input16.c_str(), in_comp, NULL, &output, &out_comp);
+      CanonicalizeQuery(input16.c_str(), in_comp, nullptr, &output, &out_comp);
       output.Complete();
 
-      EXPECT_EQ(query_cases[i].expected, out_str);
+      EXPECT_EQ(query_case.expected, out_str);
     }
   }
 
@@ -1472,7 +1515,8 @@
   std::string out_str;
   StdStringCanonOutput output(&out_str);
   Component out_comp;
-  CanonicalizeQuery("a \x00z\x01", Component(0, 5), NULL, &output, &out_comp);
+  CanonicalizeQuery("a \x00z\x01", Component(0, 5), nullptr, &output,
+                    &out_comp);
   output.Complete();
   EXPECT_EQ("?a%20%00z%01", out_str);
 }
@@ -1509,27 +1553,27 @@
       {"#asdf", L"#asdf", "##asdf", Component(1, 5), true},
   };
 
-  for (size_t i = 0; i < std::size(ref_cases); i++) {
+  for (const auto& ref_case : ref_cases) {
     // 8-bit input
-    if (ref_cases[i].input8) {
-      int len = static_cast<int>(strlen(ref_cases[i].input8));
+    if (ref_case.input8) {
+      int len = static_cast<int>(strlen(ref_case.input8));
       Component in_comp(0, len);
       Component out_comp;
 
       std::string out_str;
       StdStringCanonOutput output(&out_str);
-      CanonicalizeRef(ref_cases[i].input8, in_comp, &output, &out_comp);
+      CanonicalizeRef(ref_case.input8, in_comp, &output, &out_comp);
       output.Complete();
 
-      EXPECT_EQ(ref_cases[i].expected_component.begin, out_comp.begin);
-      EXPECT_EQ(ref_cases[i].expected_component.len, out_comp.len);
-      EXPECT_EQ(ref_cases[i].expected, out_str);
+      EXPECT_EQ(ref_case.expected_component.begin, out_comp.begin);
+      EXPECT_EQ(ref_case.expected_component.len, out_comp.len);
+      EXPECT_EQ(ref_case.expected, out_str);
     }
 
     // 16-bit input
-    if (ref_cases[i].input16) {
+    if (ref_case.input16) {
       std::u16string input16(
-          test_utils::TruncateWStringToUTF16(ref_cases[i].input16));
+          test_utils::TruncateWStringToUTF16(ref_case.input16));
       int len = static_cast<int>(input16.length());
       Component in_comp(0, len);
       Component out_comp;
@@ -1539,9 +1583,9 @@
       CanonicalizeRef(input16.c_str(), in_comp, &output, &out_comp);
       output.Complete();
 
-      EXPECT_EQ(ref_cases[i].expected_component.begin, out_comp.begin);
-      EXPECT_EQ(ref_cases[i].expected_component.len, out_comp.len);
-      EXPECT_EQ(ref_cases[i].expected, out_str);
+      EXPECT_EQ(ref_case.expected_component.begin, out_comp.begin);
+      EXPECT_EQ(ref_case.expected_component.len, out_comp.len);
+      EXPECT_EQ(ref_case.expected, out_str);
     }
   }
 
@@ -1564,24 +1608,14 @@
   // The individual component canonicalize tests should have caught the cases
   // for each of those components. Here, we just need to test that the various
   // parts are included or excluded properly, and have the correct separators.
+  // clang-format off
   struct URLCase {
     const char* input;
     const char* expected;
     bool expected_success;
   } cases[] = {
-      {"http://www.google.com/foo?bar=baz#",
-       "http://www.google.com/foo?bar=baz#", true},
-      {"http://[www.google.com]/", "http://[www.google.com]/", false},
-      {"ht\ttp:@www.google.com:80/;p?#", "ht%09tp://www.google.com:80/;p?#",
-       false},
-      {"http:////////user:@google.com:99?foo", "http://user@google.com:99/?foo",
-       true},
-      {"www.google.com", ":www.google.com/", false},
-      {"http://192.0x00A80001", "http://192.168.0.1/", true},
-      {"http://www/foo%2Ehtml", "http://www/foo.html", true},
-      {"http://user:pass@/", "http://user:pass@/", false},
-      {"http://%25DOMAIN:foobar@foodomain.com/",
-       "http://%25DOMAIN:foobar@foodomain.com/", true},
+    {"http://www.google.com/foo?bar=baz#", "http://www.google.com/foo?bar=baz#",
+     true},
 
       // Backslashes should get converted to forward slashes.
       {"http:\\\\www.google.com\\foo", "http://www.google.com/foo", true},
@@ -1618,27 +1652,28 @@
       // ICU will convert to an ASCII one, generating "%81".
       {"ws:)W\x1eW\xef\xb9\xaa"
        "81:80/",
-       "ws://%29w%1ew%81/", false},
+       "ws://)w%1ew%81/", false},
       // Regression test for the last_invalid_percent_index bug described in
       // https://crbug.com/1080890#c10.
       {R"(HTTP:S/5%\../>%41)", "http://s/%3E%41", true},
   };
+  // clang-format on
 
-  for (size_t i = 0; i < std::size(cases); i++) {
-    int url_len = static_cast<int>(strlen(cases[i].input));
+  for (const auto& i : cases) {
+    int url_len = static_cast<int>(strlen(i.input));
     Parsed parsed;
-    ParseStandardURL(cases[i].input, url_len, &parsed);
+    ParseStandardURL(i.input, url_len, &parsed);
 
     Parsed out_parsed;
     std::string out_str;
     StdStringCanonOutput output(&out_str);
     bool success = CanonicalizeStandardURL(
-        cases[i].input, url_len, parsed,
-        SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, NULL, &output, &out_parsed);
+        i.input, url_len, parsed, SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION,
+        nullptr, &output, &out_parsed);
     output.Complete();
 
-    EXPECT_EQ(cases[i].expected_success, success);
-    EXPECT_EQ(cases[i].expected, out_str);
+    EXPECT_EQ(i.expected_success, success);
+    EXPECT_EQ(i.expected, out_str);
   }
 }
 
@@ -1665,8 +1700,8 @@
        "filesystem://a:b@google.com:22/foo?baz@cat"},
   };
 
-  for (size_t i = 0; i < std::size(replace_cases); i++) {
-    const ReplaceCase& cur = replace_cases[i];
+  for (const auto& replace_case : replace_cases) {
+    const ReplaceCase& cur = replace_case;
     int base_len = static_cast<int>(strlen(cur.base));
     Parsed parsed;
     ParseStandardURL(cur.base, base_len, &parsed);
@@ -1688,12 +1723,12 @@
     std::string out_str;
     StdStringCanonOutput output(&out_str);
     Parsed out_parsed;
-    ReplaceStandardURL(replace_cases[i].base, parsed, r,
-                       SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, NULL,
+    ReplaceStandardURL(replace_case.base, parsed, r,
+                       SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, nullptr,
                        &output, &out_parsed);
     output.Complete();
 
-    EXPECT_EQ(replace_cases[i].expected, out_str);
+    EXPECT_EQ(replace_case.expected, out_str);
   }
 
   // The path pointer should be ignored if the address is invalid.
@@ -1712,7 +1747,7 @@
     StdStringCanonOutput output1(&out_str1);
     Parsed new_parsed;
     ReplaceStandardURL(src, parsed, r,
-                       SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, NULL,
+                       SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, nullptr,
                        &output1, &new_parsed);
     output1.Complete();
     EXPECT_STREQ("http://www.google.com/", out_str1.c_str());
@@ -1722,7 +1757,7 @@
     std::string out_str2;
     StdStringCanonOutput output2(&out_str2);
     ReplaceStandardURL(src, parsed, r,
-                       SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, NULL,
+                       SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, nullptr,
                        &output2, &new_parsed);
     output2.Complete();
     EXPECT_STREQ("http://www.google.com/", out_str2.c_str());
@@ -1765,8 +1800,8 @@
        nullptr, nullptr, nullptr, "file:///C:/gaba?query#ref"},
   };
 
-  for (size_t i = 0; i < std::size(replace_cases); i++) {
-    const ReplaceCase& cur = replace_cases[i];
+  for (const auto& replace_case : replace_cases) {
+    const ReplaceCase& cur = replace_case;
     SCOPED_TRACE(cur.base);
     int base_len = static_cast<int>(strlen(cur.base));
     Parsed parsed;
@@ -1786,10 +1821,10 @@
     std::string out_str;
     StdStringCanonOutput output(&out_str);
     Parsed out_parsed;
-    ReplaceFileURL(cur.base, parsed, r, NULL, &output, &out_parsed);
+    ReplaceFileURL(cur.base, parsed, r, nullptr, &output, &out_parsed);
     output.Complete();
 
-    EXPECT_EQ(replace_cases[i].expected, out_str);
+    EXPECT_EQ(replace_case.expected, out_str);
   }
 }
 
@@ -1834,8 +1869,8 @@
        "filesystem:http://bar.com:40/t/gaba?query#ref"},
   };
 
-  for (size_t i = 0; i < std::size(replace_cases); i++) {
-    const ReplaceCase& cur = replace_cases[i];
+  for (const auto& replace_case : replace_cases) {
+    const ReplaceCase& cur = replace_case;
     int base_len = static_cast<int>(strlen(cur.base));
     Parsed parsed;
     ParseFileSystemURL(cur.base, base_len, &parsed);
@@ -1854,10 +1889,10 @@
     std::string out_str;
     StdStringCanonOutput output(&out_str);
     Parsed out_parsed;
-    ReplaceFileSystemURL(cur.base, parsed, r, NULL, &output, &out_parsed);
+    ReplaceFileSystemURL(cur.base, parsed, r, nullptr, &output, &out_parsed);
     output.Complete();
 
-    EXPECT_EQ(replace_cases[i].expected, out_str);
+    EXPECT_EQ(replace_case.expected, out_str);
   }
 }
 
@@ -1878,8 +1913,8 @@
        nullptr, nullptr, "data:"},
   };
 
-  for (size_t i = 0; i < std::size(replace_cases); i++) {
-    const ReplaceCase& cur = replace_cases[i];
+  for (const auto& replace_case : replace_cases) {
+    const ReplaceCase& cur = replace_case;
     int base_len = static_cast<int>(strlen(cur.base));
     Parsed parsed;
     ParsePathURL(cur.base, base_len, false, &parsed);
@@ -1901,36 +1936,46 @@
     ReplacePathURL(cur.base, parsed, r, &output, &out_parsed);
     output.Complete();
 
-    EXPECT_EQ(replace_cases[i].expected, out_str);
+    EXPECT_EQ(replace_case.expected, out_str);
   }
 }
 
 TEST(URLCanonTest, ReplaceMailtoURL) {
   ReplaceCase replace_cases[] = {
       // Replace everything
-    {"mailto:jon@foo.com?body=sup", "mailto", NULL, NULL, NULL, NULL, "addr1", "to=tony", NULL, "mailto:addr1?to=tony"},
+      {"mailto:jon@foo.com?body=sup", "mailto", nullptr, nullptr, nullptr,
+       nullptr, "addr1", "to=tony", nullptr, "mailto:addr1?to=tony"},
       // Replace nothing
-    {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "mailto:jon@foo.com?body=sup"},
+      {"mailto:jon@foo.com?body=sup", nullptr, nullptr, nullptr, nullptr,
+       nullptr, nullptr, nullptr, nullptr, "mailto:jon@foo.com?body=sup"},
       // Replace the path
-    {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, "jason", NULL, NULL, "mailto:jason?body=sup"},
+      {"mailto:jon@foo.com?body=sup", nullptr, nullptr, nullptr, nullptr,
+       nullptr, "jason", nullptr, nullptr, "mailto:jason?body=sup"},
       // Replace the query
-    {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, "custom=1", NULL, "mailto:jon@foo.com?custom=1"},
+      {"mailto:jon@foo.com?body=sup", nullptr, nullptr, nullptr, nullptr,
+       nullptr, nullptr, "custom=1", nullptr, "mailto:jon@foo.com?custom=1"},
       // Replace the path and query
-    {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, "jason", "custom=1", NULL, "mailto:jason?custom=1"},
+      {"mailto:jon@foo.com?body=sup", nullptr, nullptr, nullptr, nullptr,
+       nullptr, "jason", "custom=1", nullptr, "mailto:jason?custom=1"},
       // Set the query to empty (should leave trailing question mark)
-    {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, "", NULL, "mailto:jon@foo.com?"},
+      {"mailto:jon@foo.com?body=sup", nullptr, nullptr, nullptr, nullptr,
+       nullptr, nullptr, "", nullptr, "mailto:jon@foo.com?"},
       // Clear the query
-    {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, "|", NULL, "mailto:jon@foo.com"},
+      {"mailto:jon@foo.com?body=sup", nullptr, nullptr, nullptr, nullptr,
+       nullptr, nullptr, "|", nullptr, "mailto:jon@foo.com"},
       // Clear the path
-    {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, "|", NULL, NULL, "mailto:?body=sup"},
+      {"mailto:jon@foo.com?body=sup", nullptr, nullptr, nullptr, nullptr,
+       nullptr, "|", nullptr, nullptr, "mailto:?body=sup"},
       // Clear the path + query
-    {"mailto:", NULL, NULL, NULL, NULL, NULL, "|", "|", NULL, "mailto:"},
+      {"mailto:", nullptr, nullptr, nullptr, nullptr, nullptr, "|", "|",
+       nullptr, "mailto:"},
       // Setting the ref should have no effect
-    {"mailto:addr1", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "BLAH", "mailto:addr1"},
+      {"mailto:addr1", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+       nullptr, "BLAH", "mailto:addr1"},
   };
 
-  for (size_t i = 0; i < std::size(replace_cases); i++) {
-    const ReplaceCase& cur = replace_cases[i];
+  for (const auto& replace_case : replace_cases) {
+    const ReplaceCase& cur = replace_case;
     int base_len = static_cast<int>(strlen(cur.base));
     Parsed parsed;
     ParseMailtoURL(cur.base, base_len, &parsed);
@@ -1952,7 +1997,7 @@
     ReplaceMailtoURL(cur.base, parsed, r, &output, &out_parsed);
     output.Complete();
 
-    EXPECT_EQ(replace_cases[i].expected, out_str);
+    EXPECT_EQ(replace_case.expected, out_str);
   }
 }
 
@@ -2042,31 +2087,31 @@
 #endif  // _WIN32
   };
 
-  for (size_t i = 0; i < std::size(cases); i++) {
-    int url_len = static_cast<int>(strlen(cases[i].input));
+  for (const auto& i : cases) {
+    int url_len = static_cast<int>(strlen(i.input));
     Parsed parsed;
-    ParseFileURL(cases[i].input, url_len, &parsed);
+    ParseFileURL(i.input, url_len, &parsed);
 
     Parsed out_parsed;
     std::string out_str;
     StdStringCanonOutput output(&out_str);
-    bool success = CanonicalizeFileURL(cases[i].input, url_len, parsed, NULL,
+    bool success = CanonicalizeFileURL(i.input, url_len, parsed, nullptr,
                                        &output, &out_parsed);
     output.Complete();
 
-    EXPECT_EQ(cases[i].expected_success, success);
-    EXPECT_EQ(cases[i].expected, out_str);
+    EXPECT_EQ(i.expected_success, success);
+    EXPECT_EQ(i.expected, out_str);
 
     // Make sure the spec was properly identified, the file canonicalizer has
     // different code for writing the spec.
     EXPECT_EQ(0, out_parsed.scheme.begin);
     EXPECT_EQ(4, out_parsed.scheme.len);
 
-    EXPECT_EQ(cases[i].expected_host.begin, out_parsed.host.begin);
-    EXPECT_EQ(cases[i].expected_host.len, out_parsed.host.len);
+    EXPECT_EQ(i.expected_host.begin, out_parsed.host.begin);
+    EXPECT_EQ(i.expected_host.len, out_parsed.host.len);
 
-    EXPECT_EQ(cases[i].expected_path.begin, out_parsed.path.begin);
-    EXPECT_EQ(cases[i].expected_path.len, out_parsed.path.len);
+    EXPECT_EQ(i.expected_path.begin, out_parsed.path.begin);
+    EXPECT_EQ(i.expected_path.len, out_parsed.path.len);
   }
 }
 
@@ -2088,23 +2133,23 @@
       {"filesystem:fiLe:///temporary", "filesystem:file:///temporary/", true},
       {"filesystem:File:///temporary/Bob?qUery#reF",
        "filesystem:file:///temporary/Bob?qUery#reF", true},
-      {"FilEsysteM:htTp:E=/.", "filesystem:http://e%3D//", false},
+      {"FilEsysteM:htTp:E=/.", "filesystem:http://e=//", false},
   };
 
-  for (size_t i = 0; i < std::size(cases); i++) {
-    int url_len = static_cast<int>(strlen(cases[i].input));
+  for (const auto& i : cases) {
+    int url_len = static_cast<int>(strlen(i.input));
     Parsed parsed;
-    ParseFileSystemURL(cases[i].input, url_len, &parsed);
+    ParseFileSystemURL(i.input, url_len, &parsed);
 
     Parsed out_parsed;
     std::string out_str;
     StdStringCanonOutput output(&out_str);
-    bool success = CanonicalizeFileSystemURL(cases[i].input, url_len, parsed,
-                                             NULL, &output, &out_parsed);
+    bool success = CanonicalizeFileSystemURL(i.input, url_len, parsed, nullptr,
+                                             &output, &out_parsed);
     output.Complete();
 
-    EXPECT_EQ(cases[i].expected_success, success);
-    EXPECT_EQ(cases[i].expected, out_str);
+    EXPECT_EQ(i.expected_success, success);
+    EXPECT_EQ(i.expected, out_str);
 
     // Make sure the spec was properly identified, the filesystem canonicalizer
     // has different code for writing the spec.
@@ -2130,26 +2175,26 @@
       {"javascript:\uFFFF", "javascript:%EF%BF%BF"},
   };
 
-  for (size_t i = 0; i < std::size(path_cases); i++) {
-    int url_len = static_cast<int>(strlen(path_cases[i].input));
+  for (const auto& path_case : path_cases) {
+    int url_len = static_cast<int>(strlen(path_case.input));
     Parsed parsed;
-    ParsePathURL(path_cases[i].input, url_len, true, &parsed);
+    ParsePathURL(path_case.input, url_len, true, &parsed);
 
     Parsed out_parsed;
     std::string out_str;
     StdStringCanonOutput output(&out_str);
-    bool success = CanonicalizePathURL(path_cases[i].input, url_len, parsed,
+    bool success = CanonicalizePathURL(path_case.input, url_len, parsed,
                                        &output, &out_parsed);
     output.Complete();
 
     EXPECT_TRUE(success);
-    EXPECT_EQ(path_cases[i].expected, out_str);
+    EXPECT_EQ(path_case.expected, out_str);
 
     EXPECT_EQ(0, out_parsed.host.begin);
     EXPECT_EQ(-1, out_parsed.host.len);
 
     // When we end with a colon at the end, there should be no path.
-    if (path_cases[i].input[url_len - 1] == ':') {
+    if (path_case.input[url_len - 1] == ':') {
       EXPECT_EQ(0, out_parsed.GetContent().begin);
       EXPECT_EQ(-1, out_parsed.GetContent().len);
     }
@@ -2168,20 +2213,20 @@
       {"\uFFFF", L"\uFFFF", "%EF%BF%BF"},
   };
 
-  for (size_t i = 0; i < std::size(path_cases); i++) {
+  for (const auto& path_case : path_cases) {
     // 8-bit string input
     std::string out_str;
     StdStringCanonOutput output(&out_str);
     url::Component out_component;
-    CanonicalizePathURLPath(path_cases[i].input.data(),
-                            Component(0, path_cases[i].input.size()), &output,
+    CanonicalizePathURLPath(path_case.input.data(),
+                            Component(0, path_case.input.size()), &output,
                             &out_component);
     output.Complete();
 
-    EXPECT_EQ(path_cases[i].expected, out_str);
+    EXPECT_EQ(path_case.expected, out_str);
 
     EXPECT_EQ(0, out_component.begin);
-    EXPECT_EQ(path_cases[i].expected.size(),
+    EXPECT_EQ(path_case.expected.size(),
               static_cast<size_t>(out_component.len));
 
     // 16-bit string input
@@ -2189,16 +2234,16 @@
     StdStringCanonOutput output16(&out_str16);
     url::Component out_component16;
     std::u16string input16(
-        test_utils::TruncateWStringToUTF16(path_cases[i].input16.data()));
+        test_utils::TruncateWStringToUTF16(path_case.input16.data()));
     CanonicalizePathURLPath(input16.c_str(),
-                            Component(0, path_cases[i].input16.size()),
-                            &output16, &out_component16);
+                            Component(0, path_case.input16.size()), &output16,
+                            &out_component16);
     output16.Complete();
 
-    EXPECT_EQ(path_cases[i].expected, out_str16);
+    EXPECT_EQ(path_case.expected, out_str16);
 
     EXPECT_EQ(0, out_component16.begin);
-    EXPECT_EQ(path_cases[i].expected.size(),
+    EXPECT_EQ(path_case.expected.size(),
               static_cast<size_t>(out_component16.len));
   }
 }
@@ -2393,148 +2438,209 @@
     const char* resolved;  // What we expect in the result when resolving.
   } rel_cases[] = {
       // Basic absolute input.
-    {"http://host/a", true, false, "http://another/", true, false, false, NULL},
-    {"http://host/a", true, false, "http:////another/", true, false, false, NULL},
+      {"http://host/a", true, false, "http://another/", true, false, false,
+       nullptr},
+      {"http://host/a", true, false, "http:////another/", true, false, false,
+       nullptr},
       // Empty relative URLs should only remove the ref part of the URL,
       // leaving the rest unchanged.
-    {"http://foo/bar", true, false, "", true, true, true, "http://foo/bar"},
-    {"http://foo/bar#ref", true, false, "", true, true, true, "http://foo/bar"},
-    {"http://foo/bar#", true, false, "", true, true, true, "http://foo/bar"},
+      {"http://foo/bar", true, false, "", true, true, true, "http://foo/bar"},
+      {"http://foo/bar#ref", true, false, "", true, true, true,
+       "http://foo/bar"},
+      {"http://foo/bar#", true, false, "", true, true, true, "http://foo/bar"},
       // Spaces at the ends of the relative path should be ignored.
-    {"http://foo/bar", true, false, "  another  ", true, true, true, "http://foo/another"},
-    {"http://foo/bar", true, false, "  .  ", true, true, true, "http://foo/"},
-    {"http://foo/bar", true, false, " \t ", true, true, true, "http://foo/bar"},
+      {"http://foo/bar", true, false, "  another  ", true, true, true,
+       "http://foo/another"},
+      {"http://foo/bar", true, false, "  .  ", true, true, true, "http://foo/"},
+      {"http://foo/bar", true, false, " \t ", true, true, true,
+       "http://foo/bar"},
       // Matching schemes without two slashes are treated as relative.
-    {"http://host/a", true, false, "http:path", true, true, true, "http://host/path"},
-    {"http://host/a/", true, false, "http:path", true, true, true, "http://host/a/path"},
-    {"http://host/a", true, false, "http:/path", true, true, true, "http://host/path"},
-    {"http://host/a", true, false, "HTTP:/path", true, true, true, "http://host/path"},
+      {"http://host/a", true, false, "http:path", true, true, true,
+       "http://host/path"},
+      {"http://host/a/", true, false, "http:path", true, true, true,
+       "http://host/a/path"},
+      {"http://host/a", true, false, "http:/path", true, true, true,
+       "http://host/path"},
+      {"http://host/a", true, false, "HTTP:/path", true, true, true,
+       "http://host/path"},
       // Nonmatching schemes are absolute.
-    {"http://host/a", true, false, "https:host2", true, false, false, NULL},
-    {"http://host/a", true, false, "htto:/host2", true, false, false, NULL},
+      {"http://host/a", true, false, "https:host2", true, false, false,
+       nullptr},
+      {"http://host/a", true, false, "htto:/host2", true, false, false,
+       nullptr},
       // Absolute path input
-    {"http://host/a", true, false, "/b/c/d", true, true, true, "http://host/b/c/d"},
-    {"http://host/a", true, false, "\\b\\c\\d", true, true, true, "http://host/b/c/d"},
-    {"http://host/a", true, false, "/b/../c", true, true, true, "http://host/c"},
-    {"http://host/a?b#c", true, false, "/b/../c", true, true, true, "http://host/c"},
-    {"http://host/a", true, false, "\\b/../c?x#y", true, true, true, "http://host/c?x#y"},
-    {"http://host/a?b#c", true, false, "/b/../c?x#y", true, true, true, "http://host/c?x#y"},
+      {"http://host/a", true, false, "/b/c/d", true, true, true,
+       "http://host/b/c/d"},
+      {"http://host/a", true, false, "\\b\\c\\d", true, true, true,
+       "http://host/b/c/d"},
+      {"http://host/a", true, false, "/b/../c", true, true, true,
+       "http://host/c"},
+      {"http://host/a?b#c", true, false, "/b/../c", true, true, true,
+       "http://host/c"},
+      {"http://host/a", true, false, "\\b/../c?x#y", true, true, true,
+       "http://host/c?x#y"},
+      {"http://host/a?b#c", true, false, "/b/../c?x#y", true, true, true,
+       "http://host/c?x#y"},
       // Relative path input
-    {"http://host/a", true, false, "b", true, true, true, "http://host/b"},
-    {"http://host/a", true, false, "bc/de", true, true, true, "http://host/bc/de"},
-    {"http://host/a/", true, false, "bc/de?query#ref", true, true, true, "http://host/a/bc/de?query#ref"},
-    {"http://host/a/", true, false, ".", true, true, true, "http://host/a/"},
-    {"http://host/a/", true, false, "..", true, true, true, "http://host/"},
-    {"http://host/a/", true, false, "./..", true, true, true, "http://host/"},
-    {"http://host/a/", true, false, "../.", true, true, true, "http://host/"},
-    {"http://host/a/", true, false, "././.", true, true, true, "http://host/a/"},
-    {"http://host/a?query#ref", true, false, "../../../foo", true, true, true, "http://host/foo"},
+      {"http://host/a", true, false, "b", true, true, true, "http://host/b"},
+      {"http://host/a", true, false, "bc/de", true, true, true,
+       "http://host/bc/de"},
+      {"http://host/a/", true, false, "bc/de?query#ref", true, true, true,
+       "http://host/a/bc/de?query#ref"},
+      {"http://host/a/", true, false, ".", true, true, true, "http://host/a/"},
+      {"http://host/a/", true, false, "..", true, true, true, "http://host/"},
+      {"http://host/a/", true, false, "./..", true, true, true, "http://host/"},
+      {"http://host/a/", true, false, "../.", true, true, true, "http://host/"},
+      {"http://host/a/", true, false, "././.", true, true, true,
+       "http://host/a/"},
+      {"http://host/a?query#ref", true, false, "../../../foo", true, true, true,
+       "http://host/foo"},
       // Query input
-    {"http://host/a", true, false, "?foo=bar", true, true, true, "http://host/a?foo=bar"},
-    {"http://host/a?x=y#z", true, false, "?", true, true, true, "http://host/a?"},
-    {"http://host/a?x=y#z", true, false, "?foo=bar#com", true, true, true, "http://host/a?foo=bar#com"},
+      {"http://host/a", true, false, "?foo=bar", true, true, true,
+       "http://host/a?foo=bar"},
+      {"http://host/a?x=y#z", true, false, "?", true, true, true,
+       "http://host/a?"},
+      {"http://host/a?x=y#z", true, false, "?foo=bar#com", true, true, true,
+       "http://host/a?foo=bar#com"},
       // Ref input
-    {"http://host/a", true, false, "#ref", true, true, true, "http://host/a#ref"},
-    {"http://host/a#b", true, false, "#", true, true, true, "http://host/a#"},
-    {"http://host/a?foo=bar#hello", true, false, "#bye", true, true, true, "http://host/a?foo=bar#bye"},
+      {"http://host/a", true, false, "#ref", true, true, true,
+       "http://host/a#ref"},
+      {"http://host/a#b", true, false, "#", true, true, true, "http://host/a#"},
+      {"http://host/a?foo=bar#hello", true, false, "#bye", true, true, true,
+       "http://host/a?foo=bar#bye"},
       // Non-hierarchical base: no relative handling. Relative input should
       // error, and if a scheme is present, it should be treated as absolute.
-    {"data:foobar", false, false, "baz.html", false, false, false, NULL},
-    {"data:foobar", false, false, "data:baz", true, false, false, NULL},
-    {"data:foobar", false, false, "data:/base", true, false, false, NULL},
+      {"data:foobar", false, false, "baz.html", false, false, false, nullptr},
+      {"data:foobar", false, false, "data:baz", true, false, false, nullptr},
+      {"data:foobar", false, false, "data:/base", true, false, false, nullptr},
       // Non-hierarchical base: absolute input should succeed.
-    {"data:foobar", false, false, "http://host/", true, false, false, NULL},
-    {"data:foobar", false, false, "http:host", true, false, false, NULL},
+      {"data:foobar", false, false, "http://host/", true, false, false,
+       nullptr},
+      {"data:foobar", false, false, "http:host", true, false, false, nullptr},
       // Non-hierarchical base: empty URL should give error.
-    {"data:foobar", false, false, "", false, false, false, NULL},
+      {"data:foobar", false, false, "", false, false, false, nullptr},
       // Invalid schemes should be treated as relative.
-    {"http://foo/bar", true, false, "./asd:fgh", true, true, true, "http://foo/asd:fgh"},
-    {"http://foo/bar", true, false, ":foo", true, true, true, "http://foo/:foo"},
-    {"http://foo/bar", true, false, " hello world", true, true, true, "http://foo/hello%20world"},
-    {"data:asdf", false, false, ":foo", false, false, false, NULL},
-    {"data:asdf", false, false, "bad(':foo')", false, false, false, NULL},
+      {"http://foo/bar", true, false, "./asd:fgh", true, true, true,
+       "http://foo/asd:fgh"},
+      {"http://foo/bar", true, false, ":foo", true, true, true,
+       "http://foo/:foo"},
+      {"http://foo/bar", true, false, " hello world", true, true, true,
+       "http://foo/hello%20world"},
+      {"data:asdf", false, false, ":foo", false, false, false, nullptr},
+      {"data:asdf", false, false, "bad(':foo')", false, false, false, nullptr},
       // We should treat semicolons like any other character in URL resolving
-    {"http://host/a", true, false, ";foo", true, true, true, "http://host/;foo"},
-    {"http://host/a;", true, false, ";foo", true, true, true, "http://host/;foo"},
-    {"http://host/a", true, false, ";/../bar", true, true, true, "http://host/bar"},
+      {"http://host/a", true, false, ";foo", true, true, true,
+       "http://host/;foo"},
+      {"http://host/a;", true, false, ";foo", true, true, true,
+       "http://host/;foo"},
+      {"http://host/a", true, false, ";/../bar", true, true, true,
+       "http://host/bar"},
       // Relative URLs can also be written as "//foo/bar" which is relative to
       // the scheme. In this case, it would take the old scheme, so for http
       // the example would resolve to "http://foo/bar".
-    {"http://host/a", true, false, "//another", true, true, true, "http://another/"},
-    {"http://host/a", true, false, "//another/path?query#ref", true, true, true, "http://another/path?query#ref"},
-    {"http://host/a", true, false, "///another/path", true, true, true, "http://another/path"},
-    {"http://host/a", true, false, "//Another\\path", true, true, true, "http://another/path"},
-    {"http://host/a", true, false, "//", true, true, false, "http:"},
+      {"http://host/a", true, false, "//another", true, true, true,
+       "http://another/"},
+      {"http://host/a", true, false, "//another/path?query#ref", true, true,
+       true, "http://another/path?query#ref"},
+      {"http://host/a", true, false, "///another/path", true, true, true,
+       "http://another/path"},
+      {"http://host/a", true, false, "//Another\\path", true, true, true,
+       "http://another/path"},
+      {"http://host/a", true, false, "//", true, true, false, "http:"},
       // IE will also allow one or the other to be a backslash to get the same
       // behavior.
-    {"http://host/a", true, false, "\\/another/path", true, true, true, "http://another/path"},
-    {"http://host/a", true, false, "/\\Another\\path", true, true, true, "http://another/path"},
+      {"http://host/a", true, false, "\\/another/path", true, true, true,
+       "http://another/path"},
+      {"http://host/a", true, false, "/\\Another\\path", true, true, true,
+       "http://another/path"},
 #ifdef WIN32
       // Resolving against Windows file base URLs.
-    {"file:///C:/foo", true, true, "http://host/", true, false, false, NULL},
-    {"file:///C:/foo", true, true, "bar", true, true, true, "file:///C:/bar"},
-    {"file:///C:/foo", true, true, "../../../bar.html", true, true, true, "file:///C:/bar.html"},
-    {"file:///C:/foo", true, true, "/../bar.html", true, true, true, "file:///C:/bar.html"},
+      {"file:///C:/foo", true, true, "http://host/", true, false, false,
+       nullptr},
+      {"file:///C:/foo", true, true, "bar", true, true, true, "file:///C:/bar"},
+      {"file:///C:/foo", true, true, "../../../bar.html", true, true, true,
+       "file:///C:/bar.html"},
+      {"file:///C:/foo", true, true, "/../bar.html", true, true, true,
+       "file:///C:/bar.html"},
       // But two backslashes on Windows should be UNC so should be treated
       // as absolute.
-    {"http://host/a", true, false, "\\\\another\\path", true, false, false, NULL},
+      {"http://host/a", true, false, "\\\\another\\path", true, false, false,
+       nullptr},
       // IE doesn't support drive specs starting with two slashes. It fails
       // immediately and doesn't even try to load. We fix it up to either
       // an absolute path or UNC depending on what it looks like.
-    {"file:///C:/something", true, true, "//c:/foo", true, true, true, "file:///C:/foo"},
-    {"file:///C:/something", true, true, "//localhost/c:/foo", true, true, true, "file:///C:/foo"},
+      {"file:///C:/something", true, true, "//c:/foo", true, true, true,
+       "file:///C:/foo"},
+      {"file:///C:/something", true, true, "//localhost/c:/foo", true, true,
+       true, "file:///C:/foo"},
       // Windows drive specs should be allowed and treated as absolute.
-    {"file:///C:/foo", true, true, "c:", true, false, false, NULL},
-    {"file:///C:/foo", true, true, "c:/foo", true, false, false, NULL},
-    {"http://host/a", true, false, "c:\\foo", true, false, false, NULL},
+      {"file:///C:/foo", true, true, "c:", true, false, false, nullptr},
+      {"file:///C:/foo", true, true, "c:/foo", true, false, false, nullptr},
+      {"http://host/a", true, false, "c:\\foo", true, false, false, nullptr},
       // Relative paths with drive letters should be allowed when the base is
       // also a file.
-    {"file:///C:/foo", true, true, "/z:/bar", true, true, true, "file:///Z:/bar"},
+      {"file:///C:/foo", true, true, "/z:/bar", true, true, true,
+       "file:///Z:/bar"},
       // Treat absolute paths as being off of the drive.
-    {"file:///C:/foo", true, true, "/bar", true, true, true, "file:///C:/bar"},
-    {"file://localhost/C:/foo", true, true, "/bar", true, true, true, "file://localhost/C:/bar"},
-    {"file:///C:/foo/com/", true, true, "/bar", true, true, true, "file:///C:/bar"},
+      {"file:///C:/foo", true, true, "/bar", true, true, true,
+       "file:///C:/bar"},
+      {"file://localhost/C:/foo", true, true, "/bar", true, true, true,
+       "file://localhost/C:/bar"},
+      {"file:///C:/foo/com/", true, true, "/bar", true, true, true,
+       "file:///C:/bar"},
       // On Windows, two slashes without a drive letter when the base is a file
       // means that the path is UNC.
-    {"file:///C:/something", true, true, "//somehost/path", true, true, true, "file://somehost/path"},
-    {"file:///C:/something", true, true, "/\\//somehost/path", true, true, true, "file://somehost/path"},
+      {"file:///C:/something", true, true, "//somehost/path", true, true, true,
+       "file://somehost/path"},
+      {"file:///C:/something", true, true, "/\\//somehost/path", true, true,
+       true, "file://somehost/path"},
 #else
       // On Unix we fall back to relative behavior since there's nothing else
       // reasonable to do.
-    {"http://host/a", true, false, "\\\\Another\\path", true, true, true, "http://another/path"},
+      {"http://host/a", true, false, "\\\\Another\\path", true, true, true,
+       "http://another/path"},
 #endif
       // Even on Windows, we don't allow relative drive specs when the base
       // is not file.
-    {"http://host/a", true, false, "/c:\\foo", true, true, true, "http://host/c:/foo"},
-    {"http://host/a", true, false, "//c:\\foo", true, true, true, "http://c/foo"},
+      {"http://host/a", true, false, "/c:\\foo", true, true, true,
+       "http://host/c:/foo"},
+      {"http://host/a", true, false, "//c:\\foo", true, true, true,
+       "http://c/foo"},
       // Cross-platform relative file: resolution behavior.
-    {"file://host/a", true, true, "/", true, true, true, "file://host/"},
-    {"file://host/a", true, true, "//", true, true, true, "file:///"},
-    {"file://host/a", true, true, "/b", true, true, true, "file://host/b"},
-    {"file://host/a", true, true, "//b", true, true, true, "file://b/"},
+      {"file://host/a", true, true, "/", true, true, true, "file://host/"},
+      {"file://host/a", true, true, "//", true, true, true, "file:///"},
+      {"file://host/a", true, true, "/b", true, true, true, "file://host/b"},
+      {"file://host/a", true, true, "//b", true, true, true, "file://b/"},
       // Ensure that ports aren't allowed for hosts relative to a file url.
       // Although the result string shows a host:port portion, the call to
       // resolve the relative URL returns false, indicating parse failure,
       // which is what is required.
-    {"file:///foo.txt", true, true, "//host:80/bar.txt", true, true, false, "file://host:80/bar.txt"},
+      {"file:///foo.txt", true, true, "//host:80/bar.txt", true, true, false,
+       "file://host:80/bar.txt"},
       // Filesystem URL tests; filesystem URLs are only valid and relative if
       // they have no scheme, e.g. "./index.html". There's no valid equivalent
       // to http:index.html.
-    {"filesystem:http://host/t/path", true, false, "filesystem:http://host/t/path2", true, false, false, NULL},
-    {"filesystem:http://host/t/path", true, false, "filesystem:https://host/t/path2", true, false, false, NULL},
-    {"filesystem:http://host/t/path", true, false, "http://host/t/path2", true, false, false, NULL},
-    {"http://host/t/path", true, false, "filesystem:http://host/t/path2", true, false, false, NULL},
-    {"filesystem:http://host/t/path", true, false, "./path2", true, true, true, "filesystem:http://host/t/path2"},
-    {"filesystem:http://host/t/path/", true, false, "path2", true, true, true, "filesystem:http://host/t/path/path2"},
-    {"filesystem:http://host/t/path", true, false, "filesystem:http:path2", true, false, false, NULL},
+      {"filesystem:http://host/t/path", true, false,
+       "filesystem:http://host/t/path2", true, false, false, nullptr},
+      {"filesystem:http://host/t/path", true, false,
+       "filesystem:https://host/t/path2", true, false, false, nullptr},
+      {"filesystem:http://host/t/path", true, false, "http://host/t/path2",
+       true, false, false, nullptr},
+      {"http://host/t/path", true, false, "filesystem:http://host/t/path2",
+       true, false, false, nullptr},
+      {"filesystem:http://host/t/path", true, false, "./path2", true, true,
+       true, "filesystem:http://host/t/path2"},
+      {"filesystem:http://host/t/path/", true, false, "path2", true, true, true,
+       "filesystem:http://host/t/path/path2"},
+      {"filesystem:http://host/t/path", true, false, "filesystem:http:path2",
+       true, false, false, nullptr},
       // Absolute URLs are still not relative to a non-standard base URL.
-    {"about:blank", false, false, "http://X/A", true, false, true, ""},
-    {"about:blank", false, false, "content://content.Provider/", true, false, true, ""},
+      {"about:blank", false, false, "http://X/A", true, false, true, ""},
+      {"about:blank", false, false, "content://content.Provider/", true, false,
+       true, ""},
   };
 
-  for (size_t i = 0; i < std::size(rel_cases); i++) {
-    const RelativeCase& cur_case = rel_cases[i];
-
+  for (const auto& cur_case : rel_cases) {
     Parsed parsed;
     int base_len = static_cast<int>(strlen(cur_case.base));
     if (cur_case.is_base_file)
@@ -2564,7 +2670,7 @@
 
       bool succeed_resolve = ResolveRelativeURL(
           cur_case.base, parsed, cur_case.is_base_file, cur_case.test,
-          relative_component, NULL, &output, &resolved_parsed);
+          relative_component, nullptr, &output, &resolved_parsed);
       output.Complete();
 
       EXPECT_EQ(cur_case.succeed_resolve, succeed_resolve);
@@ -2614,7 +2720,7 @@
   Parsed repl_parsed;
   std::string repl_str;
   StdStringCanonOutput repl_output(&repl_str);
-  ReplaceFileURL(src, parsed, repl, NULL, &repl_output, &repl_parsed);
+  ReplaceFileURL(src, parsed, repl, nullptr, &repl_output, &repl_parsed);
   repl_output.Complete();
 
   // Generate the expected string and check.
@@ -2642,7 +2748,7 @@
       {"WSS", PORT_UNSPECIFIED},
   };
 
-  for (auto& test_case : cases) {
+  for (const auto& test_case : cases) {
     SCOPED_TRACE(test_case.scheme);
     EXPECT_EQ(test_case.expected_port,
               DefaultPortForScheme(test_case.scheme, strlen(test_case.scheme)));
@@ -2651,7 +2757,7 @@
 
 TEST(URLCanonTest, FindWindowsDriveLetter) {
   struct TestCase {
-    gurl_base::StringPiece spec;
+    std::string_view spec;
     int begin;
     int end;  // -1 for end of spec
     int expected_drive_letter_pos;
@@ -2695,33 +2801,33 @@
 
   // Basic ASCII test.
   std::u16string str = u"hello";
-  EXPECT_TRUE(IDNToASCII(str.data(), str.length(), &output));
+  EXPECT_TRUE(IDNToASCII(str, &output));
   EXPECT_EQ(u"hello", std::u16string(output.data()));
   output.set_length(0);
 
   // Mixed ASCII/non-ASCII.
   str = u"hellö";
-  EXPECT_TRUE(IDNToASCII(str.data(), str.length(), &output));
+  EXPECT_TRUE(IDNToASCII(str, &output));
   EXPECT_EQ(u"xn--hell-8qa", std::u16string(output.data()));
   output.set_length(0);
 
   // All non-ASCII.
   str = u"你好";
-  EXPECT_TRUE(IDNToASCII(str.data(), str.length(), &output));
+  EXPECT_TRUE(IDNToASCII(str, &output));
   EXPECT_EQ(u"xn--6qq79v", std::u16string(output.data()));
   output.set_length(0);
 
   // Characters that need mapping (the resulting Punycode is the encoding for
   // "1⁄4").
   str = u"¼";
-  EXPECT_TRUE(IDNToASCII(str.data(), str.length(), &output));
+  EXPECT_TRUE(IDNToASCII(str, &output));
   EXPECT_EQ(u"xn--14-c6t", std::u16string(output.data()));
   output.set_length(0);
 
   // String to encode already starts with "xn--", and all ASCII. Should not
   // modify the string.
   str = u"xn--hell-8qa";
-  EXPECT_TRUE(IDNToASCII(str.data(), str.length(), &output));
+  EXPECT_TRUE(IDNToASCII(str, &output));
   EXPECT_EQ(u"xn--hell-8qa", std::u16string(output.data()));
   output.set_length(0);
 
@@ -2729,7 +2835,7 @@
   // Should fail, due to a special case: if the label starts with "xn--", it
   // should be parsed as Punycode, which must be all ASCII.
   str = u"xn--hellö";
-  EXPECT_FALSE(IDNToASCII(str.data(), str.length(), &output));
+  EXPECT_FALSE(IDNToASCII(str, &output));
   output.set_length(0);
 
   // String to encode already starts with "xn--", and mixed ASCII/non-ASCII.
@@ -2737,7 +2843,7 @@
   // which would be a valid ASCII character, U+0044, if the high byte were
   // ignored.
   str = u"xn--1⁄4";
-  EXPECT_FALSE(IDNToASCII(str.data(), str.length(), &output));
+  EXPECT_FALSE(IDNToASCII(str, &output));
   output.set_length(0);
 }
 
@@ -2765,7 +2871,7 @@
 
 TEST_P(URLCanonAsciiPercentEncodePathTest, UnescapePathCharHistogram) {
   struct TestCase {
-    gurl_base::StringPiece path;
+    std::string_view path;
     gurl_base::HistogramBase::Count cnt;
   } cases[] = {
       {"/a", 0},
diff --git a/url/url_features.cc b/url/url_features.cc
index e046f60..9c9827b 100644
--- a/url/url_features.cc
+++ b/url/url_features.cc
@@ -30,6 +30,11 @@
              "DontDecodeAsciiPercentEncodedURLPath",
              gurl_base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Kill switch for https://crbug.com/1416013.
+BASE_FEATURE(kStandardCompliantHostCharacters,
+             "StandardCompliantHostCharacters",
+             gurl_base::FEATURE_ENABLED_BY_DEFAULT);
+
 bool IsUsingIDNA2008NonTransitional() {
   // If the FeatureList isn't available yet, fall back to the feature's default
   // state. This may happen during early startup, see crbug.com/1441956.
@@ -41,6 +46,28 @@
   return gurl_base::FeatureList::IsEnabled(kUseIDNA2008NonTransitional);
 }
 
+bool IsUsingDontDecodeAsciiPercentEncodedURLPath() {
+  // If the FeatureList isn't available yet, fall back to the feature's default
+  // state. This may happen during early startup, see https://crbug.com/1478960.
+  if (!gurl_base::FeatureList::GetInstance()) {
+    return kDontDecodeAsciiPercentEncodedURLPath.default_state ==
+           gurl_base::FEATURE_ENABLED_BY_DEFAULT;
+  }
+
+  return gurl_base::FeatureList::IsEnabled(kDontDecodeAsciiPercentEncodedURLPath);
+}
+
+bool IsUsingStandardCompliantHostCharacters() {
+  // If the FeatureList isn't available yet, fall back to the feature's default
+  // state. This may happen during early startup, see crbug.com/1441956.
+  if (!gurl_base::FeatureList::GetInstance()) {
+    return kStandardCompliantHostCharacters.default_state ==
+           gurl_base::FEATURE_ENABLED_BY_DEFAULT;
+  }
+
+  return gurl_base::FeatureList::IsEnabled(kStandardCompliantHostCharacters);
+}
+
 bool IsRecordingIDNA2008Metrics() {
   return gurl_base::FeatureList::IsEnabled(kRecordIDNA2008Metrics);
 }
diff --git a/url/url_features.h b/url/url_features.h
index 3dfb383..55b3234 100644
--- a/url/url_features.h
+++ b/url/url_features.h
@@ -18,6 +18,14 @@
 // Returns true if Chrome is recording IDNA 2008 related metrics.
 COMPONENT_EXPORT(URL) bool IsRecordingIDNA2008Metrics();
 
+// Returns true if kDontDecodeAsciiPercentEncodedURLPath feature is enabled.
+// See url::kDontDecodeAsciiPercentEncodedURLPath for details.
+COMPONENT_EXPORT(URL) bool IsUsingDontDecodeAsciiPercentEncodedURLPath();
+
+// Returns true if kDontDecodeAsciiPercentEncodedURLPath feature is enabled.
+// See url::kStandardCompliantHostCharacters for details.
+COMPONENT_EXPORT(URL) bool IsUsingStandardCompliantHostCharacters();
+
 // Returns true if Chrome is enforcing the 4 part check for IPv4 embedded IPv6
 // addresses.
 COMPONENT_EXPORT(URL)
@@ -33,6 +41,12 @@
 COMPONENT_EXPORT(URL)
 BASE_DECLARE_FEATURE(kDontDecodeAsciiPercentEncodedURLPath);
 
+// When enabled, Chrome uses URL Standard compliant mode to
+// handle punctuation characters in URL host part.
+// https://crbug.com/1416013 for details.
+COMPONENT_EXPORT(URL)
+BASE_DECLARE_FEATURE(kStandardCompliantHostCharacters);
+
 }  // namespace url
 
 #endif  // URL_URL_FEATURES_H_
diff --git a/url/url_idna_ascii_only.cc b/url/url_idna_ascii_only.cc
index 2a4f0d3..6a864bb 100644
--- a/url/url_idna_ascii_only.cc
+++ b/url/url_idna_ascii_only.cc
@@ -6,8 +6,8 @@
 
 #include <ostream>
 #include <string>
+#include <string_view>
 
-#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "url/url_canon_internal.h"
@@ -16,9 +16,9 @@
 
 // Only allow ASCII to avoid ICU dependency. Use NSString+IDN
 // to convert non-ASCII URL prior to passing to API.
-bool IDNToASCII(const char16_t* src, int src_len, CanonOutputW* output) {
-  if (gurl_base::IsStringASCII(gurl_base::StringPiece16(src, src_len))) {
-    output->Append(src, src_len);
+bool IDNToASCII(std::u16string_view src, CanonOutputW* output) {
+  if (gurl_base::IsStringASCII(src)) {
+    output->Append(src);
     return true;
   }
   GURL_DCHECK(false) << "IDN URL support is not available.";
diff --git a/url/url_idna_icu.cc b/url/url_idna_icu.cc
index 4a3a602..7c1931c 100644
--- a/url/url_idna_icu.cc
+++ b/url/url_idna_icu.cc
@@ -11,6 +11,7 @@
 #include <ostream>
 
 #include "polyfills/base/check_op.h"
+#include "base/numerics/safe_conversions.h"
 #include <unicode/uidna.h>
 #include <unicode/utypes.h>
 #include "url/url_canon_icu.h"
@@ -90,7 +91,7 @@
 // conversions in our code. In addition, consider using icu::IDNA's UTF-8/ASCII
 // version with StringByteSink. That way, we can avoid C wrappers and additional
 // string conversion.
-bool IDNToASCII(const char16_t* src, int src_len, CanonOutputW* output) {
+bool IDNToASCII(std::u16string_view src, CanonOutputW* output) {
   GURL_DCHECK(output->length() == 0);  // Output buffer is assumed empty.
 
   UIDNA* uidna = GetUIDNA();
@@ -98,8 +99,9 @@
   while (true) {
     UErrorCode err = U_ZERO_ERROR;
     UIDNAInfo info = UIDNA_INFO_INITIALIZER;
-    int output_length = uidna_nameToASCII(uidna, src, src_len, output->data(),
-                                          output->capacity(), &info, &err);
+    int output_length = uidna_nameToASCII(
+        uidna, src.data(), gurl_base::checked_cast<int32_t>(src.size()),
+        output->data(), output->capacity(), &info, &err);
 
     // Ignore various errors for web compatibility. The options are specified
     // by the WHATWG URL Standard. See
diff --git a/url/url_parse_perftest.cc b/url/url_parse_perftest.cc
index b9b85d9..63bccdb 100644
--- a/url/url_parse_perftest.cc
+++ b/url/url_parse_perftest.cc
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/strings/string_piece.h"
+#include <string_view>
+
 #include "base/test/perf_time_logger.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -13,7 +14,7 @@
 namespace {
 
 TEST(URLParse, FullURL) {
-  constexpr gurl_base::StringPiece kUrl =
+  constexpr std::string_view kUrl =
       "http://me:pass@host/foo/bar.html;param?query=yes#ref";
 
   url::Parsed parsed;
@@ -24,16 +25,16 @@
   timer.Done();
 }
 
-constexpr gurl_base::StringPiece kTypicalUrl1 =
+constexpr std::string_view kTypicalUrl1 =
     "http://www.google.com/"
     "search?q=url+parsing&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:"
     "official&client=firefox-a";
 
-constexpr gurl_base::StringPiece kTypicalUrl2 =
+constexpr std::string_view kTypicalUrl2 =
     "http://www.amazon.com/Stephen-King-Thrillers-Horror-People/dp/0766012336/"
     "ref=sr_1_2/133-4144931-4505264?ie=UTF8&s=books&qid=2144880915&sr=8-2";
 
-constexpr gurl_base::StringPiece kTypicalUrl3 =
+constexpr std::string_view kTypicalUrl3 =
     "http://store.apple.com/1-800-MY-APPLE/WebObjects/AppleStore.woa/wa/"
     "RSLID?nnmm=browse&mco=578E9744&node=home/desktop/mac_pro";
 
diff --git a/url/url_util.cc b/url/url_util.cc
index 1fb663e..267e4b9 100644
--- a/url/url_util.cc
+++ b/url/url_util.cc
@@ -147,18 +147,6 @@
   DO_NOT_REMOVE_WHITESPACE,
 };
 
-// This template converts a given character type to the corresponding
-// StringPiece type.
-template<typename CHAR> struct CharToStringPiece {
-};
-template<> struct CharToStringPiece<char> {
-  typedef gurl_base::StringPiece Piece;
-};
-template <>
-struct CharToStringPiece<char16_t> {
-  typedef gurl_base::StringPiece16 Piece;
-};
-
 // Given a string and a range inside the string, compares it to the given
 // lower-case |compare_to| buffer.
 template<typename CHAR>
@@ -168,8 +156,7 @@
   if (component.is_empty())
     return compare_to[0] == 0;  // When component is empty, match empty scheme.
   return gurl_base::EqualsCaseInsensitiveASCII(
-      typename CharToStringPiece<CHAR>::Piece(&spec[component.begin],
-                                              component.len),
+      std::basic_string_view(&spec[component.begin], component.len),
       compare_to);
 }
 
@@ -185,8 +172,7 @@
 
   for (const SchemeWithType& scheme_with_type : schemes) {
     if (gurl_base::EqualsCaseInsensitiveASCII(
-            typename CharToStringPiece<CHAR>::Piece(&spec[scheme.begin],
-                                                    scheme.len),
+            std::basic_string_view(&spec[scheme.begin], scheme.len),
             scheme_with_type.scheme)) {
       *type = scheme_with_type.type;
       return true;
@@ -735,8 +721,8 @@
   return DoFindAndCompareScheme(str, str_len, compare, found_scheme);
 }
 
-bool DomainIs(gurl_base::StringPiece canonical_host,
-              gurl_base::StringPiece canonical_domain) {
+bool DomainIs(std::string_view canonical_host,
+              std::string_view canonical_domain) {
   if (canonical_host.empty() || canonical_domain.empty())
     return false;
 
@@ -754,7 +740,7 @@
   const char* host_first_pos =
       canonical_host.data() + host_len - canonical_domain.length();
 
-  if (gurl_base::StringPiece(host_first_pos, canonical_domain.length()) !=
+  if (std::string_view(host_first_pos, canonical_domain.length()) !=
       canonical_domain) {
     return false;
   }
@@ -771,7 +757,7 @@
   return true;
 }
 
-bool HostIsIPAddress(gurl_base::StringPiece host) {
+bool HostIsIPAddress(std::string_view host) {
   STACK_UNINITIALIZED url::RawCanonOutputT<char, 128> ignored_output;
   url::CanonHostInfo host_info;
   url::CanonicalizeIPAddress(host.data(), Component(0, host.length()),
@@ -847,19 +833,18 @@
                              charset_converter, output, out_parsed);
 }
 
-void DecodeURLEscapeSequences(const char* input,
-                              int length,
+void DecodeURLEscapeSequences(std::string_view input,
                               DecodeURLMode mode,
                               CanonOutputW* output) {
-  if (length <= 0)
+  if (input.empty()) {
     return;
+  }
 
   STACK_UNINITIALIZED RawCanonOutputT<char> unescaped_chars;
-  size_t length_size_t = static_cast<size_t>(length);
-  for (size_t i = 0; i < length_size_t; i++) {
+  for (size_t i = 0; i < input.length(); i++) {
     if (input[i] == '%') {
       unsigned char ch;
-      if (DecodeEscaped(input, &i, length_size_t, &ch)) {
+      if (DecodeEscaped(input.data(), &i, input.length(), &ch)) {
         unescaped_chars.push_back(ch);
       } else {
         // Invalid escape sequence, copy the percent literal.
@@ -908,16 +893,20 @@
   }
 }
 
-void EncodeURIComponent(const char* input, int length, CanonOutput* output) {
-  for (int i = 0; i < length; ++i) {
-    unsigned char c = static_cast<unsigned char>(input[i]);
-    if (IsComponentChar(c))
+void EncodeURIComponent(std::string_view input, CanonOutput* output) {
+  for (unsigned char c : input) {
+    if (IsComponentChar(c)) {
       output->push_back(c);
-    else
+    } else {
       AppendEscapedChar(c, output);
+    }
   }
 }
 
+bool IsURIComponentChar(char c) {
+  return IsComponentChar(c);
+}
+
 bool CompareSchemeComponent(const char* spec,
                             const Component& component,
                             const char* compare_to) {
@@ -930,4 +919,16 @@
   return DoCompareSchemeComponent(spec, component, compare_to);
 }
 
+bool HasInvalidURLEscapeSequences(std::string_view input) {
+  for (size_t i = 0; i < input.size(); i++) {
+    if (input[i] == '%') {
+      unsigned char ch;
+      if (!DecodeEscaped(input.data(), &i, input.size(), &ch)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 }  // namespace url
diff --git a/url/url_util.h b/url/url_util.h
index b489362..4254426 100644
--- a/url/url_util.h
+++ b/url/url_util.h
@@ -7,10 +7,10 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "polyfills/base/component_export.h"
-#include "base/strings/string_piece.h"
 #include "url/third_party/mozilla/url_parse.h"
 #include "url/url_canon.h"
 #include "url/url_constants.h"
@@ -203,12 +203,12 @@
 // input domain should match host canonicalization rules. i.e. it should be
 // lowercase except for escape chars.
 COMPONENT_EXPORT(URL)
-bool DomainIs(gurl_base::StringPiece canonical_host,
-              gurl_base::StringPiece canonical_domain);
+bool DomainIs(std::string_view canonical_host,
+              std::string_view canonical_domain);
 
 // Returns true if the hostname is an IP address. Note: this function isn't very
 // cheap, as it must re-parse the host to verify.
-COMPONENT_EXPORT(URL) bool HostIsIPAddress(gurl_base::StringPiece host);
+COMPONENT_EXPORT(URL) bool HostIsIPAddress(std::string_view host);
 
 // URL library wrappers --------------------------------------------------------
 
@@ -299,15 +299,29 @@
 
 // Unescapes the given string using URL escaping rules.
 COMPONENT_EXPORT(URL)
-void DecodeURLEscapeSequences(const char* input,
-                              int length,
+void DecodeURLEscapeSequences(std::string_view input,
                               DecodeURLMode mode,
                               CanonOutputW* output);
 
 // Escapes the given string as defined by the JS method encodeURIComponent. See
 // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/encodeURIComponent
 COMPONENT_EXPORT(URL)
-void EncodeURIComponent(const char* input, int length, CanonOutput* output);
+void EncodeURIComponent(std::string_view input, CanonOutput* output);
+
+// Returns true if `c` is a character that does not require escaping in
+// encodeURIComponent.
+// TODO(crbug.com/1481056): Remove this when event-level reportEvent is removed
+// (if it is still this function's only consumer).
+COMPONENT_EXPORT(URL)
+bool IsURIComponentChar(char c);
+
+// Checks an arbitrary string for invalid escape sequences.
+//
+// A valid percent-encoding is '%' followed by exactly two hex-digits. This
+// function returns true if an occurrence of '%' is found and followed by
+// anything other than two hex-digits.
+COMPONENT_EXPORT(URL)
+bool HasInvalidURLEscapeSequences(std::string_view input);
 
 }  // namespace url
 
diff --git a/url/url_util_unittest.cc b/url/url_util_unittest.cc
index b052c3d..1435d3b 100644
--- a/url/url_util_unittest.cc
+++ b/url/url_util_unittest.cc
@@ -6,7 +6,8 @@
 
 #include <stddef.h>
 
-#include "base/strings/string_piece.h"
+#include <string_view>
+
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest-message.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -259,14 +260,12 @@
   for (size_t i = 0; i < std::size(decode_cases); i++) {
     const char* input = decode_cases[i].input;
     RawCanonOutputT<char16_t> output;
-    DecodeURLEscapeSequences(input, strlen(input),
-                             DecodeURLMode::kUTF8OrIsomorphic, &output);
+    DecodeURLEscapeSequences(input, DecodeURLMode::kUTF8OrIsomorphic, &output);
     EXPECT_EQ(decode_cases[i].output, gurl_base::UTF16ToUTF8(std::u16string(
                                           output.data(), output.length())));
 
     RawCanonOutputT<char16_t> output_utf8;
-    DecodeURLEscapeSequences(input, strlen(input), DecodeURLMode::kUTF8,
-                             &output_utf8);
+    DecodeURLEscapeSequences(input, DecodeURLMode::kUTF8, &output_utf8);
     EXPECT_EQ(decode_cases[i].output,
               gurl_base::UTF16ToUTF8(
                   std::u16string(output_utf8.data(), output_utf8.length())));
@@ -275,8 +274,7 @@
   // Our decode should decode %00
   const char zero_input[] = "%00";
   RawCanonOutputT<char16_t> zero_output;
-  DecodeURLEscapeSequences(zero_input, strlen(zero_input), DecodeURLMode::kUTF8,
-                           &zero_output);
+  DecodeURLEscapeSequences(zero_input, DecodeURLMode::kUTF8, &zero_output);
   EXPECT_NE("%00", gurl_base::UTF16ToUTF8(std::u16string(zero_output.data(),
                                                     zero_output.length())));
 
@@ -301,14 +299,13 @@
   for (const auto& test : utf8_decode_cases) {
     const char* input = test.input;
     RawCanonOutputT<char16_t> output_iso;
-    DecodeURLEscapeSequences(input, strlen(input),
-                             DecodeURLMode::kUTF8OrIsomorphic, &output_iso);
+    DecodeURLEscapeSequences(input, DecodeURLMode::kUTF8OrIsomorphic,
+                             &output_iso);
     EXPECT_EQ(std::u16string(test.expected_iso.data()),
               std::u16string(output_iso.data(), output_iso.length()));
 
     RawCanonOutputT<char16_t> output_utf8;
-    DecodeURLEscapeSequences(input, strlen(input), DecodeURLMode::kUTF8,
-                             &output_utf8);
+    DecodeURLEscapeSequences(input, DecodeURLMode::kUTF8, &output_utf8);
     EXPECT_EQ(std::u16string(test.expected_utf8.data()),
               std::u16string(output_utf8.data(), output_utf8.length()));
   }
@@ -341,7 +338,7 @@
   for (size_t i = 0; i < std::size(encode_cases); i++) {
     const char* input = encode_cases[i].input;
     RawCanonOutputT<char> buffer;
-    EncodeURIComponent(input, strlen(input), &buffer);
+    EncodeURIComponent(input, &buffer);
     std::string output(buffer.data(), buffer.length());
     EXPECT_EQ(encode_cases[i].output, output);
   }
@@ -588,7 +585,7 @@
 }
 
 namespace {
-absl::optional<std::string> CanonicalizeSpec(gurl_base::StringPiece spec,
+absl::optional<std::string> CanonicalizeSpec(std::string_view spec,
                                              bool trim_path_end) {
   std::string canonicalized;
   StdStringCanonOutput output(&canonicalized);
@@ -635,4 +632,87 @@
   }
 }
 
+TEST_F(URLUtilTest, TestHasInvalidURLEscapeSequences) {
+  struct TestCase {
+    const char* input;
+    bool is_invalid;
+  } cases[] = {
+      // Edge cases.
+      {"", false},
+      {"%", true},
+
+      // Single regular chars with no escaping are valid.
+      {"a", false},
+      {"g", false},
+      {"A", false},
+      {"G", false},
+      {":", false},
+      {"]", false},
+      {"\x00", false},      // ASCII 'NUL' char
+      {"\x01", false},      // ASCII 'SOH' char
+      {"\xC2\xA3", false},  // UTF-8 encoded '£'.
+
+      // Longer strings without escaping are valid.
+      {"Hello world", false},
+      {"Here: [%25] <-- a percent-encoded percent character.", false},
+
+      // Valid %-escaped sequences ('%' followed by two hex digits).
+      {"%00", false},
+      {"%20", false},
+      {"%02", false},
+      {"%ff", false},
+      {"%FF", false},
+      {"%0a", false},
+      {"%0A", false},
+      {"abc%FF", false},
+      {"%FFabc", false},
+      {"abc%FFabc", false},
+      {"hello %FF world", false},
+      {"%20hello%20world", false},
+      {"%25", false},
+      {"%25%25", false},
+      {"%250", false},
+      {"%259", false},
+      {"%25A", false},
+      {"%25F", false},
+      {"%0a:", false},
+
+      // '%' followed by a single character is never a valid sequence.
+      {"%%", true},
+      {"%2", true},
+      {"%a", true},
+      {"%A", true},
+      {"%g", true},
+      {"%G", true},
+      {"%:", true},
+      {"%[", true},
+      {"%F", true},
+      {"%\xC2\xA3", true},  //% followed by UTF-8 encoded '£'.
+
+      // String ends on a potential escape sequence but without two hex-digits
+      // is invalid.
+      {"abc%", true},
+      {"abc%%", true},
+      {"abc%%%", true},
+      {"abc%a", true},
+
+      // One hex and one non-hex digit is invalid.
+      {"%a:", true},
+      {"%:a", true},
+      {"%::", true},
+      {"%ag", true},
+      {"%ga", true},
+      {"%-1", true},
+      {"%1-", true},
+      {"%0\xC2\xA3", true},  // %0£.
+  };
+
+  for (TestCase test_case : cases) {
+    const char* input = test_case.input;
+    bool result = HasInvalidURLEscapeSequences(input);
+    EXPECT_EQ(test_case.is_invalid, result)
+        << "Invalid result for '" << input << "'";
+  }
+}
+
 }  // namespace url