Do not sanitize about:blank/#foo & about:blank?foo

This CL ensures that the browser will not sanitize these two types of
URLs. This fixes an issue with PlzNavigate where we could not go back to
about:blank/#foo due to some discrepancy between the url in the
FrameNavigationEntry & the url stored in the PageState of the same
entry.

BUG=575210

Review-Url: https://codereview.chromium.org/2644133002
Cr-Commit-Position: refs/heads/master@{#445525}
diff --git a/url/url_constants.cc b/url/url_constants.cc
index 73c9a767..37fc82c 100644
--- a/url/url_constants.cc
+++ b/url/url_constants.cc
@@ -8,6 +8,9 @@
 
 const char kAboutBlankURL[] = "about:blank";
 
+const char kAboutBlankPath[] = "blank";
+const char kAboutBlankWithHashPath[] = "blank/";
+
 const char kAboutScheme[] = "about";
 const char kBlobScheme[] = "blob";
 const char kContentScheme[] = "content";
diff --git a/url/url_constants.h b/url/url_constants.h
index c110589..7e5cb53f 100644
--- a/url/url_constants.h
+++ b/url/url_constants.h
@@ -13,6 +13,9 @@
 
 URL_EXPORT extern const char kAboutBlankURL[];
 
+URL_EXPORT extern const char kAboutBlankPath[];
+URL_EXPORT extern const char kAboutBlankWithHashPath[];
+
 URL_EXPORT extern const char kAboutScheme[];
 URL_EXPORT extern const char kBlobScheme[];
 // The content scheme is specific to Android for identifying a stored file.
diff --git a/url/url_util.cc b/url/url_util.cc
index 2c8d697..9a2cce4 100644
--- a/url/url_util.cc
+++ b/url/url_util.cc
@@ -10,6 +10,7 @@
 #include "base/debug/leak_annotations.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
+#include "url/gurl.h"
 #include "url/url_canon_internal.h"
 #include "url/url_constants.h"
 #include "url/url_file.h"
@@ -608,6 +609,21 @@
   return DoIsInSchemes(spec, scheme, &unused_scheme_type, *referrer_schemes);
 }
 
+bool IsAboutBlank(const GURL& url) {
+  if (!url.SchemeIs(url::kAboutScheme))
+    return false;
+
+  if (url.has_host() || url.has_username() || url.has_password() ||
+      url.has_port()) {
+    return false;
+  }
+
+  if (url.path() != kAboutBlankPath && url.path() != kAboutBlankWithHashPath)
+    return false;
+
+  return true;
+}
+
 bool FindAndCompareScheme(const char* str,
                           int str_len,
                           const char* compare,
diff --git a/url/url_util.h b/url/url_util.h
index a4b74b1..e424c503 100644
--- a/url/url_util.h
+++ b/url/url_util.h
@@ -15,6 +15,8 @@
 #include "url/url_constants.h"
 #include "url/url_export.h"
 
+class GURL;
+
 namespace url {
 
 // Init ------------------------------------------------------------------------
@@ -153,6 +155,10 @@
                                       const Component& scheme,
                                       SchemeType* type);
 
+// Check whether the url is of the form about:blank, about:blank?foo or
+// about:blank/#foo.
+URL_EXPORT bool IsAboutBlank(const GURL& url);
+
 // Hosts  ----------------------------------------------------------------------
 
 // Returns true if the |canonicalized_host| matches or is in the same domain as
diff --git a/url/url_util_unittest.cc b/url/url_util_unittest.cc
index a706f5a08..a3b61fff 100644
--- a/url/url_util_unittest.cc
+++ b/url/url_util_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
 #include "url/third_party/mozilla/url_parse.h"
 #include "url/url_canon.h"
 #include "url/url_canon_stdstring.h"
@@ -417,4 +418,19 @@
   }
 }
 
+TEST(URLUtilTest, IsAboutBlank) {
+  const std::string kAboutBlankUrls[] = {"about:blank", "about:blank?foo",
+                                         "about:blank/#foo",
+                                         "about:blank?foo#foo"};
+  for (const auto& url : kAboutBlankUrls)
+    EXPECT_TRUE(IsAboutBlank(GURL(url)));
+
+  const std::string kNotAboutBlankUrls[] = {
+      "http:blank",      "about:blan",          "about://blank",
+      "about:blank/foo", "about://:8000/blank", "about://foo:foo@/blank",
+      "foo@about:blank", "foo:bar@about:blank", "about:blank:8000"};
+  for (const auto& url : kNotAboutBlankUrls)
+    EXPECT_FALSE(IsAboutBlank(GURL(url)));
+}
+
 }  // namespace url