#include "url/origin.h"
#include <stdint.h>
#include <algorithm>
#include <compare>
#include <ostream>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include "base/base64.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/containers/contains.h"
#include "base/containers/span.h"
#include "base/debug/crash_logging.h"
#include "base/pickle.h"
#include "base/strings/strcat.h"
#include "base/trace_event/memory_usage_estimator.h"
#include "base/trace_event/trace_event.h"
#include "base/unguessable_token.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
#include "url/url_constants.h"
#include "url/url_features.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()) {
tuple = SchemeHostPort(GURL(url.GetContent()));
} else {
tuple = SchemeHostPort(url);
DCHECK(!tuple.IsValid() || url.IsStandard() ||
base::Contains(GetLocalSchemes(), url.scheme()) ||
AllowNonStandardSchemesForAndroidWebView());
}
if (!tuple.IsValid())
return Origin();
return Origin(std::move(tuple));
}
Origin Origin::Resolve(const GURL& url, const Origin& base_origin) {
if (url.SchemeIs(kAboutScheme) || url.is_empty())
return base_origin;
Origin result = Origin::Create(url);
if (!result.opaque())
return result;
return base_origin.DeriveNewOpaqueOrigin();
}
Origin::Origin(const Origin&) = default;
Origin& Origin::operator=(const Origin&) = default;
Origin::Origin(Origin&&) noexcept = default;
Origin& Origin::operator=(Origin&&) noexcept = default;
Origin::~Origin() = default;
std::optional<Origin> Origin::UnsafelyCreateTupleOriginWithoutNormalization(
std::string_view scheme,
std::string_view host,
uint16_t port) {
SchemeHostPort tuple(std::string(scheme), std::string(host), port,
SchemeHostPort::CHECK_CANONICALIZATION);
if (!tuple.IsValid())
return std::nullopt;
return Origin(std::move(tuple));
}
std::optional<Origin> Origin::UnsafelyCreateOpaqueOriginWithoutNormalization(
std::string_view precursor_scheme,
std::string_view precursor_host,
uint16_t precursor_port,
const Origin::Nonce& nonce) {
SchemeHostPort precursor(std::string(precursor_scheme),
std::string(precursor_host), precursor_port,
SchemeHostPort::CHECK_CANONICALIZATION);
if (!precursor.IsValid() &&
!(precursor_scheme.empty() && precursor_host.empty() &&
precursor_port == 0)) {
return std::nullopt;
}
return Origin(std::move(nonce), std::move(precursor));
}
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.IsValid())
return Origin();
return Origin(std::move(tuple));
}
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);
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();
}
const base::UnguessableToken* Origin::GetNonceForSerialization() const {
return nonce_ ? &nonce_->token() : nullptr;
}
bool Origin::IsSameOriginWith(const Origin& other) const {
return *this == other;
}
bool Origin::IsSameOriginWith(const GURL& url) const {
if (opaque())
return false;
return IsSameOriginWith(url::Origin::Create(url));
}
bool Origin::CanBeDerivedFrom(const GURL& url) const {
DCHECK(url.is_valid());
if (base::Contains(url::GetNoAccessSchemes(), url.GetScheme()) &&
!url.SchemeIs(kAboutScheme)) {
if (!opaque())
return false;
if (!tuple_.IsValid())
return true;
}
SchemeHostPort url_tuple;
if (url.IsStandard()) {
if (url.SchemeIsFileSystem()) {
url_tuple = SchemeHostPort(*url.inner_url());
} else {
url_tuple = SchemeHostPort(url);
}
return url_tuple == tuple_;
} else if (url.SchemeIsBlob()) {
if (!tuple_.IsValid())
return true;
url_tuple = SchemeHostPort(GURL(url.GetContent()));
return url_tuple == tuple_;
}
DCHECK(!url.IsStandard());
if (url.SchemeIs(kAboutScheme))
return true;
if (url.SchemeIs(kDataScheme))
return opaque();
if (!tuple_.IsValid())
return true;
#if BUILDFLAG(ARKWEB_NETWORK_LOAD)
if (IsUsingStandardCompliantNonSpecialSchemeURLParsing()) {
return SchemeHostPort(url) == tuple_;
} else {
return url.scheme() == tuple_.scheme();
}
#else
return SchemeHostPort(url) == tuple_;
#endif
}
bool Origin::DomainIs(std::string_view canonical_domain) const {
return !opaque() && url::DomainIs(tuple_.host(), canonical_domain);
}
Origin Origin::DeriveNewOpaqueOrigin() const {
return Origin(Nonce(), tuple_);
}
const base::UnguessableToken* Origin::GetNonceForTesting() const {
return GetNonceForSerialization();
}
std::string Origin::GetDebugString(bool include_nonce) const {
if (!opaque()) {
std::string out = Serialize();
if (scheme() == kFileScheme)
base::StrAppend(&out, {" [internally: ", tuple_.Serialize(), "]"});
return out;
}
std::string out = base::StrCat({Serialize(), " [internally:"});
if (include_nonce) {
out += " (";
if (nonce_->raw_token().is_empty())
out += "nonce TBD";
else
out += nonce_->raw_token().ToString();
out += ")";
}
if (!tuple_.IsValid())
base::StrAppend(&out, {" anonymous]"});
else
base::StrAppend(&out, {" derived from ", tuple_.Serialize(), "]"});
return out;
}
Origin::Origin(SchemeHostPort tuple) : tuple_(std::move(tuple)) {
DCHECK(!opaque());
DCHECK(tuple_.IsValid());
}
Origin::Origin(const Nonce& nonce, SchemeHostPort precursor)
: tuple_(std::move(precursor)), nonce_(std::move(nonce)) {
DCHECK(opaque());
DCHECK_EQ("", scheme());
DCHECK_EQ("", host());
DCHECK_EQ(0U, port());
}
std::optional<std::string> Origin::SerializeWithNonce() const {
return SerializeWithNonceImpl();
}
std::optional<std::string> Origin::SerializeWithNonceAndInitIfNeeded() {
GetNonceForSerialization();
return SerializeWithNonceImpl();
}
std::optional<std::string> Origin::SerializeWithNonceImpl() const {
if (!opaque() && !tuple_.IsValid())
return std::nullopt;
base::Pickle pickle;
pickle.WriteString(tuple_.Serialize());
if (opaque() && !nonce_->raw_token().is_empty()) {
pickle.WriteUInt64(nonce_->token().GetHighForSerialization());
pickle.WriteUInt64(nonce_->token().GetLowForSerialization());
} else if (opaque()) {
pickle.WriteUInt64(0);
pickle.WriteUInt64(0);
}
base::span<const uint8_t> UNSAFE_TODO(
data(static_cast<const uint8_t*>(pickle.data()), pickle.size()));
return base::Base64Encode(data);
}
std::optional<Origin> Origin::Deserialize(std::string_view value) {
std::string data;
if (!base::Base64Decode(value, &data))
return std::nullopt;
base::Pickle pickle =
base::Pickle::WithUnownedBuffer(base::as_byte_span(data));
base::PickleIterator reader(pickle);
std::string pickled_url;
if (!reader.ReadString(&pickled_url))
return std::nullopt;
GURL url(pickled_url);
bool is_opaque = !reader.ReachedEnd();
if (!is_opaque && !url.is_valid())
return std::nullopt;
SchemeHostPort tuple(url);
if (!is_opaque) {
Origin origin(tuple);
if (origin.opaque())
return std::nullopt;
return origin;
}
uint64_t nonce_high = 0;
if (!reader.ReadUInt64(&nonce_high))
return std::nullopt;
uint64_t nonce_low = 0;
if (!reader.ReadUInt64(&nonce_low))
return std::nullopt;
std::optional<base::UnguessableToken> nonce_token =
base::UnguessableToken::Deserialize(nonce_high, nonce_low);
Origin::Nonce nonce;
if (nonce_token.has_value()) {
nonce = Origin::Nonce(nonce_token.value());
}
Origin origin;
origin.nonce_ = std::move(nonce);
origin.tuple_ = tuple;
return origin;
}
void Origin::WriteIntoTrace(perfetto::TracedValue context) const {
std::move(context).WriteString(GetDebugString());
}
size_t Origin::EstimateMemoryUsage() const {
return base::trace_event::EstimateMemoryUsage(tuple_);
}
std::ostream& operator<<(std::ostream& out, const url::Origin& origin) {
out << origin.GetDebugString();
return out;
}
std::ostream& operator<<(std::ostream& out, const url::Origin::Nonce& nonce) {
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() = default;
Origin::Nonce::Nonce(const base::UnguessableToken& token) : token_(token) {
CHECK(!token_.is_empty());
}
const base::UnguessableToken& Origin::Nonce::token() const {
if (token_.is_empty())
token_ = base::UnguessableToken::Create();
return token_;
}
const base::UnguessableToken& Origin::Nonce::raw_token() const {
return token_;
}
Origin::Nonce::Nonce(const Origin::Nonce& other) : token_(other.token()) {}
Origin::Nonce& Origin::Nonce::operator=(const Origin::Nonce& other) {
token_ = other.token();
return *this;
}
Origin::Nonce::Nonce(Origin::Nonce&& other) noexcept : token_(other.token_) {
other.token_ = base::UnguessableToken();
}
Origin::Nonce& Origin::Nonce::operator=(Origin::Nonce&& other) noexcept {
token_ = other.token_;
other.token_ = base::UnguessableToken();
return *this;
}
std::strong_ordering Origin::Nonce::operator<=>(
const Origin::Nonce& other) const {
return token() <=> other.token();
}
bool Origin::Nonce::operator==(const Origin::Nonce& other) const {
return (other.token_ == token_) && !(token_.is_empty() && (&other != this));
}
}