Commit 048bee10 authored by csharrison's avatar csharrison Committed by Commit bot

Add url::Origin::GetURL() to convert Origins to URLs without reparsing

The previous canonical way to convert an Origin to a GURL was
to call GURL(origin.Serialize()). This is expensive and often in the
critical path of user perceived page loads. One such caller has
been converted to the new method, and the rest will be converted
in a followup patch.

BUG=651554

Review-Url: https://codereview.chromium.org/2378323003
Cr-Commit-Position: refs/heads/master@{#422613}
parent 5d1bf66e
......@@ -4096,7 +4096,7 @@ void RenderFrameImpl::willSendRequest(blink::WebLocalFrame* frame,
extra_data->set_render_frame_id(routing_id_);
extra_data->set_is_main_frame(!parent);
extra_data->set_frame_origin(
blink::WebStringToGURL(frame->document().getSecurityOrigin().toString()));
url::Origin(frame->document().getSecurityOrigin()).GetURL());
extra_data->set_parent_is_main_frame(parent && !parent->parent());
extra_data->set_parent_render_frame_id(parent_routing_id);
extra_data->set_allow_download(
......
......@@ -65,6 +65,16 @@ std::string Origin::Serialize() const {
return tuple_.Serialize();
}
GURL Origin::GetURL() const {
if (unique())
return GURL();
if (scheme() == kFileScheme)
return GURL("file:///");
return tuple_.GetURL();
}
bool Origin::IsSameOriginWith(const Origin& other) const {
if (unique_ || other.unique_)
return false;
......
......@@ -122,6 +122,14 @@ class URL_EXPORT Origin {
return IsSameOriginWith(other);
}
// 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.
// Note: The returned URL will not necessarily be serialized to the same value
// as the Origin would. The GURL will have an added "/" path for Origins with
// valid SchemeHostPorts and file Origins.
GURL GetURL() const;
// Same as GURL::DomainIs. If |this| origin is unique, then returns false.
bool DomainIs(base::StringPiece lower_ascii_domain) const;
......
......@@ -13,6 +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);
}
TEST(OriginTest, UniqueOriginComparison) {
url::Origin unique_origin;
EXPECT_EQ("", unique_origin.scheme());
......@@ -38,6 +58,8 @@ TEST(OriginTest, UniqueOriginComparison) {
EXPECT_FALSE(origin.IsSameOriginWith(origin));
EXPECT_FALSE(unique_origin.IsSameOriginWith(origin));
EXPECT_FALSE(origin.IsSameOriginWith(unique_origin));
ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
}
}
......@@ -103,6 +125,8 @@ TEST(OriginTest, ConstructFromGURL) {
EXPECT_TRUE(origin.IsSameOriginWith(origin));
EXPECT_FALSE(different_origin.IsSameOriginWith(origin));
EXPECT_FALSE(origin.IsSameOriginWith(different_origin));
ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
}
}
......@@ -127,7 +151,10 @@ TEST(OriginTest, Serialization) {
GURL url(test_case.url);
EXPECT_TRUE(url.is_valid());
url::Origin origin(url);
EXPECT_EQ(test_case.expected, origin.Serialize());
std::string serialized = origin.Serialize();
ExpectParsedUrlsEqual(GURL(serialized), origin.GetURL());
EXPECT_EQ(test_case.expected, serialized);
// The '<<' operator should produce the same serialization as Serialize().
std::stringstream out;
......@@ -186,6 +213,8 @@ TEST(OriginTest, UnsafelyCreate) {
EXPECT_EQ(test.port, origin.port());
EXPECT_FALSE(origin.unique());
EXPECT_TRUE(origin.IsSameOriginWith(origin));
ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
}
}
......@@ -221,6 +250,8 @@ TEST(OriginTest, UnsafelyCreateUniqueOnInvalidInput) {
EXPECT_EQ(0, origin.port());
EXPECT_TRUE(origin.unique());
EXPECT_FALSE(origin.IsSameOriginWith(origin));
ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
}
}
......@@ -249,6 +280,8 @@ TEST(OriginTest, UnsafelyCreateUniqueViaEmbeddedNulls) {
EXPECT_EQ(0, origin.port());
EXPECT_TRUE(origin.unique());
EXPECT_FALSE(origin.IsSameOriginWith(origin));
ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
}
}
......
......@@ -13,6 +13,7 @@
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "url/gurl.h"
#include "url/third_party/mozilla/url_parse.h"
#include "url/url_canon.h"
#include "url/url_canon_stdstring.h"
#include "url/url_constants.h"
......@@ -142,12 +143,46 @@ bool SchemeHostPort::IsInvalid() const {
}
std::string SchemeHostPort::Serialize() const {
// Null checking for |parsed| in SerializeInternal is probably slower than
// just filling it in and discarding it here.
url::Parsed parsed;
return SerializeInternal(&parsed);
}
GURL SchemeHostPort::GetURL() const {
url::Parsed parsed;
std::string serialized = SerializeInternal(&parsed);
// 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.
DCHECK(!parsed.path.is_valid());
parsed.path = Component(serialized.length(), 1);
serialized.append("/");
return GURL(std::move(serialized), parsed, true);
}
bool SchemeHostPort::Equals(const SchemeHostPort& other) const {
return port_ == other.port() && scheme_ == other.scheme() &&
host_ == other.host();
}
bool SchemeHostPort::operator<(const SchemeHostPort& other) const {
return std::tie(port_, scheme_, host_) <
std::tie(other.port_, other.scheme_, other.host_);
}
std::string SchemeHostPort::SerializeInternal(url::Parsed* parsed) const {
std::string result;
if (IsInvalid())
return result;
parsed->scheme = Component(0, scheme_.length());
result.append(scheme_);
result.append(kStandardSchemeSeparator);
parsed->host = Component(result.length(), host_.length());
result.append(host_);
if (port_ == 0)
......@@ -161,20 +196,12 @@ std::string SchemeHostPort::Serialize() const {
return result;
if (port_ != default_port) {
result.push_back(':');
result.append(base::UintToString(port_));
std::string port(base::UintToString(port_));
parsed->port = Component(result.length(), port.length());
result.append(std::move(port));
}
return result;
}
bool SchemeHostPort::Equals(const SchemeHostPort& other) const {
return port_ == other.port() && scheme_ == other.scheme() &&
host_ == other.host();
}
bool SchemeHostPort::operator<(const SchemeHostPort& other) const {
return std::tie(port_, scheme_, host_) <
std::tie(other.port_, other.scheme_, other.host_);
}
} // namespace url
......@@ -16,6 +16,8 @@ class GURL;
namespace url {
struct Parsed;
// This class represents a (scheme, host, port) tuple extracted from a URL.
//
// The primary purpose of this class is to represent relevant network-authority
......@@ -111,6 +113,10 @@ class URL_EXPORT SchemeHostPort {
// serialized as a unique Origin.
std::string Serialize() const;
// Efficiently returns what GURL(Serialize()) would return, without needing to
// re-parse the URL.
GURL GetURL() const;
// Two SchemeHostPort objects are "equal" iff their schemes, hosts, and ports
// are exact matches.
//
......@@ -124,6 +130,8 @@ class URL_EXPORT SchemeHostPort {
bool operator<(const SchemeHostPort& other) const;
private:
std::string SerializeInternal(url::Parsed* parsed) const;
std::string scheme_;
std::string host_;
uint16_t port_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment