// Copyright 2015 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 "url/origin.h" #include #include #include "base/logging.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "url/gurl.h" #include "url/url_canon.h" #include "url/url_canon_stdstring.h" #include "url/url_constants.h" #include "url/url_util.h" namespace url { Origin::Origin() : nonce_(Nonce()) {} Origin Origin::Create(const GURL& url) { if (!url.is_valid()) return Origin(); SchemeHostPort tuple; if (url.SchemeIsFileSystem()) { tuple = SchemeHostPort(*url.inner_url()); } else if (url.SchemeIsBlob()) { // If we're dealing with a 'blob:' URL, https://url.spec.whatwg.org/#origin // defines the origin as the origin of the URL which results from parsing // the "path", which boils down to everything after the scheme. GURL's // 'GetContent()' gives us exactly that. tuple = SchemeHostPort(GURL(url.GetContent())); } else { tuple = SchemeHostPort(url); // It's SchemeHostPort's responsibility to filter out unrecognized schemes; // sanity check that this is happening. DCHECK(tuple.IsInvalid() || url.IsStandard() || base::ContainsValue(GetLocalSchemes(), url.scheme_piece()) || AllowNonStandardSchemesForAndroidWebView()); } if (tuple.IsInvalid()) return Origin(); return Origin(std::move(tuple)); } Origin Origin::Resolve(const GURL& url, const Origin& base_origin) { if (url.IsAboutBlank()) return base_origin; Origin result = Origin::Create(url); if (!result.opaque()) return result; return base_origin.DeriveNewOpaqueOrigin(); } Origin::Origin(const Origin& other) = default; Origin& Origin::operator=(const Origin& other) = default; Origin::Origin(Origin&& other) = default; Origin& Origin::operator=(Origin&& other) = default; Origin::~Origin() = default; // static base::Optional Origin::UnsafelyCreateTupleOriginWithoutNormalization( base::StringPiece scheme, base::StringPiece host, uint16_t port) { SchemeHostPort tuple(scheme.as_string(), host.as_string(), port, SchemeHostPort::CHECK_CANONICALIZATION); if (tuple.IsInvalid()) return base::nullopt; return Origin(std::move(tuple)); } // static base::Optional Origin::UnsafelyCreateOpaqueOriginWithoutNormalization( base::StringPiece precursor_scheme, base::StringPiece precursor_host, uint16_t precursor_port, const Origin::Nonce& nonce) { SchemeHostPort precursor(precursor_scheme.as_string(), precursor_host.as_string(), precursor_port, SchemeHostPort::CHECK_CANONICALIZATION); // For opaque origins, it is okay for the SchemeHostPort to be invalid; // however, this should only arise when the arguments indicate the // canonical representation of the invalid SchemeHostPort. if (precursor.IsInvalid() && !(precursor_scheme.empty() && precursor_host.empty() && precursor_port == 0)) { return base::nullopt; } return Origin(std::move(nonce), std::move(precursor)); } // static Origin Origin::CreateFromNormalizedTuple(std::string scheme, std::string host, uint16_t port) { SchemeHostPort tuple(std::move(scheme), std::move(host), port, SchemeHostPort::ALREADY_CANONICALIZED); if (tuple.IsInvalid()) return Origin(); return Origin(std::move(tuple)); } // static Origin Origin::CreateOpaqueFromNormalizedPrecursorTuple( std::string precursor_scheme, std::string precursor_host, uint16_t precursor_port, const Origin::Nonce& nonce) { SchemeHostPort precursor(std::move(precursor_scheme), std::move(precursor_host), precursor_port, SchemeHostPort::ALREADY_CANONICALIZED); // For opaque origins, it is okay for the SchemeHostPort to be invalid. return Origin(std::move(nonce), std::move(precursor)); } std::string Origin::Serialize() const { if (opaque()) return "null"; if (scheme() == kFileScheme) return "file://"; return tuple_.Serialize(); } GURL Origin::GetURL() const { if (opaque()) return GURL(); if (scheme() == kFileScheme) return GURL("file:///"); return tuple_.GetURL(); } base::Optional Origin::GetNonceForSerialization() const { // TODO(nasko): Consider not making a copy here, but return a reference to // the nonce. return nonce_ ? base::make_optional(nonce_->token()) : base::nullopt; } bool Origin::IsSameOriginWith(const Origin& other) const { // scheme/host/port must match, even for opaque origins where |tuple_| holds // the precursor origin. return std::tie(tuple_, nonce_) == std::tie(other.tuple_, other.nonce_); } bool Origin::DomainIs(base::StringPiece canonical_domain) const { return !opaque() && url::DomainIs(tuple_.host(), canonical_domain); } bool Origin::operator<(const Origin& other) const { return std::tie(tuple_, nonce_) < std::tie(other.tuple_, other.nonce_); } Origin Origin::DeriveNewOpaqueOrigin() const { return Origin(Nonce(), tuple_); } Origin::Origin(SchemeHostPort tuple) : tuple_(std::move(tuple)) { DCHECK(!opaque()); DCHECK(!tuple_.IsInvalid()); } // Constructs an opaque origin derived from |precursor|. Origin::Origin(const Nonce& nonce, SchemeHostPort precursor) : tuple_(std::move(precursor)), nonce_(std::move(nonce)) { DCHECK(opaque()); // |precursor| is retained, but not accessible via scheme()/host()/port(). DCHECK_EQ("", scheme()); DCHECK_EQ("", host()); DCHECK_EQ(0U, port()); } std::ostream& operator<<(std::ostream& out, const url::Origin& origin) { out << origin.Serialize(); if (origin.opaque()) { // For opaque origins, log the nonce and precursor as well. Without this, // EXPECT_EQ failures between opaque origins are nearly impossible to // understand. out << " [internally: " << *origin.nonce_; if (origin.tuple_.IsInvalid()) out << " anonymous"; else out << " derived from " << origin.tuple_; out << "]"; } else if (origin.scheme() == kFileScheme) { out << " [internally: " << origin.tuple_ << "]"; } return out; } std::ostream& operator<<(std::ostream& out, const url::Origin::Nonce& nonce) { // Subtle: don't let logging trigger lazy-generation of the token value. if (nonce.raw_token().is_empty()) return (out << "(nonce TBD)"); else return (out << nonce.raw_token()); } bool IsSameOriginWith(const GURL& a, const GURL& b) { return Origin::Create(a).IsSameOriginWith(Origin::Create(b)); } Origin::Nonce::Nonce() {} Origin::Nonce::Nonce(const base::UnguessableToken& token) : token_(token) { CHECK(!token_.is_empty()); } const base::UnguessableToken& Origin::Nonce::token() const { // Inspecting the value of a nonce triggers lazy-generation. // TODO(dcheng): UnguessableToken::is_empty should go away -- what sentinel // value to use instead? if (token_.is_empty()) token_ = base::UnguessableToken::Create(); return token_; } const base::UnguessableToken& Origin::Nonce::raw_token() const { return token_; } // Copying a Nonce triggers lazy-generation of the token. Origin::Nonce::Nonce(const Origin::Nonce& other) : token_(other.token()) {} Origin::Nonce& Origin::Nonce::operator=(const Origin::Nonce& other) { // Copying a Nonce triggers lazy-generation of the token. token_ = other.token(); return *this; } // Moving a nonce does NOT trigger lazy-generation of the token. Origin::Nonce::Nonce(Origin::Nonce&& other) : token_(other.token_) { other.token_ = base::UnguessableToken(); // Reset |other|. } Origin::Nonce& Origin::Nonce::operator=(Origin::Nonce&& other) { token_ = other.token_; other.token_ = base::UnguessableToken(); // Reset |other|. return *this; } bool Origin::Nonce::operator<(const Origin::Nonce& other) const { // When comparing, lazy-generation is required of both tokens, so that an // ordering is established. return token() < other.token(); } bool Origin::Nonce::operator==(const Origin::Nonce& other) const { // Equality testing doesn't actually require that the tokens be generated. // If the tokens are both zero, equality only holds if they're the same // object. return (other.token_ == token_) && !(token_.is_empty() && (&other != this)); } bool Origin::Nonce::operator!=(const Origin::Nonce& other) const { return !(*this == other); } } // namespace url