Internal change PiperOrigin-RevId: 148486483 Change-Id: Iebcb04cdcbdf2d47099b8ee50eff901139c5a721
diff --git a/import.sh b/import.sh index 47de023..8671302 100644 --- a/import.sh +++ b/import.sh
@@ -4,7 +4,7 @@ top=/tmp/chromium mkdir $top prefix=https://chromium.googlesource.com/chromium/src.git/+archive -for version in 54.0.2840.100 55.0.2883.87 +for version in 55.0.2883.87 56.0.2924.87 do mkdir $top/$version cd $top/$version
diff --git a/src/base/strings/utf_string_conversions.cc b/src/base/strings/utf_string_conversions.cc new file mode 100644 index 0000000..944078f --- /dev/null +++ b/src/base/strings/utf_string_conversions.cc
@@ -0,0 +1,124 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/utf_string_conversions.h" + +#include <stdint.h> + +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversion_utils.h" +#include "build/build_config.h" + +namespace url { +namespace base { + +namespace { + +// Generalized Unicode converter ----------------------------------------------- + +// Converts the given source Unicode character type to the given destination +// Unicode character type as a STL string. The given input buffer and size +// determine the source, and the given output STL string will be replaced by +// the result. +template<typename SRC_CHAR, typename DEST_STRING> +bool ConvertUnicode(const SRC_CHAR* src, + size_t src_len, + DEST_STRING* output) { + // ICU requires 32-bit numbers. + bool success = true; + int32_t src_len32 = static_cast<int32_t>(src_len); + for (int32_t i = 0; i < src_len32; i++) { + uint32_t code_point; + if (ReadUnicodeCharacter(src, src_len32, &i, &code_point)) { + WriteUnicodeCharacter(code_point, output); + } else { + WriteUnicodeCharacter(0xFFFD, output); + success = false; + } + } + + return success; +} + +} // namespace + +// UTF16 <-> UTF8 -------------------------------------------------------------- + +#if defined(WCHAR_T_IS_UTF32) + +bool UTF8ToUTF16(const char* src, size_t src_len, string16* output) { + if (IsStringASCII(StringPiece(src, src_len))) { + output->assign(src, src + src_len); + return true; + } else { + PrepareForUTF16Or32Output(src, src_len, output); + return ConvertUnicode(src, src_len, output); + } +} + +string16 UTF8ToUTF16(StringPiece utf8) { + if (IsStringASCII(utf8)) { + return string16(utf8.begin(), utf8.end()); + } + + string16 ret; + PrepareForUTF16Or32Output(utf8.data(), utf8.length(), &ret); + // Ignore the success flag of this call, it will do the best it can for + // invalid input, which is what we want here. + ConvertUnicode(utf8.data(), utf8.length(), &ret); + return ret; +} + +bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output) { + if (IsStringASCII(StringPiece16(src, src_len))) { + output->assign(src, src + src_len); + return true; + } else { + PrepareForUTF8Output(src, src_len, output); + return ConvertUnicode(src, src_len, output); + } +} + +std::string UTF16ToUTF8(StringPiece16 utf16) { + if (IsStringASCII(utf16)) { + return std::string(utf16.begin(), utf16.end()); + } + + std::string ret; + // Ignore the success flag of this call, it will do the best it can for + // invalid input, which is what we want here. + UTF16ToUTF8(utf16.data(), utf16.length(), &ret); + return ret; +} + +#elif defined(WCHAR_T_IS_UTF16) +// Easy case since we can use the "wide" versions we already wrote above. + +bool UTF8ToUTF16(const char* src, size_t src_len, string16* output) { + return UTF8ToWide(src, src_len, output); +} + +string16 UTF8ToUTF16(StringPiece utf8) { + return UTF8ToWide(utf8); +} + +bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output) { + return WideToUTF8(src, src_len, output); +} + +std::string UTF16ToUTF8(StringPiece16 utf16) { + if (IsStringASCII(utf16)) + return std::string(utf16.data(), utf16.data() + utf16.length()); + + std::string ret; + PrepareForUTF8Output(utf16.data(), utf16.length(), &ret); + ConvertUnicode(utf16.data(), utf16.length(), &ret); + return ret; +} + +#endif + +} // namespace base +} // namespace url
diff --git a/src/base/strings/utf_string_conversions.h b/src/base/strings/utf_string_conversions.h new file mode 100644 index 0000000..d6876b3 --- /dev/null +++ b/src/base/strings/utf_string_conversions.h
@@ -0,0 +1,26 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRINGS_UTF_STRING_CONVERSIONS_H_ +#define BASE_STRINGS_UTF_STRING_CONVERSIONS_H_ + +#include <stddef.h> + +#include <string> + +#include "base/strings/string16.h" +#include "base/strings/string_piece.h" + +#define BASE_EXPORT + +namespace url { +namespace base { + +BASE_EXPORT string16 UTF8ToUTF16(StringPiece utf8); +BASE_EXPORT std::string UTF16ToUTF8(StringPiece16 utf16); + +} // namespace base +} // namespace url + +#endif // BASE_STRINGS_UTF_STRING_CONVERSIONS_H_
diff --git a/src/build/build_config.h b/src/build/build_config.h index f67dd48..5f99fc1 100644 --- a/src/build/build_config.h +++ b/src/build/build_config.h
@@ -48,7 +48,6 @@ #endif #elif defined(_WIN32) #define OS_WIN 1 -#define TOOLKIT_VIEWS 1 #elif defined(__FreeBSD__) #define OS_FREEBSD 1 #elif defined(__NetBSD__) @@ -111,6 +110,31 @@ #define ARCH_CPU_X86 1 #define ARCH_CPU_32_BITS 1 #define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__s390x__) +#define ARCH_CPU_S390_FAMILY 1 +#define ARCH_CPU_S390X 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif defined(__s390__) +#define ARCH_CPU_S390_FAMILY 1 +#define ARCH_CPU_S390 1 +#define ARCH_CPU_31_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif defined(__PPC64__) && defined(__BIG_ENDIAN__) +#define ARCH_CPU_PPC64_FAMILY 1 +#define ARCH_CPU_PPC64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif defined(__PPC64__) && defined(__LITTLE_ENDIAN__) +#define ARCH_CPU_PPC64_FAMILY 1 +#define ARCH_CPU_PPC64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__PPC__) +#define ARCH_CPU_PPC_FAMILY 1 +#define ARCH_CPU_PPC 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 #elif defined(__ARMEL__) #define ARCH_CPU_ARM_FAMILY 1 #define ARCH_CPU_ARMEL 1
diff --git a/src/url/gurl.cc b/src/url/gurl.cc index d81b252..1d6dfc6 100644 --- a/src/url/gurl.cc +++ b/src/url/gurl.cc
@@ -180,14 +180,6 @@ return EmptyStringForGURL(); } -bool GURL::operator==(const GURL& other) const { - return spec_ == other.spec_; -} - -bool GURL::operator!=(const GURL& other) const { - return spec_ != other.spec_; -} - bool GURL::operator<(const GURL& other) const { return spec_ < other.spec_; } @@ -510,3 +502,20 @@ std::ostream& operator<<(std::ostream& out, const GURL& url) { return out << url.possibly_invalid_spec(); } + +bool operator==(const GURL& x, const GURL& y) { + return x.possibly_invalid_spec() == y.possibly_invalid_spec(); +} + +bool operator!=(const GURL& x, const GURL& y) { + return !(x == y); +} + +bool operator==(const GURL& x, const url::base::StringPiece& spec) { + DCHECK_EQ(GURL(spec).possibly_invalid_spec(), spec); + return x.possibly_invalid_spec() == spec; +} + +bool operator!=(const GURL& x, const url::base::StringPiece& spec) { + return !(x == spec); +}
diff --git a/src/url/gurl.h b/src/url/gurl.h index 4d8b5d4..aeb77aa 100644 --- a/src/url/gurl.h +++ b/src/url/gurl.h
@@ -132,10 +132,6 @@ return parsed_; } - // Defiant equality operator! - bool operator==(const GURL& other) const; - bool operator!=(const GURL& other) const; - // Allows GURL to used as a key in STL (for example, a std::set or std::map). bool operator<(const GURL& other) const; bool operator>(const GURL& other) const; @@ -240,7 +236,8 @@ // higher-level and more complete semantics. See that function's documentation // for more detail. bool SchemeIsCryptographic() const { - return SchemeIs(url::kHttpsScheme) || SchemeIs(url::kWssScheme); + return SchemeIs(url::kHttpsScheme) || SchemeIs(url::kWssScheme) || + SchemeIs(url::kHttpsSuboriginScheme); } // Returns true if the scheme is "blob". @@ -248,6 +245,12 @@ return SchemeIs(url::kBlobScheme); } + // Returns true if the scheme indicates a serialized suborigin. + bool SchemeIsSuborigin() const { + return SchemeIs(url::kHttpSuboriginScheme) || + SchemeIs(url::kHttpsSuboriginScheme); + } + // The "content" of the URL is everything after the scheme (skipping the // scheme delimiting colon). It is an error to get the content of an invalid // URL: the result will be an empty string. @@ -447,4 +450,13 @@ // Stream operator so GURL can be used in assertion statements. URL_EXPORT std::ostream& operator<<(std::ostream& out, const GURL& url); +URL_EXPORT bool operator==(const GURL& x, const GURL& y); +URL_EXPORT bool operator!=(const GURL& x, const GURL& y); + +// Equality operator for comparing raw spec_. This should be used in place of +// url == GURL(spec) where |spec| is known (i.e. constants). This is to prevent +// needlessly re-parsing |spec| into a temporary GURL. +URL_EXPORT bool operator==(const GURL& x, const url::base::StringPiece& spec); +URL_EXPORT bool operator!=(const GURL& x, const url::base::StringPiece& spec); + #endif // URL_GURL_H_
diff --git a/src/url/gurl_unittest.cc b/src/url/gurl_unittest.cc index 4e18da8..aae5048 100644 --- a/src/url/gurl_unittest.cc +++ b/src/url/gurl_unittest.cc
@@ -5,6 +5,7 @@ #include <stddef.h> #include "base/macros.h" +#include "base/strings/utf_string_conversions.h" #include "testing/base/public/gunit.h" #include "url/gurl.h" #include "url/url_canon.h" @@ -12,9 +13,6 @@ namespace url { -using test_utils::WStringToUTF16; -using test_utils::ConvertUTF8ToUTF16; - namespace { template<typename CHAR> @@ -67,11 +65,11 @@ // the parser is already tested and works, so we are mostly interested if the // object does the right thing with the results. TEST(GURLTest, Components) { - GURL empty_url(WStringToUTF16(L"")); + GURL empty_url(base::UTF8ToUTF16("")); EXPECT_TRUE(empty_url.is_empty()); EXPECT_FALSE(empty_url.is_valid()); - GURL url(WStringToUTF16(L"http://user:pass@google.com:99/foo;bar?q=a#ref")); + GURL url(base::UTF8ToUTF16("http://user:pass@google.com:99/foo;bar?q=a#ref")); EXPECT_FALSE(url.is_empty()); EXPECT_TRUE(url.is_valid()); EXPECT_TRUE(url.SchemeIs("http")); @@ -116,7 +114,8 @@ } TEST(GURLTest, Copy) { - GURL url(WStringToUTF16(L"http://user:pass@google.com:99/foo;bar?q=a#ref")); + GURL url(base::UTF8ToUTF16( + "http://user:pass@google.com:99/foo;bar?q=a#ref")); GURL url2(url); EXPECT_TRUE(url2.is_valid()); @@ -149,7 +148,8 @@ } TEST(GURLTest, Assign) { - GURL url(WStringToUTF16(L"http://user:pass@google.com:99/foo;bar?q=a#ref")); + GURL url(base::UTF8ToUTF16( + "http://user:pass@google.com:99/foo;bar?q=a#ref")); GURL url2; url2 = url; @@ -191,7 +191,8 @@ } TEST(GURLTest, CopyFileSystem) { - GURL url(WStringToUTF16(L"filesystem:https://user:pass@google.com:99/t/foo;bar?q=a#ref")); + GURL url(base::UTF8ToUTF16( + "filesystem:https://user:pass@google.com:99/t/foo;bar?q=a#ref")); GURL url2(url); EXPECT_TRUE(url2.is_valid()); @@ -313,9 +314,9 @@ EXPECT_EQ(output.SchemeIsFileSystem(), output.inner_url() != NULL); // Wide code path. - GURL inputw(ConvertUTF8ToUTF16(resolve_cases[i].base)); + GURL inputw(base::UTF8ToUTF16(resolve_cases[i].base)); GURL outputw = - input.Resolve(ConvertUTF8ToUTF16(resolve_cases[i].relative)); + input.Resolve(base::UTF8ToUTF16(resolve_cases[i].relative)); EXPECT_EQ(resolve_cases[i].expected_valid, outputw.is_valid()) << i; EXPECT_EQ(resolve_cases[i].expected, outputw.spec()) << i; EXPECT_EQ(outputw.SchemeIsFileSystem(), outputw.inner_url() != NULL); @@ -677,12 +678,42 @@ EXPECT_FALSE(GURL("http://bar/").SchemeIsWSOrWSS()); } +TEST(GURLTest, SchemeIsCryptographic) { + EXPECT_TRUE(GURL("https://foo.bar.com/").SchemeIsCryptographic()); + EXPECT_TRUE(GURL("HTTPS://foo.bar.com/").SchemeIsCryptographic()); + EXPECT_TRUE(GURL("HtTpS://foo.bar.com/").SchemeIsCryptographic()); + + EXPECT_TRUE(GURL("wss://foo.bar.com/").SchemeIsCryptographic()); + EXPECT_TRUE(GURL("WSS://foo.bar.com/").SchemeIsCryptographic()); + EXPECT_TRUE(GURL("WsS://foo.bar.com/").SchemeIsCryptographic()); + + EXPECT_TRUE(GURL("https-so://foo.bar.com/").SchemeIsCryptographic()); + EXPECT_TRUE(GURL("HTTPS-SO://foo.bar.com/").SchemeIsCryptographic()); + EXPECT_TRUE(GURL("HtTpS-So://foo.bar.com/").SchemeIsCryptographic()); + + EXPECT_FALSE(GURL("http://foo.bar.com/").SchemeIsCryptographic()); + EXPECT_FALSE(GURL("ws://foo.bar.com/").SchemeIsCryptographic()); + EXPECT_FALSE(GURL("http-so://foo.bar.com/").SchemeIsCryptographic()); +} + TEST(GURLTest, SchemeIsBlob) { EXPECT_TRUE(GURL("BLOB://BAR/").SchemeIsBlob()); EXPECT_TRUE(GURL("blob://bar/").SchemeIsBlob()); EXPECT_FALSE(GURL("http://bar/").SchemeIsBlob()); } +TEST(GURLTest, SchemeIsSuborigin) { + EXPECT_TRUE(GURL("http-so://foo.bar.com/").SchemeIsSuborigin()); + EXPECT_TRUE(GURL("HTTP-SO://foo.bar.com/").SchemeIsSuborigin()); + EXPECT_TRUE(GURL("HtTp-So://foo.bar.com/").SchemeIsSuborigin()); + EXPECT_FALSE(GURL("http://foo.bar.com/").SchemeIsSuborigin()); + + EXPECT_TRUE(GURL("https-so://foo.bar.com/").SchemeIsSuborigin()); + EXPECT_TRUE(GURL("HTTPS-SO://foo.bar.com/").SchemeIsSuborigin()); + EXPECT_TRUE(GURL("HtTpS-So://foo.bar.com/").SchemeIsSuborigin()); + EXPECT_FALSE(GURL("https://foo.bar.com/").SchemeIsSuborigin()); +} + TEST(GURLTest, ContentAndPathForNonStandardURLs) { struct TestCase { const char* url;
diff --git a/src/url/origin.cc b/src/url/origin.cc index 1ba07c1..fac78cf 100644 --- a/src/url/origin.cc +++ b/src/url/origin.cc
@@ -16,10 +16,26 @@ namespace url { -Origin::Origin() : unique_(true) { +namespace { + +GURL AddSuboriginToUrl(const GURL& url, const std::string& suborigin) { + GURL::Replacements replacements; + if (url.scheme() == kHttpScheme) { + replacements.SetSchemeStr(kHttpSuboriginScheme); + } else { + DCHECK(url.scheme() == kHttpsScheme); + replacements.SetSchemeStr(kHttpsSuboriginScheme); + } + std::string new_host = suborigin + "." + url.host(); + replacements.SetHostStr(new_host); + return url.ReplaceComponents(replacements); } -Origin::Origin(const GURL& url) : unique_(true) { +} // namespace + +Origin::Origin() : unique_(true), suborigin_(std::string()) {} + +Origin::Origin(const GURL& url) : unique_(true), suborigin_(std::string()) { if (!url.is_valid() || (!url.IsStandard() && !url.SchemeIsBlob())) return; @@ -31,6 +47,31 @@ // the "path", which boils down to everything after the scheme. GURL's // 'GetContent()' gives us exactly that. tuple_ = SchemeHostPort(GURL(url.GetContent())); + } else if (url.SchemeIsSuborigin()) { + GURL::Replacements replacements; + if (url.scheme() == kHttpSuboriginScheme) { + replacements.SetSchemeStr(kHttpScheme); + } else { + DCHECK(url.scheme() == kHttpsSuboriginScheme); + replacements.SetSchemeStr(kHttpsScheme); + } + + std::string host = url.host(); + size_t suborigin_end = host.find("."); + bool no_dot = suborigin_end == std::string::npos; + std::string new_host( + no_dot ? "" + : host.substr(suborigin_end + 1, + url.host().length() - suborigin_end - 1)); + replacements.SetHostStr(new_host); + + tuple_ = SchemeHostPort(url.ReplaceComponents(replacements)); + + bool invalid_suborigin = no_dot || suborigin_end == 0; + if (invalid_suborigin || tuple_.IsInvalid()) + return; + + suborigin_ = host.substr(0, suborigin_end); } else { tuple_ = SchemeHostPort(url); } @@ -38,9 +79,14 @@ unique_ = tuple_.IsInvalid(); } -Origin::Origin(base::StringPiece scheme, base::StringPiece host, uint16_t port) - : tuple_(scheme, host, port) { +Origin::Origin(base::StringPiece scheme, + base::StringPiece host, + uint16_t port, + base::StringPiece suborigin, + SchemeHostPort::ConstructPolicy policy) + : tuple_(scheme, host, port, policy) { unique_ = tuple_.IsInvalid(); + suborigin_ = suborigin.as_string(); } Origin::~Origin() { @@ -51,7 +97,22 @@ base::StringPiece scheme, base::StringPiece host, uint16_t port) { - return Origin(scheme, host, port); + return Origin(scheme, host, port, "", SchemeHostPort::CHECK_CANONICALIZATION); +} + +Origin Origin::CreateFromNormalizedTuple(base::StringPiece scheme, + base::StringPiece host, + uint16_t port) { + return CreateFromNormalizedTupleWithSuborigin(scheme, host, port, ""); +} + +Origin Origin::CreateFromNormalizedTupleWithSuborigin( + base::StringPiece scheme, + base::StringPiece host, + uint16_t port, + base::StringPiece suborigin) { + return Origin(scheme, host, port, suborigin, + SchemeHostPort::ALREADY_CANONICALIZED); } std::string Origin::Serialize() const { @@ -61,9 +122,21 @@ if (scheme() == kFileScheme) return "file://"; + if (!suborigin_.empty()) { + GURL url_with_suborigin = AddSuboriginToUrl(tuple_.GetURL(), suborigin_); + return SchemeHostPort(url_with_suborigin).Serialize(); + } + return tuple_.Serialize(); } +Origin Origin::GetPhysicalOrigin() const { + if (suborigin_.empty()) + return *this; + + return Origin(tuple_.GetURL()); +} + GURL Origin::GetURL() const { if (unique()) return GURL(); @@ -71,14 +144,23 @@ if (scheme() == kFileScheme) return GURL("file:///"); - return tuple_.GetURL(); + GURL tuple_url(tuple_.GetURL()); + + if (!suborigin_.empty()) + return AddSuboriginToUrl(tuple_url, suborigin_); + + return tuple_url; } bool Origin::IsSameOriginWith(const Origin& other) const { if (unique_ || other.unique_) return false; - return tuple_.Equals(other.tuple_); + return tuple_.Equals(other.tuple_) && suborigin_ == other.suborigin_; +} + +bool Origin::IsSamePhysicalOriginWith(const Origin& other) const { + return GetPhysicalOrigin().IsSameOriginWith(other.GetPhysicalOrigin()); } bool Origin::DomainIs(base::StringPiece lower_ascii_domain) const { @@ -97,4 +179,8 @@ return Origin(a).IsSameOriginWith(Origin(b)); } +bool IsSamePhysicalOriginWith(const GURL& a, const GURL& b) { + return Origin(a).IsSamePhysicalOriginWith(Origin(b)); +} + } // namespace url
diff --git a/src/url/origin.h b/src/url/origin.h index 273622e..1c28588 100644 --- a/src/url/origin.h +++ b/src/url/origin.h
@@ -90,8 +90,8 @@ explicit Origin(const GURL& url); // Creates an Origin from a |scheme|, |host|, and |port|. All the parameters - // must be valid and canonicalized. In particular, note that this cannot be - // used to create unique origins; 'url::Origin()' is the right way to do that. + // must be valid and canonicalized. Do not use this method to create unique + // origins. Use Origin() for that. // // This constructor should be used in order to pass 'Origin' objects back and // forth over IPC (as transitioning through GURL would risk potentially @@ -102,6 +102,21 @@ base::StringPiece host, uint16_t port); + // Creates an origin without sanity checking that the host is canonicalized. + // This should only be used when converting between already normalized types, + // and should NOT be used for IPC. + static Origin CreateFromNormalizedTuple(base::StringPiece scheme, + base::StringPiece host, + uint16_t port); + + // Same as CreateFromNormalizedTuple() above, but adds a suborigin component + // as well. + static Origin CreateFromNormalizedTupleWithSuborigin( + base::StringPiece scheme, + base::StringPiece host, + uint16_t port, + base::StringPiece suborigin); + ~Origin(); // For unique origins, these return ("", "", 0). @@ -109,19 +124,34 @@ const std::string& host() const { return tuple_.host(); } uint16_t port() const { return tuple_.port(); } + // Note that an origin without a suborgin will return the empty string. + const std::string& suborigin() const { return suborigin_; } + bool unique() const { return unique_; } // An ASCII serialization of the Origin as per Section 6.2 of RFC 6454, with // the addition that all Origins with a 'file' scheme serialize to "file://". + // If the Origin has a suborigin, it will be serialized per + // https://w3c.github.io/webappsec-suborigins/#serializing. std::string Serialize() const; + // Returns the physical origin for Origin. If the suborigin is empty, this + // will just return a copy of the Origin. If it has a suborigin, will return + // the Origin of just the scheme/host/port tuple, without the suborigin. See + // https://w3c.github.io/webappsec-suborigins/. + Origin GetPhysicalOrigin() const; + // Two Origins are "same-origin" if their schemes, hosts, and ports are exact - // matches; and neither is unique. + // matches; and neither is unique. If either of the origins have suborigins, + // the suborigins also must be exact matches. bool IsSameOriginWith(const Origin& other) const; bool operator==(const Origin& other) const { return IsSameOriginWith(other); } + // Same as above, but ignores suborigins if they exist. + bool IsSamePhysicalOriginWith(const Origin& other) const; + // Efficiently returns what GURL(Serialize()) would without re-parsing the // URL. This can be used for the (rare) times a GURL representation is needed // for an Origin. @@ -138,15 +168,21 @@ bool operator<(const Origin& other) const; private: - Origin(base::StringPiece scheme, base::StringPiece host, uint16_t port); + Origin(base::StringPiece scheme, + base::StringPiece host, + uint16_t port, + base::StringPiece suborigin, + SchemeHostPort::ConstructPolicy policy); SchemeHostPort tuple_; bool unique_; + std::string suborigin_; }; URL_EXPORT std::ostream& operator<<(std::ostream& out, const Origin& origin); URL_EXPORT bool IsSameOriginWith(const GURL& a, const GURL& b); +URL_EXPORT bool IsSamePhysicalOriginWith(const GURL& a, const GURL& b); } // namespace url
diff --git a/src/url/origin_unittest.cc b/src/url/origin_unittest.cc index 7a67533..fee161b 100644 --- a/src/url/origin_unittest.cc +++ b/src/url/origin_unittest.cc
@@ -13,24 +13,26 @@ namespace { -void ExpectParsedComponentEqual(const url::Component& a, - const url::Component& b) { - EXPECT_EQ(a.begin, b.begin); - EXPECT_EQ(a.len, b.len); -} - void ExpectParsedUrlsEqual(const GURL& a, const GURL& b) { EXPECT_EQ(a, b); const url::Parsed& a_parsed = a.parsed_for_possibly_invalid_spec(); const url::Parsed& b_parsed = b.parsed_for_possibly_invalid_spec(); - ExpectParsedComponentEqual(a_parsed.scheme, b_parsed.scheme); - ExpectParsedComponentEqual(a_parsed.username, b_parsed.username); - ExpectParsedComponentEqual(a_parsed.password, b_parsed.password); - ExpectParsedComponentEqual(a_parsed.host, b_parsed.host); - ExpectParsedComponentEqual(a_parsed.port, b_parsed.port); - ExpectParsedComponentEqual(a_parsed.path, b_parsed.path); - ExpectParsedComponentEqual(a_parsed.query, b_parsed.query); - ExpectParsedComponentEqual(a_parsed.ref, b_parsed.ref); + EXPECT_EQ(a_parsed.scheme.begin, b_parsed.scheme.begin); + EXPECT_EQ(a_parsed.scheme.len, b_parsed.scheme.len); + EXPECT_EQ(a_parsed.username.begin, b_parsed.username.begin); + EXPECT_EQ(a_parsed.username.len, b_parsed.username.len); + EXPECT_EQ(a_parsed.password.begin, b_parsed.password.begin); + EXPECT_EQ(a_parsed.password.len, b_parsed.password.len); + EXPECT_EQ(a_parsed.host.begin, b_parsed.host.begin); + EXPECT_EQ(a_parsed.host.len, b_parsed.host.len); + EXPECT_EQ(a_parsed.port.begin, b_parsed.port.begin); + EXPECT_EQ(a_parsed.port.len, b_parsed.port.len); + EXPECT_EQ(a_parsed.path.begin, b_parsed.path.begin); + EXPECT_EQ(a_parsed.path.len, b_parsed.path.len); + EXPECT_EQ(a_parsed.query.begin, b_parsed.query.begin); + EXPECT_EQ(a_parsed.query.len, b_parsed.query.len); + EXPECT_EQ(a_parsed.ref.begin, b_parsed.ref.begin); + EXPECT_EQ(a_parsed.ref.len, b_parsed.ref.len); } TEST(OriginTest, UniqueOriginComparison) { @@ -63,6 +65,52 @@ } } +TEST(OriginTest, ConstructFromTuple) { + struct TestCases { + const char* const scheme; + const char* const host; + const uint16_t port; + const char* const suborigin; + } cases[] = { + {"http", "example.com", 80, ""}, + {"http", "example.com", 123, ""}, + {"https", "example.com", 443, ""}, + {"http-so", "foobar.example.com", 80, "foobar"}, + {"http-so", "foobar.example.com", 123, "foobar"}, + {"https-so", "foobar.example.com", 443, "foobar"}, + }; + + for (const auto& test_case : cases) { + testing::Message scope_message; + if (test_case.suborigin != std::string()) { + scope_message << test_case.scheme << "-so://" << test_case.suborigin + << "." << test_case.host << ":" << test_case.port; + } else { + scope_message << test_case.scheme << "://" << test_case.host << ":" + << test_case.port; + } + SCOPED_TRACE(scope_message); + + url::Origin origin_without_suborigin = + url::Origin::CreateFromNormalizedTuple(test_case.scheme, test_case.host, + test_case.port); + + url::Origin origin_with_suborigin = + url::Origin::CreateFromNormalizedTupleWithSuborigin( + test_case.scheme, test_case.host, test_case.port, + test_case.suborigin); + + EXPECT_EQ(test_case.scheme, origin_without_suborigin.scheme()); + EXPECT_EQ(test_case.host, origin_without_suborigin.host()); + EXPECT_EQ(test_case.port, origin_without_suborigin.port()); + + EXPECT_EQ(test_case.scheme, origin_with_suborigin.scheme()); + EXPECT_EQ(test_case.host, origin_with_suborigin.host()); + EXPECT_EQ(test_case.port, origin_with_suborigin.port()); + EXPECT_EQ(test_case.suborigin, origin_with_suborigin.suborigin()); + } +} + TEST(OriginTest, ConstructFromGURL) { url::Origin different_origin(GURL("https://not-in-the-list.test/")); @@ -151,10 +199,14 @@ GURL url(test_case.url); EXPECT_TRUE(url.is_valid()); url::Origin origin(url); + EXPECT_TRUE(origin.suborigin().empty()); std::string serialized = origin.Serialize(); + std::string serialized_physical_origin = + origin.GetPhysicalOrigin().Serialize(); ExpectParsedUrlsEqual(GURL(serialized), origin.GetURL()); EXPECT_EQ(test_case.expected, serialized); + EXPECT_EQ(test_case.expected, serialized_physical_origin); // The '<<' operator should produce the same serialization as Serialize(). std::stringstream out; @@ -163,6 +215,119 @@ } } +TEST(OriginTest, SuboriginSerialization) { + struct TestCases { + const char* const url; + const char* const expected; + const char* const expected_physical_origin; + const char* const expected_suborigin; + } cases[] = { + {"http-so://foobar.example.com/", "http-so://foobar.example.com", + "http://example.com", "foobar"}, + {"http-so://foobar.example.com:123/", "http-so://foobar.example.com:123", + "http://example.com:123", "foobar"}, + {"https-so://foobar.example.com/", "https-so://foobar.example.com", + "https://example.com", "foobar"}, + {"https-so://foobar.example.com:123/", + "https-so://foobar.example.com:123", "https://example.com:123", + "foobar"}, + {"http://example.com/", "http://example.com", "http://example.com", ""}, + {"http-so://foobar.example.com/some/path", "http-so://foobar.example.com", + "http://example.com", "foobar"}, + {"http-so://foobar.example.com/some/path?query", + "http-so://foobar.example.com", "http://example.com", "foobar"}, + {"http-so://foobar.example.com/some/path#fragment", + "http-so://foobar.example.com", "http://example.com", "foobar"}, + {"http-so://foobar.example.com/some/path?query#fragment", + "http-so://foobar.example.com", "http://example.com", "foobar"}, + {"http-so://foobar.example.com:1234/some/path?query#fragment", + "http-so://foobar.example.com:1234", "http://example.com:1234", + "foobar"}, + }; + + for (const auto& test_case : cases) { + SCOPED_TRACE(test_case.url); + GURL url(test_case.url); + EXPECT_TRUE(url.is_valid()); + url::Origin origin(url); + std::string serialized = origin.Serialize(); + std::string serialized_physical_origin = + origin.GetPhysicalOrigin().Serialize(); + EXPECT_FALSE(origin.unique()); + EXPECT_EQ(test_case.expected_suborigin, origin.suborigin()); + ExpectParsedUrlsEqual(GURL(serialized), origin.GetURL()); + + EXPECT_EQ(test_case.expected, serialized); + EXPECT_EQ(test_case.expected_physical_origin, serialized_physical_origin); + + // The '<<' operator should produce the same serialization as Serialize(). + std::stringstream out; + out << origin; + EXPECT_EQ(test_case.expected, out.str()); + } + + const char* const failure_cases[] = { + "http-so://.", "http-so://foo", "http-so://.foo", "http-so://foo.", + "https-so://.", "https-so://foo", "https-so://.foo", "https-so://foo.", + }; + + for (const auto& test_case : failure_cases) { + SCOPED_TRACE(test_case); + GURL url(test_case); + EXPECT_TRUE(url.is_valid()); + url::Origin origin(url); + std::string serialized = origin.Serialize(); + std::string serialized_physical_origin = + origin.GetPhysicalOrigin().Serialize(); + EXPECT_TRUE(origin.unique()); + EXPECT_EQ("", origin.suborigin()); + ExpectParsedUrlsEqual(GURL(serialized), origin.GetURL()); + + EXPECT_EQ("null", serialized); + EXPECT_EQ("null", serialized_physical_origin); + } +} + +TEST(OriginTest, SuboriginIsSameOriginWith) { + struct TestCases { + const char* const url1; + const char* const url2; + bool is_same_origin; + bool is_same_physical_origin; + } cases[]{ + {"http-so://foobar1.example.com/", "http-so://foobar1.example.com", true, + true}, + {"http-so://foobar2.example.com/", "https-so://foobar2.example.com", + false, false}, + {"http-so://foobar3.example.com/", "http://example.com", false, true}, + {"https-so://foobar4.example.com/", "https-so://foobar4.example.com", + true, true}, + {"https-so://foobar5.example.com/", "https://example.com", false, true}, + {"http-so://foobar6.example.com/", "http-so://bazbar.example.com", false, + true}, + {"http-so://foobar7.example.com/", "http-so://foobar7.google.com", false, + false}, + }; + + for (const auto& test_case : cases) { + SCOPED_TRACE(test_case.url1); + url::Origin origin1(GURL(test_case.url1)); + url::Origin origin2(GURL(test_case.url2)); + + EXPECT_TRUE(origin1.IsSameOriginWith(origin1)); + EXPECT_TRUE(origin2.IsSameOriginWith(origin2)); + EXPECT_EQ(test_case.is_same_origin, origin1.IsSameOriginWith(origin2)); + EXPECT_EQ(test_case.is_same_origin, origin2.IsSameOriginWith(origin1)); + + EXPECT_TRUE(origin1.IsSamePhysicalOriginWith(origin1)); + EXPECT_TRUE(origin2.IsSamePhysicalOriginWith(origin2)); + EXPECT_EQ(test_case.is_same_physical_origin, + origin1.IsSamePhysicalOriginWith(origin2)); + EXPECT_EQ(test_case.is_same_physical_origin, + origin2.IsSamePhysicalOriginWith(origin1)); + } +} + TEST(OriginTest, Comparison) { // These URLs are arranged in increasing order: const char* const urls[] = {
diff --git a/src/url/scheme_host_port.cc b/src/url/scheme_host_port.cc index e6bb493..b5de079 100644 --- a/src/url/scheme_host_port.cc +++ b/src/url/scheme_host_port.cc
@@ -48,7 +48,8 @@ bool IsValidInput(const base::StringPiece& scheme, const base::StringPiece& host, - uint16_t port) { + uint16_t port, + SchemeHostPort::ConstructPolicy policy) { SchemeType scheme_type = SCHEME_WITH_PORT; bool is_standard = GetStandardSchemeType( scheme.data(), @@ -71,8 +72,14 @@ if (host.empty() || port == 0) return false; - if (!IsCanonicalHost(host)) + // Don't do an expensive canonicalization if the host is already + // canonicalized. + DCHECK(policy == SchemeHostPort::CHECK_CANONICALIZATION || + IsCanonicalHost(host)); + if (policy == SchemeHostPort::CHECK_CANONICALIZATION && + !IsCanonicalHost(host)) { return false; + } return true; @@ -83,8 +90,14 @@ return false; } - if (!IsCanonicalHost(host)) + // Don't do an expensive canonicalization if the host is already + // canonicalized. + DCHECK(policy == SchemeHostPort::CHECK_CANONICALIZATION || + IsCanonicalHost(host)); + if (policy == SchemeHostPort::CHECK_CANONICALIZATION && + !IsCanonicalHost(host)) { return false; + } return true; @@ -104,9 +117,10 @@ SchemeHostPort::SchemeHostPort(base::StringPiece scheme, base::StringPiece host, - uint16_t port) + uint16_t port, + ConstructPolicy policy) : port_(0) { - if (!IsValidInput(scheme, host, port)) + if (!IsValidInput(scheme, host, port, policy)) return; scheme.CopyToString(&scheme_); @@ -114,6 +128,14 @@ port_ = port; } +SchemeHostPort::SchemeHostPort(base::StringPiece scheme, + base::StringPiece host, + uint16_t port) + : SchemeHostPort(scheme, + host, + port, + ConstructPolicy::CHECK_CANONICALIZATION) {} + SchemeHostPort::SchemeHostPort(const GURL& url) : port_(0) { if (!url.is_valid()) return; @@ -126,7 +148,7 @@ if (port == PORT_UNSPECIFIED) port = 0; - if (!IsValidInput(scheme, host, port)) + if (!IsValidInput(scheme, host, port, ALREADY_CANONICALIZED)) return; scheme.CopyToString(&scheme_); @@ -152,6 +174,9 @@ url::Parsed parsed; std::string serialized = SerializeInternal(&parsed); + if (IsInvalid()) + return GURL(std::move(serialized), parsed, false); + // If the serialized string is passed to GURL for parsing, it will append an // empty path "/". Add that here. Note: per RFC 6454 we cannot do this for // normal Origin serialization. @@ -176,13 +201,17 @@ if (IsInvalid()) return result; - parsed->scheme = Component(0, scheme_.length()); - result.append(scheme_); + if (!scheme_.empty()) { + parsed->scheme = Component(0, scheme_.length()); + result.append(scheme_); + } result.append(kStandardSchemeSeparator); - parsed->host = Component(result.length(), host_.length()); - result.append(host_); + if (!host_.empty()) { + parsed->host = Component(result.length(), host_.length()); + result.append(host_); + } if (port_ == 0) return result;
diff --git a/src/url/scheme_host_port.h b/src/url/scheme_host_port.h index dc8862a..065e4aa 100644 --- a/src/url/scheme_host_port.h +++ b/src/url/scheme_host_port.h
@@ -88,6 +88,19 @@ base::StringPiece host, uint16_t port); + // Metadata influencing whether or not the constructor should sanity check + // host canonicalization. + enum ConstructPolicy { CHECK_CANONICALIZATION, ALREADY_CANONICALIZED }; + + // Creates a (scheme, host, port) tuple without performing sanity checking + // that the host and port are canonicalized. This should only be used when + // converting between already normalized types, and should NOT be used for + // IPC. + SchemeHostPort(base::StringPiece scheme, + base::StringPiece host, + uint16_t port, + ConstructPolicy policy); + // Creates a (scheme, host, port) tuple from |url|, as described at // https://tools.ietf.org/html/rfc6454#section-4 //
diff --git a/src/url/scheme_host_port_unittest.cc b/src/url/scheme_host_port_unittest.cc index 46e1cf8..9c59b9f 100644 --- a/src/url/scheme_host_port_unittest.cc +++ b/src/url/scheme_host_port_unittest.cc
@@ -12,6 +12,28 @@ namespace { +void ExpectParsedUrlsEqual(const GURL& a, const GURL& b) { + EXPECT_EQ(a, b); + const url::Parsed& a_parsed = a.parsed_for_possibly_invalid_spec(); + const url::Parsed& b_parsed = b.parsed_for_possibly_invalid_spec(); + EXPECT_EQ(a_parsed.scheme.begin, b_parsed.scheme.begin); + EXPECT_EQ(a_parsed.scheme.len, b_parsed.scheme.len); + EXPECT_EQ(a_parsed.username.begin, b_parsed.username.begin); + EXPECT_EQ(a_parsed.username.len, b_parsed.username.len); + EXPECT_EQ(a_parsed.password.begin, b_parsed.password.begin); + EXPECT_EQ(a_parsed.password.len, b_parsed.password.len); + EXPECT_EQ(a_parsed.host.begin, b_parsed.host.begin); + EXPECT_EQ(a_parsed.host.len, b_parsed.host.len); + EXPECT_EQ(a_parsed.port.begin, b_parsed.port.begin); + EXPECT_EQ(a_parsed.port.len, b_parsed.port.len); + EXPECT_EQ(a_parsed.path.begin, b_parsed.path.begin); + EXPECT_EQ(a_parsed.path.len, b_parsed.path.len); + EXPECT_EQ(a_parsed.query.begin, b_parsed.query.begin); + EXPECT_EQ(a_parsed.query.len, b_parsed.query.len); + EXPECT_EQ(a_parsed.ref.begin, b_parsed.ref.begin); + EXPECT_EQ(a_parsed.ref.len, b_parsed.ref.len); +} + TEST(SchemeHostPortTest, Invalid) { url::SchemeHostPort invalid; EXPECT_EQ("", invalid.scheme()); @@ -37,6 +59,7 @@ EXPECT_TRUE(tuple.Equals(tuple)); EXPECT_TRUE(tuple.Equals(invalid)); EXPECT_TRUE(invalid.Equals(tuple)); + ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL()); } } @@ -63,6 +86,7 @@ EXPECT_EQ(test.port, tuple.port()); EXPECT_FALSE(tuple.IsInvalid()); EXPECT_TRUE(tuple.Equals(tuple)); + ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL()); } } @@ -98,6 +122,7 @@ EXPECT_EQ(0, tuple.port()); EXPECT_TRUE(tuple.IsInvalid()); EXPECT_TRUE(tuple.Equals(tuple)); + ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL()); } } @@ -125,6 +150,7 @@ EXPECT_EQ("", tuple.host()); EXPECT_EQ(0, tuple.port()); EXPECT_TRUE(tuple.IsInvalid()); + ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL()); } } @@ -160,6 +186,7 @@ EXPECT_EQ(test.port, tuple.port()); EXPECT_FALSE(tuple.IsInvalid()); EXPECT_TRUE(tuple.Equals(tuple)); + ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL()); } } @@ -184,6 +211,7 @@ GURL url(test.url); url::SchemeHostPort tuple(url); EXPECT_EQ(test.expected, tuple.Serialize()); + ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL()); } }
diff --git a/src/url/url_canon.h b/src/url/url_canon.h index 95d5345..c4852e4 100644 --- a/src/url/url_canon.h +++ b/src/url/url_canon.h
@@ -379,6 +379,33 @@ CanonOutput* output, CanonHostInfo* host_info); +// Canonicalizes a string according to the host canonicalization rules. Unlike +// CanonicalizeHost, this will not check for IP addresses which can change the +// meaning (and canonicalization) of the components. This means it is possible +// to call this for sub-components of a host name without corruption. +// +// As an example, "01.02.03.04.com" is a canonical hostname. If you called +// CanonicalizeHost on the substring "01.02.03.04" it will get "fixed" to +// "1.2.3.4" which will produce an invalid host name when reassembled. This +// can happen more than one might think because all numbers by themselves are +// considered IP addresses; so "5" canonicalizes to "0.0.0.5". +// +// Be careful: Because Punycode works on each dot-separated substring as a +// unit, you should only pass this function substrings that represent complete +// dot-separated subcomponents of the original host. Even if you have ASCII +// input, percent-escaped characters will have different meanings if split in +// the middle. +// +// Returns true if the host was valid. This function will treat a 0-length +// host as valid (because it's designed to be used for substrings) while the +// full version above will mark empty hosts as broken. +URL_EXPORT bool CanonicalizeHostSubstring(const char* spec, + const Component& host, + CanonOutput* output); +URL_EXPORT bool CanonicalizeHostSubstring(const base::char16* spec, + const Component& host, + CanonOutput* output); + // IP addresses. // // Tries to interpret the given host name as an IPv4 or IPv6 address. If it is
diff --git a/src/url/url_canon_host.cc b/src/url/url_canon_host.cc index d4cdfd5..76a2236 100644 --- a/src/url/url_canon_host.cc +++ b/src/url/url_canon_host.cc
@@ -308,7 +308,25 @@ return DoIDNHost(host, host_len, output); } -template<typename CHAR, typename UCHAR> +template <typename CHAR, typename UCHAR> +bool DoHostSubstring(const CHAR* spec, + const Component& host, + CanonOutput* output) { + bool has_non_ascii, has_escaped; + ScanHostname<CHAR, UCHAR>(spec, host, &has_non_ascii, &has_escaped); + + if (has_non_ascii || has_escaped) { + return DoComplexHost(&spec[host.begin], host.len, has_non_ascii, + has_escaped, output); + } + + const bool success = + DoSimpleHost(&spec[host.begin], host.len, output, &has_non_ascii); + DCHECK(!has_non_ascii); + return success; +} + +template <typename CHAR, typename UCHAR> void DoHost(const CHAR* spec, const Component& host, CanonOutput* output, @@ -320,26 +338,10 @@ return; } - bool has_non_ascii, has_escaped; - ScanHostname<CHAR, UCHAR>(spec, host, &has_non_ascii, &has_escaped); - // Keep track of output's initial length, so we can rewind later. const int output_begin = output->length(); - bool success; - if (!has_non_ascii && !has_escaped) { - success = DoSimpleHost(&spec[host.begin], host.len, - output, &has_non_ascii); - DCHECK(!has_non_ascii); - } else { - success = DoComplexHost(&spec[host.begin], host.len, - has_non_ascii, has_escaped, output); - } - - if (!success) { - // Canonicalization failed. Set BROKEN to notify the caller. - host_info->family = CanonHostInfo::BROKEN; - } else { + if (DoHostSubstring<CHAR, UCHAR>(spec, host, output)) { // After all the other canonicalization, check if we ended up with an IP // address. IP addresses are small, so writing into this temporary buffer // should not cause an allocation. @@ -355,6 +357,9 @@ output->set_length(output_begin); output->Append(canon_ip.data(), canon_ip.length()); } + } else { + // Canonicalization failed. Set BROKEN to notify the caller. + host_info->family = CanonHostInfo::BROKEN; } host_info->out_host = MakeRange(output_begin, output->length()); @@ -396,4 +401,16 @@ DoHost<base::char16, base::char16>(spec, host, output, host_info); } +bool CanonicalizeHostSubstring(const char* spec, + const Component& host, + CanonOutput* output) { + return DoHostSubstring<char, unsigned char>(spec, host, output); +} + +bool CanonicalizeHostSubstring(const base::char16* spec, + const Component& host, + CanonOutput* output) { + return DoHostSubstring<base::char16, base::char16>(spec, host, output); +} + } // namespace url
diff --git a/src/url/url_canon_icu_unittest.cc b/src/url/url_canon_icu_unittest.cc index f7ce199..83a7263 100644 --- a/src/url/url_canon_icu_unittest.cc +++ b/src/url/url_canon_icu_unittest.cc
@@ -14,8 +14,6 @@ namespace url { -using test_utils::WStringToUTF16; - namespace { // Wrapper around a UConverter object that managers creation and destruction. @@ -64,7 +62,8 @@ std::string str; StdStringCanonOutput output(&str); - base::string16 input_str(WStringToUTF16(icu_cases[i].input)); + base::string16 input_str( + test_utils::TruncateWStringToUTF16(icu_cases[i].input)); int input_len = static_cast<int>(input_str.length()); converter.ConvertFromUTF16(input_str.c_str(), input_len, &output); output.Complete(); @@ -134,7 +133,8 @@ } if (query_cases[i].input16) { - base::string16 input16(WStringToUTF16(query_cases[i].input16)); + base::string16 input16( + test_utils::TruncateWStringToUTF16(query_cases[i].input16)); int len = static_cast<int>(input16.length()); Component in_comp(0, len); std::string out_str;
diff --git a/src/url/url_canon_stdurl.cc b/src/url/url_canon_stdurl.cc index 7d1758b..e0bca9d 100644 --- a/src/url/url_canon_stdurl.cc +++ b/src/url/url_canon_stdurl.cc
@@ -120,6 +120,14 @@ if (!strncmp(scheme, kWsScheme, scheme_len)) default_port = 80; break; + case 7: + if (!strncmp(scheme, kHttpSuboriginScheme, scheme_len)) + default_port = 80; + break; + case 8: + if (!strncmp(scheme, kHttpsSuboriginScheme, scheme_len)) + default_port = 443; + break; } return default_port; }
diff --git a/src/url/url_canon_unittest.cc b/src/url/url_canon_unittest.cc index 2f053f2..26d7815 100644 --- a/src/url/url_canon_unittest.cc +++ b/src/url/url_canon_unittest.cc
@@ -6,6 +6,7 @@ #include <stddef.h> #include "base/macros.h" +#include "base/strings/utf_string_conversions.h" #include "testing/base/public/gunit.h" #include "url/third_party/mozilla/url_parse.h" #include "url/url_canon.h" @@ -15,10 +16,6 @@ namespace url { -using test_utils::WStringToUTF16; -using test_utils::ConvertUTF8ToUTF16; -using test_utils::ConvertUTF16ToUTF8; - namespace { struct ComponentCase { @@ -195,7 +192,8 @@ out_str.clear(); StdStringCanonOutput output(&out_str); - base::string16 input_str(WStringToUTF16(utf_cases[i].input16)); + base::string16 input_str( + test_utils::TruncateWStringToUTF16(utf_cases[i].input16)); int input_len = static_cast<int>(input_str.length()); bool success = true; for (int ch = 0; ch < input_len; ch++) { @@ -213,11 +211,12 @@ // UTF-16 -> UTF-8 std::string input8_str(utf_cases[i].input8); - base::string16 input16_str(WStringToUTF16(utf_cases[i].input16)); - EXPECT_EQ(input8_str, ConvertUTF16ToUTF8(input16_str)); + base::string16 input16_str( + test_utils::TruncateWStringToUTF16(utf_cases[i].input16)); + EXPECT_EQ(input8_str, base::UTF16ToUTF8(input16_str)); // UTF-8 -> UTF-16 - EXPECT_EQ(input16_str, ConvertUTF8ToUTF16(input8_str)); + EXPECT_EQ(input16_str, base::UTF8ToUTF16(input8_str)); } } } @@ -265,7 +264,7 @@ out_str.clear(); StdStringCanonOutput output2(&out_str); - base::string16 wide_input(ConvertUTF8ToUTF16(scheme_cases[i].input)); + base::string16 wide_input(base::UTF8ToUTF16(scheme_cases[i].input)); in_comp.len = static_cast<int>(wide_input.length()); success = CanonicalizeScheme(wide_input.c_str(), in_comp, &output2, &out_comp); @@ -530,7 +529,8 @@ // Wide version. if (host_cases[i].input16) { - base::string16 input16(WStringToUTF16(host_cases[i].input16)); + base::string16 input16( + test_utils::TruncateWStringToUTF16(host_cases[i].input16)); int host_len = static_cast<int>(input16.length()); Component in_comp(0, host_len); Component out_comp; @@ -580,7 +580,8 @@ // Wide version. if (host_cases[i].input16) { - base::string16 input16(WStringToUTF16(host_cases[i].input16)); + base::string16 input16( + test_utils::TruncateWStringToUTF16(host_cases[i].input16)); int host_len = static_cast<int>(input16.length()); Component in_comp(0, host_len); @@ -702,7 +703,8 @@ } // 16-bit version. - base::string16 input16(WStringToUTF16(cases[i].input16)); + base::string16 input16( + test_utils::TruncateWStringToUTF16(cases[i].input16)); component = Component(0, static_cast<int>(input16.length())); std::string out_str2; @@ -854,7 +856,8 @@ } // 16-bit version. - base::string16 input16(WStringToUTF16(cases[i].input16)); + base::string16 input16( + test_utils::TruncateWStringToUTF16(cases[i].input16)); component = Component(0, static_cast<int>(input16.length())); std::string out_str2; @@ -887,6 +890,51 @@ EXPECT_FALSE(host_info.IsIPAddress()); } +// Verifies that CanonicalizeHostSubstring produces the expected output and +// does not "fix" IP addresses. Because this code is a subset of +// CanonicalizeHost, the shared functionality is not tested. +TEST(URLCanonTest, CanonicalizeHostSubstring) { + // Basic sanity check. + { + std::string out_str; + StdStringCanonOutput output(&out_str); + EXPECT_TRUE(CanonicalizeHostSubstring("M\xc3\x9cNCHEN.com", + Component(0, 12), &output)); + output.Complete(); + EXPECT_EQ("xn--mnchen-3ya.com", out_str); + } + + // Failure case. + { + std::string out_str; + StdStringCanonOutput output(&out_str); + EXPECT_FALSE(CanonicalizeHostSubstring( + test_utils::TruncateWStringToUTF16(L"\xfdd0zyx.com").c_str(), + Component(0, 8), &output)); + output.Complete(); + EXPECT_EQ("%EF%BF%BDzyx.com", out_str); + } + + // Should return true for empty input strings. + { + std::string out_str; + StdStringCanonOutput output(&out_str); + EXPECT_TRUE(CanonicalizeHostSubstring("", Component(0, 0), &output)); + output.Complete(); + EXPECT_EQ(std::string(), out_str); + } + + // Numbers that look like IP addresses should not be changed. + { + std::string out_str; + StdStringCanonOutput output(&out_str); + EXPECT_TRUE( + CanonicalizeHostSubstring("01.02.03.04", Component(0, 11), &output)); + output.Complete(); + EXPECT_EQ("01.02.03.04", out_str); + } +} + TEST(URLCanonTest, UserInfo) { // Note that the canonicalizer should escape and treat empty components as // not being there. @@ -940,7 +988,7 @@ // Now try the wide version out_str.clear(); StdStringCanonOutput output2(&out_str); - base::string16 wide_input(ConvertUTF8ToUTF16(user_info_cases[i].input)); + base::string16 wide_input(base::UTF8ToUTF16(user_info_cases[i].input)); success = CanonicalizeUserInfo(wide_input.c_str(), parsed.username, wide_input.c_str(), @@ -1003,7 +1051,7 @@ // Now try the wide version out_str.clear(); StdStringCanonOutput output2(&out_str); - base::string16 wide_input(ConvertUTF8ToUTF16(port_cases[i].input)); + base::string16 wide_input(base::UTF8ToUTF16(port_cases[i].input)); success = CanonicalizePort(wide_input.c_str(), in_comp, port_cases[i].default_port, @@ -1123,7 +1171,8 @@ } if (path_cases[i].input16) { - base::string16 input16(WStringToUTF16(path_cases[i].input16)); + base::string16 input16( + test_utils::TruncateWStringToUTF16(path_cases[i].input16)); int len = static_cast<int>(input16.length()); Component in_comp(0, len); Component out_comp; @@ -1198,7 +1247,8 @@ } if (query_cases[i].input16) { - base::string16 input16(WStringToUTF16(query_cases[i].input16)); + base::string16 input16( + test_utils::TruncateWStringToUTF16(query_cases[i].input16)); int len = static_cast<int>(input16.length()); Component in_comp(0, len); std::string out_str; @@ -1260,7 +1310,8 @@ // 16-bit input if (ref_cases[i].input16) { - base::string16 input16(WStringToUTF16(ref_cases[i].input16)); + base::string16 input16( + test_utils::TruncateWStringToUTF16(ref_cases[i].input16)); int len = static_cast<int>(input16.length()); Component in_comp(0, len); Component out_comp; @@ -1896,12 +1947,12 @@ const base::char16 fill_char = 0xffff; memset(buf, fill_mem, sizeof(buf)); EXPECT_EQ(0, _itow_s(12, buf, sizeof(buf) / 2 - 1, 10)); - EXPECT_EQ(WStringToUTF16(L"12"), base::string16(buf)); + EXPECT_EQ(base::UTF8ToUTF16("12"), base::string16(buf)); EXPECT_EQ(fill_char, buf[3]); // Test the edge cases - exactly the buffer size and one over EXPECT_EQ(0, _itow_s(1234, buf, sizeof(buf) / 2 - 1, 10)); - EXPECT_EQ(WStringToUTF16(L"1234"), base::string16(buf)); + EXPECT_EQ(base::UTF8ToUTF16("1234"), base::string16(buf)); EXPECT_EQ(fill_char, buf[5]); memset(buf, fill_mem, sizeof(buf)); @@ -1911,12 +1962,13 @@ // Test the template overload (note that this will see the full buffer) memset(buf, fill_mem, sizeof(buf)); EXPECT_EQ(0, _itow_s(12, buf, 10)); - EXPECT_EQ(WStringToUTF16(L"12"), base::string16(buf)); + EXPECT_EQ(base::UTF8ToUTF16("12"), + base::string16(buf)); EXPECT_EQ(fill_char, buf[3]); memset(buf, fill_mem, sizeof(buf)); EXPECT_EQ(0, _itow_s(12345, buf, 10)); - EXPECT_EQ(WStringToUTF16(L"12345"), base::string16(buf)); + EXPECT_EQ(base::UTF8ToUTF16("12345"), base::string16(buf)); EXPECT_EQ(EINVAL, _itow_s(123456, buf, 10)); } @@ -2152,7 +2204,7 @@ for (int i = 0; i < 4800; i++) new_query.push_back('a'); - base::string16 new_path(WStringToUTF16(L"/foo")); + base::string16 new_path(test_utils::TruncateWStringToUTF16(L"/foo")); repl.SetPath(new_path.c_str(), Component(0, 4)); repl.SetQuery(new_query.c_str(), Component(0, static_cast<int>(new_query.length()))); @@ -2173,4 +2225,35 @@ EXPECT_TRUE(expected == repl_str); } +TEST(URLCanonTest, DefaultPortForScheme) { + struct TestCases { + const char* scheme; + const int expected_port; + } cases[]{ + {"http", 80}, + {"https", 443}, + {"ftp", 21}, + {"ws", 80}, + {"wss", 443}, + {"gopher", 70}, + {"http-so", 80}, + {"https-so", 443}, + {"fake-scheme", PORT_UNSPECIFIED}, + {"HTTP", PORT_UNSPECIFIED}, + {"HTTPS", PORT_UNSPECIFIED}, + {"FTP", PORT_UNSPECIFIED}, + {"WS", PORT_UNSPECIFIED}, + {"WSS", PORT_UNSPECIFIED}, + {"GOPHER", PORT_UNSPECIFIED}, + {"HTTP-SO", PORT_UNSPECIFIED}, + {"HTTPS-SO", PORT_UNSPECIFIED}, + }; + + for (auto& test_case : cases) { + SCOPED_TRACE(test_case.scheme); + EXPECT_EQ(test_case.expected_port, + DefaultPortForScheme(test_case.scheme, strlen(test_case.scheme))); + } +} + } // namespace url
diff --git a/src/url/url_constants.cc b/src/url/url_constants.cc index 549819e..73c9a76 100644 --- a/src/url/url_constants.cc +++ b/src/url/url_constants.cc
@@ -24,6 +24,9 @@ const char kWsScheme[] = "ws"; const char kWssScheme[] = "wss"; +const char kHttpSuboriginScheme[] = "http-so"; +const char kHttpsSuboriginScheme[] = "https-so"; + const char kStandardSchemeSeparator[] = "://"; const size_t kMaxURLChars = 2 * 1024 * 1024;
diff --git a/src/url/url_constants.h b/src/url/url_constants.h index 3a423d2..c110589 100644 --- a/src/url/url_constants.h +++ b/src/url/url_constants.h
@@ -30,6 +30,11 @@ URL_EXPORT extern const char kWsScheme[]; URL_EXPORT extern const char kWssScheme[]; +// Special HTTP and HTTPS schemes for serialization of suborigins. See +// https://w3c.github.io/webappsec-suborigins/. +URL_EXPORT extern const char kHttpSuboriginScheme[]; +URL_EXPORT extern const char kHttpsSuboriginScheme[]; + // Used to separate a standard scheme and the hostname: "://". URL_EXPORT extern const char kStandardSchemeSeparator[];
diff --git a/src/url/url_test_utils.h b/src/url/url_test_utils.h index 174e5e0..8c89622 100644 --- a/src/url/url_test_utils.h +++ b/src/url/url_test_utils.h
@@ -11,6 +11,7 @@ #include <string> #include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" #include "testing/base/public/gunit.h" #include "url/url_canon_internal.h" @@ -18,10 +19,12 @@ namespace test_utils { -// Converts a UTF-16 string from native wchar_t format to char16, by -// truncating the high 32 bits. This is not meant to handle true UTF-32 -// encoded strings. -inline base::string16 WStringToUTF16(const wchar_t* src) { +// Converts a UTF-16 string from native wchar_t format to char16 by +// truncating the high 32 bits. This is different than the conversion function +// in base bacause it passes invalid UTF-16 characters which is important for +// test purposes. As a result, this is not meant to handle true UTF-32 encoded +// strings. +inline base::string16 TruncateWStringToUTF16(const wchar_t* src) { base::string16 str; int length = static_cast<int>(wcslen(src)); for (int i = 0; i < length; ++i) { @@ -30,25 +33,6 @@ return str; } -// Converts a string from UTF-8 to UTF-16. -inline base::string16 ConvertUTF8ToUTF16(const std::string& src) { - int length = static_cast<int>(src.length()); - EXPECT_LT(length, 1024); - RawCanonOutputW<1024> output; - EXPECT_TRUE(ConvertUTF8ToUTF16(src.data(), length, &output)); - return base::string16(output.data(), output.length()); -} - -// Converts a string from UTF-16 to UTF-8. -inline std::string ConvertUTF16ToUTF8(const base::string16& src) { - std::string str; - StdStringCanonOutput output(&str); - EXPECT_TRUE(ConvertUTF16ToUTF8(src.data(), static_cast<int>(src.length()), - &output)); - output.Complete(); - return str; -} - } // namespace test_utils } // namespace url
diff --git a/src/url/url_util.cc b/src/url/url_util.cc index 8522eb1..ebe386f 100644 --- a/src/url/url_util.cc +++ b/src/url/url_util.cc
@@ -21,25 +21,29 @@ namespace { -const int kNumStandardURLSchemes = 8; +const int kNumStandardURLSchemes = 10; const SchemeWithType kStandardURLSchemes[kNumStandardURLSchemes] = { - {kHttpScheme, SCHEME_WITH_PORT}, - {kHttpsScheme, SCHEME_WITH_PORT}, - // Yes, file URLs can have a hostname, so file URLs should be handled as - // "standard". File URLs never have a port as specified by the SchemeType - // field. - {kFileScheme, SCHEME_WITHOUT_PORT}, - {kFtpScheme, SCHEME_WITH_PORT}, - {kGopherScheme, SCHEME_WITH_PORT}, - {kWsScheme, SCHEME_WITH_PORT}, // WebSocket. - {kWssScheme, SCHEME_WITH_PORT}, // WebSocket secure. - {kFileSystemScheme, SCHEME_WITHOUT_AUTHORITY}, + {kHttpScheme, SCHEME_WITH_PORT}, + {kHttpsScheme, SCHEME_WITH_PORT}, + // Yes, file URLs can have a hostname, so file URLs should be handled as + // "standard". File URLs never have a port as specified by the SchemeType + // field. + {kFileScheme, SCHEME_WITHOUT_PORT}, + {kFtpScheme, SCHEME_WITH_PORT}, + {kGopherScheme, SCHEME_WITH_PORT}, + {kWsScheme, SCHEME_WITH_PORT}, // WebSocket. + {kWssScheme, SCHEME_WITH_PORT}, // WebSocket secure. + {kFileSystemScheme, SCHEME_WITHOUT_AUTHORITY}, + {kHttpSuboriginScheme, SCHEME_WITH_PORT}, + {kHttpsSuboriginScheme, SCHEME_WITH_PORT}, }; -const int kNumReferrerURLSchemes = 2; +const int kNumReferrerURLSchemes = 4; const SchemeWithType kReferrerURLSchemes[kNumReferrerURLSchemes] = { - {kHttpScheme, SCHEME_WITH_PORT}, - {kHttpsScheme, SCHEME_WITH_PORT}, + {kHttpScheme, SCHEME_WITH_PORT}, + {kHttpsScheme, SCHEME_WITH_PORT}, + {kHttpSuboriginScheme, SCHEME_WITH_PORT}, + {kHttpsSuboriginScheme, SCHEME_WITH_PORT}, }; // Lists of the currently installed standard and referrer schemes. These lists
diff --git a/src/url/url_util_unittest.cc b/src/url/url_util_unittest.cc index eceb505..6d7c8f7 100644 --- a/src/url/url_util_unittest.cc +++ b/src/url/url_util_unittest.cc
@@ -214,15 +214,15 @@ RawCanonOutputT<base::char16> output; DecodeURLEscapeSequences(input, strlen(input), &output); EXPECT_EQ(decode_cases[i].output, - test_utils::ConvertUTF16ToUTF8(base::string16(output.data(), - output.length()))); + base::UTF16ToUTF8(base::string16(output.data(), + output.length()))); } // Our decode should decode %00 const char zero_input[] = "%00"; RawCanonOutputT<base::char16> zero_output; DecodeURLEscapeSequences(zero_input, strlen(zero_input), &zero_output); - EXPECT_NE("%00", test_utils::ConvertUTF16ToUTF8( + EXPECT_NE("%00", base::UTF16ToUTF8( base::string16(zero_output.data(), zero_output.length()))); // Test the error behavior for invalid UTF-8.