summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormkwst <mkwst@chromium.org>2015-02-22 21:10:31 -0800
committerCommit bot <commit-bot@chromium.org>2015-02-23 05:11:25 +0000
commitae819bb3096b63a11b8c1ff47dd3b69f85ea241b (patch)
treeec97dfaf204412c66e4246c597fe00cbdf64083e
parentd726d218c92888278509ef9b4a9e639cf9fce659 (diff)
downloadchromium_src-ae819bb3096b63a11b8c1ff47dd3b69f85ea241b.zip
chromium_src-ae819bb3096b63a11b8c1ff47dd3b69f85ea241b.tar.gz
chromium_src-ae819bb3096b63a11b8c1ff47dd3b69f85ea241b.tar.bz2
Implement the "First-Party-Only" cookie attribute.
First-party-only cookies allow servers to mitigate the risk of cross-site request forgery and related information leakage attacks by asserting that a particular cookie should only be sent in a "first-party" context. This patch adds support for the 'First-Party-Only' attribute to the CookieMonster and CookieStore, but does not yet wire up requests such that the flag has any effect. https://codereview.chromium.org/940373002 will do so by correctly setting the first-party URL on the CookieOptions object used to load cookies for a request. Spec: https://tools.ietf.org/html/draft-west-first-party-cookies Intent to Implement: https://groups.google.com/a/chromium.org/d/msg/blink-dev/vT98riFhhT0/3Q-lADqsh0UJ BUG=459154 TBR=dpolukhin@chromium.org Review URL: https://codereview.chromium.org/876973003 Cr-Commit-Position: refs/heads/master@{#317544}
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/cookies/CanonicalCookie.java24
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/cookies/CookiesFetcher.java30
-rw-r--r--chrome/browser/android/cookies/cookies_fetcher.cc43
-rw-r--r--chrome/browser/android/cookies/cookies_fetcher.h1
-rw-r--r--chrome/browser/chromeos/login/profile_auth_data_unittest.cc30
-rw-r--r--chrome/browser/extensions/api/cookies/cookies_api.cc4
-rw-r--r--chrome/browser/extensions/api/cookies/cookies_unittest.cc51
-rw-r--r--content/browser/net/sqlite_persistent_cookie_store.cc101
-rw-r--r--content/browser/net/sqlite_persistent_cookie_store_perftest.cc8
-rw-r--r--content/browser/net/sqlite_persistent_cookie_store_unittest.cc82
-rw-r--r--net/cookies/canonical_cookie.cc43
-rw-r--r--net/cookies/canonical_cookie.h4
-rw-r--r--net/cookies/canonical_cookie_unittest.cc118
-rw-r--r--net/cookies/cookie_monster.cc17
-rw-r--r--net/cookies/cookie_monster.h2
-rw-r--r--net/cookies/cookie_monster_store_test.cc6
-rw-r--r--net/cookies/cookie_monster_unittest.cc111
-rw-r--r--net/cookies/cookie_options.h27
-rw-r--r--net/cookies/parsed_cookie.cc13
-rw-r--r--net/cookies/parsed_cookie.h3
-rw-r--r--net/cookies/parsed_cookie_unittest.cc22
-rw-r--r--net/url_request/url_request_http_job.cc6
-rw-r--r--tools/metrics/histograms/histograms.xml14
23 files changed, 461 insertions, 299 deletions
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/cookies/CanonicalCookie.java b/chrome/android/java/src/org/chromium/chrome/browser/cookies/CanonicalCookie.java
index 951a636..8657e51 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/cookies/CanonicalCookie.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/cookies/CanonicalCookie.java
@@ -22,20 +22,13 @@ class CanonicalCookie {
private final long mLastAccess;
private final boolean mSecure;
private final boolean mHttpOnly;
+ private final boolean mFirstPartyOnly;
private final int mPriority;
/** Constructs a CanonicalCookie */
- CanonicalCookie(String url,
- String name,
- String value,
- String domain,
- String path,
- long creation,
- long expiration,
- long lastAccess,
- boolean secure,
- boolean httpOnly,
- int priority) {
+ CanonicalCookie(String url, String name, String value, String domain, String path,
+ long creation, long expiration, long lastAccess, boolean secure, boolean httpOnly,
+ boolean firstPartyOnly, int priority) {
mUrl = url;
mName = name;
mValue = value;
@@ -46,6 +39,7 @@ class CanonicalCookie {
mLastAccess = lastAccess;
mSecure = secure;
mHttpOnly = httpOnly;
+ mFirstPartyOnly = firstPartyOnly;
mPriority = priority;
}
@@ -59,6 +53,11 @@ class CanonicalCookie {
return mHttpOnly;
}
+ /** @return True if the cookie is First-Party only. */
+ boolean isFirstPartyOnly() {
+ return mFirstPartyOnly;
+ }
+
/** @return True if the cookie is secure. */
boolean isSecure() {
return mSecure;
@@ -121,6 +120,7 @@ class CanonicalCookie {
out.writeLong(mLastAccess);
out.writeBoolean(mSecure);
out.writeBoolean(mHttpOnly);
+ out.writeBoolean(mFirstPartyOnly);
out.writeInt(mPriority);
}
@@ -134,6 +134,6 @@ class CanonicalCookie {
throws IOException {
return new CanonicalCookie(in.readUTF(), in.readUTF(), in.readUTF(), in.readUTF(),
in.readUTF(), in.readLong(), in.readLong(), in.readLong(), in.readBoolean(),
- in.readBoolean(), in.readInt());
+ in.readBoolean(), in.readBoolean(), in.readInt());
}
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/cookies/CookiesFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/cookies/CookiesFetcher.java
index ef54545..bd6ceee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/cookies/CookiesFetcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/cookies/CookiesFetcher.java
@@ -162,19 +162,11 @@ public class CookiesFetcher {
protected void onPostExecute(List<CanonicalCookie> cookies) {
// We can only access cookies and profiles on the UI thread.
for (CanonicalCookie cookie : cookies) {
- nativeRestoreCookies(mNativeCookiesFetcher,
- cookie.getUrl(),
- cookie.getName(),
- cookie.getValue(),
- cookie.getDomain(),
- cookie.getPath(),
- cookie.getCreationDate(),
- cookie.getExpirationDate(),
- cookie.getLastAccessDate(),
- cookie.isSecure(),
- cookie.isHttpOnly(),
- cookie.getPriority()
- );
+ nativeRestoreCookies(mNativeCookiesFetcher, cookie.getUrl(), cookie.getName(),
+ cookie.getValue(), cookie.getDomain(), cookie.getPath(),
+ cookie.getCreationDate(), cookie.getExpirationDate(),
+ cookie.getLastAccessDate(), cookie.isSecure(), cookie.isHttpOnly(),
+ cookie.isFirstPartyOnly(), cookie.getPriority());
}
}
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
@@ -201,9 +193,9 @@ public class CookiesFetcher {
@CalledByNative
private CanonicalCookie createCookie(String url, String name, String value, String domain,
String path, long creation, long expiration, long lastAccess, boolean secure,
- boolean httpOnly, int priority) {
+ boolean httpOnly, boolean firstPartyOnly, int priority) {
return new CanonicalCookie(url, name, value, domain, path, creation, expiration, lastAccess,
- secure, httpOnly, priority);
+ secure, httpOnly, firstPartyOnly, priority);
}
@CalledByNative
@@ -292,8 +284,8 @@ public class CookiesFetcher {
private native long nativeInit();
private static native void nativeDestroy(long nativeCookiesFetcher);
private native void nativePersistCookies(long nativeCookiesFetcher);
- private native void nativeRestoreCookies(long nativeCookiesFetcher,
- String url, String name, String value, String domain, String path,
- long creation, long expiration, long lastAccess, boolean secure,
- boolean httpOnly, int priority);
+ private native void nativeRestoreCookies(long nativeCookiesFetcher, String url, String name,
+ String value, String domain, String path, long creation, long expiration,
+ long lastAccess, boolean secure, boolean httpOnly, boolean firstPartyOnly,
+ int priority);
}
diff --git a/chrome/browser/android/cookies/cookies_fetcher.cc b/chrome/browser/android/cookies/cookies_fetcher.cc
index 16cfbaf..25f55e7 100644
--- a/chrome/browser/android/cookies/cookies_fetcher.cc
+++ b/chrome/browser/android/cookies/cookies_fetcher.cc
@@ -76,20 +76,16 @@ void CookiesFetcher::OnCookiesFetchFinished(const net::CookieList& cookies) {
int index = 0;
for (net::CookieList::const_iterator i = cookies.begin();
i != cookies.end(); ++i) {
- ScopedJavaLocalRef<jobject> java_cookie =
- Java_CookiesFetcher_createCookie(
- env, jobject_.obj(),
- base::android::ConvertUTF8ToJavaString(env, i->Source()).obj(),
- base::android::ConvertUTF8ToJavaString(env, i->Name()).obj(),
- base::android::ConvertUTF8ToJavaString(env, i->Value()).obj(),
- base::android::ConvertUTF8ToJavaString(env, i->Domain()).obj(),
- base::android::ConvertUTF8ToJavaString(env, i->Path()).obj(),
- i->CreationDate().ToInternalValue(),
- i->ExpiryDate().ToInternalValue(),
- i->LastAccessDate().ToInternalValue(),
- i->IsSecure(),
- i->IsHttpOnly(),
- i->Priority());
+ ScopedJavaLocalRef<jobject> java_cookie = Java_CookiesFetcher_createCookie(
+ env, jobject_.obj(),
+ base::android::ConvertUTF8ToJavaString(env, i->Source()).obj(),
+ base::android::ConvertUTF8ToJavaString(env, i->Name()).obj(),
+ base::android::ConvertUTF8ToJavaString(env, i->Value()).obj(),
+ base::android::ConvertUTF8ToJavaString(env, i->Domain()).obj(),
+ base::android::ConvertUTF8ToJavaString(env, i->Path()).obj(),
+ i->CreationDate().ToInternalValue(), i->ExpiryDate().ToInternalValue(),
+ i->LastAccessDate().ToInternalValue(), i->IsSecure(), i->IsHttpOnly(),
+ i->IsFirstPartyOnly(), i->Priority());
env->SetObjectArrayElement(joa.obj(), index++, java_cookie.obj());
}
@@ -111,6 +107,7 @@ void CookiesFetcher::RestoreCookies(JNIEnv* env,
int64 last_access,
bool secure,
bool httponly,
+ bool firstpartyonly,
int priority) {
Profile* profile = ProfileManager::GetPrimaryUserProfile();
if (!profile->HasOffTheRecordProfile()) {
@@ -129,8 +126,8 @@ void CookiesFetcher::RestoreCookies(JNIEnv* env,
base::android::ConvertJavaStringToUTF8(env, path),
base::Time::FromInternalValue(creation),
base::Time::FromInternalValue(expiration),
- base::Time::FromInternalValue(last_access),
- secure, httponly, static_cast<net::CookiePriority>(priority));
+ base::Time::FromInternalValue(last_access), secure, httponly,
+ firstpartyonly, static_cast<net::CookiePriority>(priority));
// The rest must be done from the IO thread.
content::BrowserThread::PostTask(
@@ -158,17 +155,9 @@ void CookiesFetcher::RestoreToCookieJarInternal(
base::Callback<void(bool success)> cb;
monster->SetCookieWithDetailsAsync(
- GURL(cookie.Source()),
- cookie.Name(),
- cookie.Value(),
- cookie.Domain(),
- cookie.Path(),
- cookie.ExpiryDate(),
- cookie.IsSecure(),
- cookie.IsHttpOnly(),
- cookie.Priority(),
- cb
- );
+ GURL(cookie.Source()), cookie.Name(), cookie.Value(), cookie.Domain(),
+ cookie.Path(), cookie.ExpiryDate(), cookie.IsSecure(),
+ cookie.IsHttpOnly(), cookie.IsFirstPartyOnly(), cookie.Priority(), cb);
}
// JNI functions
diff --git a/chrome/browser/android/cookies/cookies_fetcher.h b/chrome/browser/android/cookies/cookies_fetcher.h
index e8462cc..2601670 100644
--- a/chrome/browser/android/cookies/cookies_fetcher.h
+++ b/chrome/browser/android/cookies/cookies_fetcher.h
@@ -47,6 +47,7 @@ class CookiesFetcher {
int64 last_access,
bool secure,
bool httponly,
+ bool firstpartyonly,
int priority);
private:
diff --git a/chrome/browser/chromeos/login/profile_auth_data_unittest.cc b/chrome/browser/chromeos/login/profile_auth_data_unittest.cc
index 6d7d88a..bf6bc33 100644
--- a/chrome/browser/chromeos/login/profile_auth_data_unittest.cc
+++ b/chrome/browser/chromeos/login/profile_auth_data_unittest.cc
@@ -223,28 +223,14 @@ void ProfileAuthDataTest::PopulateBrowserContext(
run_loop_->Run();
net::CookieList cookie_list;
- cookie_list.push_back(net::CanonicalCookie(GURL(kGAIACookieURL),
- kCookieName,
- cookie_value,
- kGAIACookieDomain,
- std::string(),
- base::Time(),
- base::Time(),
- base::Time(),
- true,
- false,
- net::COOKIE_PRIORITY_DEFAULT));
- cookie_list.push_back(net::CanonicalCookie(GURL(kSAMLIdPCookieURL),
- kCookieName,
- cookie_value,
- kSAMLIdPCookieDomain,
- std::string(),
- base::Time(),
- base::Time(),
- base::Time(),
- true,
- false,
- net::COOKIE_PRIORITY_DEFAULT));
+ cookie_list.push_back(net::CanonicalCookie(
+ GURL(kGAIACookieURL), kCookieName, cookie_value, kGAIACookieDomain,
+ std::string(), base::Time(), base::Time(), base::Time(), true, false,
+ false, net::COOKIE_PRIORITY_DEFAULT));
+ cookie_list.push_back(net::CanonicalCookie(
+ GURL(kSAMLIdPCookieURL), kCookieName, cookie_value, kSAMLIdPCookieDomain,
+ std::string(), base::Time(), base::Time(), base::Time(), true, false,
+ false, net::COOKIE_PRIORITY_DEFAULT));
cookies->ImportCookies(cookie_list);
GetChannelIDs(browser_context)->SetChannelID(kChannelIDServerIdentifier,
diff --git a/chrome/browser/extensions/api/cookies/cookies_api.cc b/chrome/browser/extensions/api/cookies/cookies_api.cc
index d059071..d3bc6e7 100644
--- a/chrome/browser/extensions/api/cookies/cookies_api.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_api.cc
@@ -399,6 +399,10 @@ void CookiesSetFunction::SetCookieOnIOThread() {
: false,
parsed_args_->details.http_only.get() ? *parsed_args_->details.http_only
: false,
+ // TODO(mkwst): If we decide to ship First-party-only cookies, we'll need
+ // to extend the extension API to support them. For the moment, we'll set
+ // all cookies as non-First-party-only.
+ false,
net::COOKIE_PRIORITY_DEFAULT,
base::Bind(&CookiesSetFunction::PullCookie, this));
}
diff --git a/chrome/browser/extensions/api/cookies/cookies_unittest.cc b/chrome/browser/extensions/api/cookies/cookies_unittest.cc
index e578df2..cbb4cc2 100644
--- a/chrome/browser/extensions/api/cookies/cookies_unittest.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_unittest.cc
@@ -81,9 +81,8 @@ TEST_F(ExtensionCookiesTest, StoreIdProfileConversion) {
TEST_F(ExtensionCookiesTest, ExtensionTypeCreation) {
net::CanonicalCookie canonical_cookie1(
- GURL(), "ABC", "DEF", "www.foobar.com", "/",
- base::Time(), base::Time(), base::Time(),
- false, false, net::COOKIE_PRIORITY_DEFAULT);
+ GURL(), "ABC", "DEF", "www.foobar.com", "/", base::Time(), base::Time(),
+ base::Time(), false, false, false, net::COOKIE_PRIORITY_DEFAULT);
scoped_ptr<Cookie> cookie1(
cookies_helpers::CreateCookie(
canonical_cookie1, "some cookie store"));
@@ -99,9 +98,9 @@ TEST_F(ExtensionCookiesTest, ExtensionTypeCreation) {
EXPECT_EQ("some cookie store", cookie1->store_id);
net::CanonicalCookie canonical_cookie2(
- GURL(), "ABC", "DEF", ".foobar.com", "/",
- base::Time(), base::Time::FromDoubleT(10000), base::Time(),
- false, false, net::COOKIE_PRIORITY_DEFAULT);
+ GURL(), "ABC", "DEF", ".foobar.com", "/", base::Time(),
+ base::Time::FromDoubleT(10000), base::Time(), false, false, false,
+ net::COOKIE_PRIORITY_DEFAULT);
scoped_ptr<Cookie> cookie2(
cookies_helpers::CreateCookie(
canonical_cookie2, "some cookie store"));
@@ -120,16 +119,16 @@ TEST_F(ExtensionCookiesTest, ExtensionTypeCreation) {
}
TEST_F(ExtensionCookiesTest, GetURLFromCanonicalCookie) {
- net::CanonicalCookie cookie1(
- GURL(), "ABC", "DEF", "www.foobar.com", "/", base::Time(), base::Time(),
- base::Time(), false, false, net::COOKIE_PRIORITY_DEFAULT);
+ net::CanonicalCookie cookie1(GURL(), "ABC", "DEF", "www.foobar.com", "/",
+ base::Time(), base::Time(), base::Time(), false,
+ false, false, net::COOKIE_PRIORITY_DEFAULT);
EXPECT_EQ("http://www.foobar.com/",
cookies_helpers::GetURLFromCanonicalCookie(
cookie1).spec());
- net::CanonicalCookie cookie2(
- GURL(), "ABC", "DEF", ".helloworld.com", "/", base::Time(), base::Time(),
- base::Time(), true, false, net::COOKIE_PRIORITY_DEFAULT);
+ net::CanonicalCookie cookie2(GURL(), "ABC", "DEF", ".helloworld.com", "/",
+ base::Time(), base::Time(), base::Time(), true,
+ false, false, net::COOKIE_PRIORITY_DEFAULT);
EXPECT_EQ("https://helloworld.com/",
cookies_helpers::GetURLFromCanonicalCookie(
cookie2).spec());
@@ -165,33 +164,19 @@ TEST_F(ExtensionCookiesTest, DomainMatching) {
scoped_ptr<GetAll::Params> params(GetAll::Params::Create(args));
cookies_helpers::MatchFilter filter(&params->details);
- net::CanonicalCookie cookie(GURL(),
- std::string(),
- std::string(),
- tests[i].domain,
- std::string(),
- base::Time(),
- base::Time(),
- base::Time(),
- false,
- false,
+ net::CanonicalCookie cookie(GURL(), std::string(), std::string(),
+ tests[i].domain, std::string(), base::Time(),
+ base::Time(), base::Time(), false, false, false,
net::COOKIE_PRIORITY_DEFAULT);
EXPECT_EQ(tests[i].matches, filter.MatchesCookie(cookie));
}
}
TEST_F(ExtensionCookiesTest, DecodeUTF8WithErrorHandling) {
- net::CanonicalCookie canonical_cookie(GURL(),
- std::string(),
- "011Q255bNX_1!yd\203e+",
- "test.com",
- "/path\203",
- base::Time(),
- base::Time(),
- base::Time(),
- false,
- false,
- net::COOKIE_PRIORITY_DEFAULT);
+ net::CanonicalCookie canonical_cookie(
+ GURL(), std::string(), "011Q255bNX_1!yd\203e+", "test.com", "/path\203",
+ base::Time(), base::Time(), base::Time(), false, false, false,
+ net::COOKIE_PRIORITY_DEFAULT);
scoped_ptr<Cookie> cookie(
cookies_helpers::CreateCookie(
canonical_cookie, "some cookie store"));
diff --git a/content/browser/net/sqlite_persistent_cookie_store.cc b/content/browser/net/sqlite_persistent_cookie_store.cc
index cb2f98e..75c89fd 100644
--- a/content/browser/net/sqlite_persistent_cookie_store.cc
+++ b/content/browser/net/sqlite_persistent_cookie_store.cc
@@ -310,6 +310,8 @@ namespace {
// Version number of the database.
//
+// Version 8 adds "first-party only" cookies.
+//
// Version 7 adds encrypted values. Old values will continue to be used but
// all new values written will be encrypted on selected operating systems. New
// records read by old clients will simply get an empty cookie value while old
@@ -333,7 +335,7 @@ namespace {
// Version 3 updated the database to include the last access time, so we can
// expire them in decreasing order of use when we've reached the maximum
// number of cookies.
-const int kCurrentVersionNumber = 7;
+const int kCurrentVersionNumber = 8;
const int kCompatibleVersionNumber = 5;
// Possible values for the 'priority' column.
@@ -399,19 +401,20 @@ bool InitTable(sql::Connection* db) {
if (!db->DoesTableExist("cookies")) {
std::string stmt(base::StringPrintf(
"CREATE TABLE cookies ("
- "creation_utc INTEGER NOT NULL UNIQUE PRIMARY KEY,"
- "host_key TEXT NOT NULL,"
- "name TEXT NOT NULL,"
- "value TEXT NOT NULL,"
- "path TEXT NOT NULL,"
- "expires_utc INTEGER NOT NULL,"
- "secure INTEGER NOT NULL,"
- "httponly INTEGER NOT NULL,"
- "last_access_utc INTEGER NOT NULL, "
- "has_expires INTEGER NOT NULL DEFAULT 1, "
- "persistent INTEGER NOT NULL DEFAULT 1,"
- "priority INTEGER NOT NULL DEFAULT %d,"
- "encrypted_value BLOB DEFAULT '')",
+ "creation_utc INTEGER NOT NULL UNIQUE PRIMARY KEY,"
+ "host_key TEXT NOT NULL,"
+ "name TEXT NOT NULL,"
+ "value TEXT NOT NULL,"
+ "path TEXT NOT NULL,"
+ "expires_utc INTEGER NOT NULL,"
+ "secure INTEGER NOT NULL,"
+ "httponly INTEGER NOT NULL,"
+ "last_access_utc INTEGER NOT NULL, "
+ "has_expires INTEGER NOT NULL DEFAULT 1, "
+ "persistent INTEGER NOT NULL DEFAULT 1,"
+ "priority INTEGER NOT NULL DEFAULT %d,"
+ "encrypted_value BLOB DEFAULT '',"
+ "firstpartyonly INTEGER NOT NULL DEFAULT 0)",
CookiePriorityToDBCookiePriority(net::COOKIE_PRIORITY_DEFAULT)));
if (!db->Execute(stmt.c_str()))
return false;
@@ -723,14 +726,14 @@ bool SQLitePersistentCookieStore::Backend::LoadCookiesForDomains(
smt.Assign(db_->GetCachedStatement(
SQL_FROM_HERE,
"SELECT creation_utc, host_key, name, value, encrypted_value, path, "
- "expires_utc, secure, httponly, last_access_utc, has_expires, "
- "persistent, priority FROM cookies WHERE host_key = ?"));
+ "expires_utc, secure, httponly, firstpartyonly, last_access_utc, "
+ "has_expires, persistent, priority FROM cookies WHERE host_key = ?"));
} else {
smt.Assign(db_->GetCachedStatement(
SQL_FROM_HERE,
"SELECT creation_utc, host_key, name, value, encrypted_value, path, "
- "expires_utc, secure, httponly, last_access_utc, has_expires, "
- "persistent, priority FROM cookies WHERE host_key = ? "
+ "expires_utc, secure, httponly, firstpartyonly, last_access_utc, "
+ "has_expires, persistent, priority FROM cookies WHERE host_key = ? "
"AND persistent = 1"));
}
if (!smt.is_valid()) {
@@ -769,18 +772,19 @@ void SQLitePersistentCookieStore::Backend::MakeCookiesFromSQLStatement(
}
scoped_ptr<net::CanonicalCookie> cc(new net::CanonicalCookie(
// The "source" URL is not used with persisted cookies.
- GURL(), // Source
- smt.ColumnString(2), // name
- value, // value
- smt.ColumnString(1), // domain
- smt.ColumnString(5), // path
- Time::FromInternalValue(smt.ColumnInt64(0)), // creation_utc
- Time::FromInternalValue(smt.ColumnInt64(6)), // expires_utc
- Time::FromInternalValue(smt.ColumnInt64(9)), // last_access_utc
- smt.ColumnInt(7) != 0, // secure
- smt.ColumnInt(8) != 0, // httponly
+ GURL(), // Source
+ smt.ColumnString(2), // name
+ value, // value
+ smt.ColumnString(1), // domain
+ smt.ColumnString(5), // path
+ Time::FromInternalValue(smt.ColumnInt64(0)), // creation_utc
+ Time::FromInternalValue(smt.ColumnInt64(6)), // expires_utc
+ Time::FromInternalValue(smt.ColumnInt64(10)), // last_access_utc
+ smt.ColumnInt(7) != 0, // secure
+ smt.ColumnInt(8) != 0, // httponly
+ smt.ColumnInt(9) != 0, // firstpartyonly
DBCookiePriorityToCookiePriority(
- static_cast<DBCookiePriority>(smt.ColumnInt(12))))); // priority
+ static_cast<DBCookiePriority>(smt.ColumnInt(13))))); // priority
DLOG_IF(WARNING, cc->CreationDate() > Time::Now())
<< L"CreationDate too recent";
cookies_per_origin_[CookieOrigin(cc->Domain(), cc->IsSecure())]++;
@@ -916,6 +920,27 @@ bool SQLitePersistentCookieStore::Backend::EnsureDatabaseVersion() {
base::TimeTicks::Now() - start_time);
}
+ if (cur_version == 7) {
+ const base::TimeTicks start_time = base::TimeTicks::Now();
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin())
+ return false;
+ // Alter the table to add a 'firstpartyonly' column.
+ if (!db_->Execute(
+ "ALTER TABLE cookies "
+ "ADD COLUMN firstpartyonly INTEGER DEFAULT 0")) {
+ LOG(WARNING) << "Unable to update cookie database to version 8.";
+ return false;
+ }
+ ++cur_version;
+ meta_table_.SetVersionNumber(cur_version);
+ meta_table_.SetCompatibleVersionNumber(
+ std::min(cur_version, kCompatibleVersionNumber));
+ transaction.Commit();
+ UMA_HISTOGRAM_TIMES("Cookie.TimeDatabaseMigrationToV8",
+ base::TimeTicks::Now() - start_time);
+ }
+
// Put future migration cases here.
if (cur_version < kCurrentVersionNumber) {
@@ -999,11 +1024,12 @@ void SQLitePersistentCookieStore::Backend::Commit() {
if (!db_.get() || ops.empty())
return;
- sql::Statement add_smt(db_->GetCachedStatement(SQL_FROM_HERE,
+ sql::Statement add_smt(db_->GetCachedStatement(
+ SQL_FROM_HERE,
"INSERT INTO cookies (creation_utc, host_key, name, value, "
- "encrypted_value, path, expires_utc, secure, httponly, last_access_utc, "
- "has_expires, persistent, priority) "
- "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)"));
+ "encrypted_value, path, expires_utc, secure, httponly, firstpartyonly, "
+ "last_access_utc, has_expires, persistent, priority) "
+ "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
if (!add_smt.is_valid())
return;
@@ -1048,11 +1074,12 @@ void SQLitePersistentCookieStore::Backend::Commit() {
add_smt.BindInt64(6, po->cc().ExpiryDate().ToInternalValue());
add_smt.BindInt(7, po->cc().IsSecure());
add_smt.BindInt(8, po->cc().IsHttpOnly());
- add_smt.BindInt64(9, po->cc().LastAccessDate().ToInternalValue());
- add_smt.BindInt(10, po->cc().IsPersistent());
+ add_smt.BindInt(9, po->cc().IsFirstPartyOnly());
+ add_smt.BindInt64(10, po->cc().LastAccessDate().ToInternalValue());
add_smt.BindInt(11, po->cc().IsPersistent());
- add_smt.BindInt(
- 12, CookiePriorityToDBCookiePriority(po->cc().Priority()));
+ add_smt.BindInt(12, po->cc().IsPersistent());
+ add_smt.BindInt(13,
+ CookiePriorityToDBCookiePriority(po->cc().Priority()));
if (!add_smt.Run())
NOTREACHED() << "Could not add a cookie to the DB.";
break;
diff --git a/content/browser/net/sqlite_persistent_cookie_store_perftest.cc b/content/browser/net/sqlite_persistent_cookie_store_perftest.cc
index 21f3c85..2a708b42 100644
--- a/content/browser/net/sqlite_persistent_cookie_store_perftest.cc
+++ b/content/browser/net/sqlite_persistent_cookie_store_perftest.cc
@@ -78,11 +78,9 @@ class SQLitePersistentCookieStorePerfTest : public testing::Test {
GURL gurl("www" + domain_name);
for (int cookie_num = 0; cookie_num < 50; ++cookie_num) {
t += base::TimeDelta::FromInternalValue(10);
- store_->AddCookie(
- net::CanonicalCookie(gurl,
- base::StringPrintf("Cookie_%d", cookie_num), "1",
- domain_name, "/", t, t, t, false, false,
- net::COOKIE_PRIORITY_DEFAULT));
+ store_->AddCookie(net::CanonicalCookie(
+ gurl, base::StringPrintf("Cookie_%d", cookie_num), "1", domain_name,
+ "/", t, t, t, false, false, false, net::COOKIE_PRIORITY_DEFAULT));
}
}
// Replace the store effectively destroying the current one and forcing it
diff --git a/content/browser/net/sqlite_persistent_cookie_store_unittest.cc b/content/browser/net/sqlite_persistent_cookie_store_unittest.cc
index 47590d7..ab177a9 100644
--- a/content/browser/net/sqlite_persistent_cookie_store_unittest.cc
+++ b/content/browser/net/sqlite_persistent_cookie_store_unittest.cc
@@ -159,10 +159,9 @@ class SQLitePersistentCookieStoreTest : public testing::Test {
const std::string& domain,
const std::string& path,
const base::Time& creation) {
- store_->AddCookie(
- net::CanonicalCookie(GURL(), name, value, domain, path, creation,
- creation, creation, false, false,
- net::COOKIE_PRIORITY_DEFAULT));
+ store_->AddCookie(net::CanonicalCookie(
+ GURL(), name, value, domain, path, creation, creation, creation, false,
+ false, false, net::COOKIE_PRIORITY_DEFAULT));
}
std::string ReadRawDBContents() {
@@ -361,11 +360,10 @@ TEST_F(SQLitePersistentCookieStoreTest, TestLoadOldSessionCookies) {
InitializeStore(false, true);
// Add a session cookie.
- store_->AddCookie(
- net::CanonicalCookie(
- GURL(), "C", "D", "sessioncookie.com", "/", base::Time::Now(),
- base::Time(), base::Time::Now(), false, false,
- net::COOKIE_PRIORITY_DEFAULT));
+ store_->AddCookie(net::CanonicalCookie(GURL(), "C", "D", "sessioncookie.com",
+ "/", base::Time::Now(), base::Time(),
+ base::Time::Now(), false, false, false,
+ net::COOKIE_PRIORITY_DEFAULT));
// Force the store to write its data to the disk.
DestroyStore();
@@ -389,11 +387,10 @@ TEST_F(SQLitePersistentCookieStoreTest, TestDontLoadOldSessionCookies) {
InitializeStore(false, true);
// Add a session cookie.
- store_->AddCookie(
- net::CanonicalCookie(
- GURL(), "C", "D", "sessioncookie.com", "/", base::Time::Now(),
- base::Time(), base::Time::Now(), false, false,
- net::COOKIE_PRIORITY_DEFAULT));
+ store_->AddCookie(net::CanonicalCookie(GURL(), "C", "D", "sessioncookie.com",
+ "/", base::Time::Now(), base::Time(),
+ base::Time::Now(), false, false, false,
+ net::COOKIE_PRIORITY_DEFAULT));
// Force the store to write its data to the disk.
DestroyStore();
@@ -420,19 +417,16 @@ TEST_F(SQLitePersistentCookieStoreTest, PersistIsPersistent) {
static const char kPersistentName[] = "persistent";
// Add a session cookie.
- store_->AddCookie(
- net::CanonicalCookie(
- GURL(), kSessionName, "val", "sessioncookie.com", "/",
- base::Time::Now(), base::Time(), base::Time::Now(), false, false,
- net::COOKIE_PRIORITY_DEFAULT));
+ store_->AddCookie(net::CanonicalCookie(
+ GURL(), kSessionName, "val", "sessioncookie.com", "/", base::Time::Now(),
+ base::Time(), base::Time::Now(), false, false, false,
+ net::COOKIE_PRIORITY_DEFAULT));
// Add a persistent cookie.
- store_->AddCookie(
- net::CanonicalCookie(
- GURL(), kPersistentName, "val", "sessioncookie.com", "/",
- base::Time::Now() - base::TimeDelta::FromDays(1),
- base::Time::Now() + base::TimeDelta::FromDays(1),
- base::Time::Now(), false, false,
- net::COOKIE_PRIORITY_DEFAULT));
+ store_->AddCookie(net::CanonicalCookie(
+ GURL(), kPersistentName, "val", "sessioncookie.com", "/",
+ base::Time::Now() - base::TimeDelta::FromDays(1),
+ base::Time::Now() + base::TimeDelta::FromDays(1), base::Time::Now(),
+ false, false, false, net::COOKIE_PRIORITY_DEFAULT));
// Force the store to write its data to the disk.
DestroyStore();
@@ -473,31 +467,25 @@ TEST_F(SQLitePersistentCookieStoreTest, PriorityIsPersistent) {
InitializeStore(false, true);
// Add a low-priority persistent cookie.
- store_->AddCookie(
- net::CanonicalCookie(
- GURL(), kLowName, kCookieValue, kCookieDomain, kCookiePath,
- base::Time::Now() - base::TimeDelta::FromMinutes(1),
- base::Time::Now() + base::TimeDelta::FromDays(1),
- base::Time::Now(), false, false,
- net::COOKIE_PRIORITY_LOW));
+ store_->AddCookie(net::CanonicalCookie(
+ GURL(), kLowName, kCookieValue, kCookieDomain, kCookiePath,
+ base::Time::Now() - base::TimeDelta::FromMinutes(1),
+ base::Time::Now() + base::TimeDelta::FromDays(1), base::Time::Now(),
+ false, false, false, net::COOKIE_PRIORITY_LOW));
// Add a medium-priority persistent cookie.
- store_->AddCookie(
- net::CanonicalCookie(
- GURL(), kMediumName, kCookieValue, kCookieDomain, kCookiePath,
- base::Time::Now() - base::TimeDelta::FromMinutes(2),
- base::Time::Now() + base::TimeDelta::FromDays(1),
- base::Time::Now(), false, false,
- net::COOKIE_PRIORITY_MEDIUM));
+ store_->AddCookie(net::CanonicalCookie(
+ GURL(), kMediumName, kCookieValue, kCookieDomain, kCookiePath,
+ base::Time::Now() - base::TimeDelta::FromMinutes(2),
+ base::Time::Now() + base::TimeDelta::FromDays(1), base::Time::Now(),
+ false, false, false, net::COOKIE_PRIORITY_MEDIUM));
// Add a high-priority peristent cookie.
- store_->AddCookie(
- net::CanonicalCookie(
- GURL(), kHighName, kCookieValue, kCookieDomain, kCookiePath,
- base::Time::Now() - base::TimeDelta::FromMinutes(3),
- base::Time::Now() + base::TimeDelta::FromDays(1),
- base::Time::Now(), false, false,
- net::COOKIE_PRIORITY_HIGH));
+ store_->AddCookie(net::CanonicalCookie(
+ GURL(), kHighName, kCookieValue, kCookieDomain, kCookiePath,
+ base::Time::Now() - base::TimeDelta::FromMinutes(3),
+ base::Time::Now() + base::TimeDelta::FromDays(1), base::Time::Now(),
+ false, false, false, net::COOKIE_PRIORITY_HIGH));
// Force the store to write its data to the disk.
DestroyStore();
diff --git a/net/cookies/canonical_cookie.cc b/net/cookies/canonical_cookie.cc
index f0b3b3f..9d7ccf2 100644
--- a/net/cookies/canonical_cookie.cc
+++ b/net/cookies/canonical_cookie.cc
@@ -109,12 +109,18 @@ CanonicalCookie::CanonicalCookie()
httponly_(false) {
}
-CanonicalCookie::CanonicalCookie(
- const GURL& url, const std::string& name, const std::string& value,
- const std::string& domain, const std::string& path,
- const base::Time& creation, const base::Time& expiration,
- const base::Time& last_access, bool secure, bool httponly,
- CookiePriority priority)
+CanonicalCookie::CanonicalCookie(const GURL& url,
+ const std::string& name,
+ const std::string& value,
+ const std::string& domain,
+ const std::string& path,
+ const base::Time& creation,
+ const base::Time& expiration,
+ const base::Time& last_access,
+ bool secure,
+ bool httponly,
+ bool firstpartyonly,
+ CookiePriority priority)
: source_(GetCookieSourceFromURL(url)),
name_(name),
value_(value),
@@ -125,6 +131,7 @@ CanonicalCookie::CanonicalCookie(
last_access_date_(last_access),
secure_(secure),
httponly_(httponly),
+ first_party_only_(firstpartyonly),
priority_(priority) {
}
@@ -137,6 +144,7 @@ CanonicalCookie::CanonicalCookie(const GURL& url, const ParsedCookie& pc)
last_access_date_(Time()),
secure_(pc.IsSecure()),
httponly_(pc.IsHttpOnly()),
+ first_party_only_(pc.IsFirstPartyOnly()),
priority_(pc.Priority()) {
if (pc.HasExpires())
expiry_date_ = CanonExpiration(pc, creation_date_, creation_date_);
@@ -238,12 +246,11 @@ CanonicalCookie* CanonicalCookie::Create(const GURL& url,
creation_time,
server_time);
- return new CanonicalCookie(url, parsed_cookie.Name(), parsed_cookie.Value(),
- cookie_domain, cookie_path, creation_time,
- cookie_expires, creation_time,
- parsed_cookie.IsSecure(),
- parsed_cookie.IsHttpOnly(),
- parsed_cookie.Priority());
+ return new CanonicalCookie(
+ url, parsed_cookie.Name(), parsed_cookie.Value(), cookie_domain,
+ cookie_path, creation_time, cookie_expires, creation_time,
+ parsed_cookie.IsSecure(), parsed_cookie.IsHttpOnly(),
+ parsed_cookie.IsFirstPartyOnly(), parsed_cookie.Priority());
}
CanonicalCookie* CanonicalCookie::Create(const GURL& url,
@@ -255,6 +262,7 @@ CanonicalCookie* CanonicalCookie::Create(const GURL& url,
const base::Time& expiration,
bool secure,
bool http_only,
+ bool first_party_only,
CookiePriority priority) {
// Expect valid attribute tokens and values, as defined by the ParsedCookie
// logic, otherwise don't create the cookie.
@@ -293,7 +301,7 @@ CanonicalCookie* CanonicalCookie::Create(const GURL& url,
return new CanonicalCookie(url, parsed_name, parsed_value, cookie_domain,
cookie_path, creation, expiration, creation,
- secure, http_only, priority);
+ secure, http_only, first_party_only, priority);
}
bool CanonicalCookie::IsOnPath(const std::string& url_path) const {
@@ -382,6 +390,14 @@ bool CanonicalCookie::IncludeForRequestURL(const GURL& url,
if (!IsOnPath(url.path()))
return false;
+ // Include first-party-only cookies iff |options| tells us to include all of
+ // them, or if a first-party URL is set and its origin matches the origin of
+ // |url|.
+ if (IsFirstPartyOnly() && !options.include_first_party_only() &&
+ options.first_party_url().GetOrigin() != url.GetOrigin()) {
+ return false;
+ }
+
return true;
}
@@ -406,6 +422,7 @@ CanonicalCookie* CanonicalCookie::Duplicate() const {
cc->last_access_date_ = last_access_date_;
cc->secure_ = secure_;
cc->httponly_ = httponly_;
+ cc->first_party_only_ = first_party_only_;
cc->priority_ = priority_;
return cc;
}
diff --git a/net/cookies/canonical_cookie.h b/net/cookies/canonical_cookie.h
index 19ed388..f54fb6d 100644
--- a/net/cookies/canonical_cookie.h
+++ b/net/cookies/canonical_cookie.h
@@ -37,6 +37,7 @@ class NET_EXPORT CanonicalCookie {
const base::Time& last_access,
bool secure,
bool httponly,
+ bool firstpartyonly,
CookiePriority priority);
// This constructor does canonicalization but not validation.
@@ -68,6 +69,7 @@ class NET_EXPORT CanonicalCookie {
const base::Time& expiration,
bool secure,
bool http_only,
+ bool first_party_only,
CookiePriority priority);
const std::string& Source() const { return source_; }
@@ -81,6 +83,7 @@ class NET_EXPORT CanonicalCookie {
const base::Time& ExpiryDate() const { return expiry_date_; }
bool IsSecure() const { return secure_; }
bool IsHttpOnly() const { return httponly_; }
+ bool IsFirstPartyOnly() const { return first_party_only_; }
CookiePriority Priority() const { return priority_; }
bool IsDomainCookie() const {
return !domain_.empty() && domain_[0] == '.'; }
@@ -158,6 +161,7 @@ class NET_EXPORT CanonicalCookie {
base::Time last_access_date_;
bool secure_;
bool httponly_;
+ bool first_party_only_;
CookiePriority priority_;
// NOTE: When any new members are added above this comment, the
// implementation of Duplicate() must be updated to copy the new member
diff --git a/net/cookies/canonical_cookie_unittest.cc b/net/cookies/canonical_cookie_unittest.cc
index 46d730b..78af2e0 100644
--- a/net/cookies/canonical_cookie_unittest.cc
+++ b/net/cookies/canonical_cookie_unittest.cc
@@ -39,23 +39,27 @@ TEST(CanonicalCookieTest, Constructor) {
CanonicalCookie cookie(url, "A", "2", "www.example.com", "/test",
current_time, base::Time(), current_time, false, false,
- COOKIE_PRIORITY_DEFAULT);
+ false, COOKIE_PRIORITY_DEFAULT);
EXPECT_EQ(url.GetOrigin().spec(), cookie.Source());
EXPECT_EQ("A", cookie.Name());
EXPECT_EQ("2", cookie.Value());
EXPECT_EQ("www.example.com", cookie.Domain());
EXPECT_EQ("/test", cookie.Path());
EXPECT_FALSE(cookie.IsSecure());
+ EXPECT_FALSE(cookie.IsHttpOnly());
+ EXPECT_FALSE(cookie.IsFirstPartyOnly());
CanonicalCookie cookie2(url, "A", "2", std::string(), std::string(),
current_time, base::Time(), current_time, false,
- false, COOKIE_PRIORITY_DEFAULT);
+ false, false, COOKIE_PRIORITY_DEFAULT);
EXPECT_EQ(url.GetOrigin().spec(), cookie.Source());
EXPECT_EQ("A", cookie2.Name());
EXPECT_EQ("2", cookie2.Value());
EXPECT_EQ("", cookie2.Domain());
EXPECT_EQ("", cookie2.Path());
EXPECT_FALSE(cookie2.IsSecure());
+ EXPECT_FALSE(cookie2.IsHttpOnly());
+ EXPECT_FALSE(cookie2.IsFirstPartyOnly());
}
TEST(CanonicalCookieTest, Create) {
@@ -99,27 +103,39 @@ TEST(CanonicalCookieTest, Create) {
httponly_options));
EXPECT_TRUE(cookie->IsHttpOnly());
+ // Test creating http only cookies.
+ CookieOptions first_party_options;
+ first_party_options.set_first_party_url(url);
+ cookie.reset(CanonicalCookie::Create(url, "A=2; First-Party-Only",
+ creation_time, httponly_options));
+ EXPECT_TRUE(cookie.get());
+ EXPECT_TRUE(cookie->IsFirstPartyOnly());
+
// Test the creating cookies using specific parameter instead of a cookie
// string.
- cookie.reset(CanonicalCookie::Create(url, "A", "2", "www.example.com",
- "/test", creation_time, base::Time(),
- false, false, COOKIE_PRIORITY_DEFAULT));
+ cookie.reset(CanonicalCookie::Create(
+ url, "A", "2", "www.example.com", "/test", creation_time, base::Time(),
+ false, false, false, COOKIE_PRIORITY_DEFAULT));
EXPECT_EQ(url.GetOrigin().spec(), cookie->Source());
EXPECT_EQ("A", cookie->Name());
EXPECT_EQ("2", cookie->Value());
EXPECT_EQ(".www.example.com", cookie->Domain());
EXPECT_EQ("/test", cookie->Path());
EXPECT_FALSE(cookie->IsSecure());
+ EXPECT_FALSE(cookie->IsHttpOnly());
+ EXPECT_FALSE(cookie->IsFirstPartyOnly());
- cookie.reset(CanonicalCookie::Create(url, "A", "2", ".www.example.com",
- "/test", creation_time, base::Time(),
- false, false, COOKIE_PRIORITY_DEFAULT));
+ cookie.reset(CanonicalCookie::Create(
+ url, "A", "2", ".www.example.com", "/test", creation_time, base::Time(),
+ false, false, false, COOKIE_PRIORITY_DEFAULT));
EXPECT_EQ(url.GetOrigin().spec(), cookie->Source());
EXPECT_EQ("A", cookie->Name());
EXPECT_EQ("2", cookie->Value());
EXPECT_EQ(".www.example.com", cookie->Domain());
EXPECT_EQ("/test", cookie->Path());
EXPECT_FALSE(cookie->IsSecure());
+ EXPECT_FALSE(cookie->IsHttpOnly());
+ EXPECT_FALSE(cookie->IsFirstPartyOnly());
}
TEST(CanonicalCookieTest, EmptyExpiry) {
@@ -166,18 +182,19 @@ TEST(CanonicalCookieTest, IsEquivalent) {
base::Time expiration_time = creation_time + base::TimeDelta::FromDays(2);
bool secure(false);
bool httponly(false);
+ bool firstparty(false);
// Test that a cookie is equivalent to itself.
scoped_ptr<CanonicalCookie> cookie(new CanonicalCookie(
url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time,
- expiration_time, last_access_time, secure, httponly,
+ expiration_time, last_access_time, secure, httponly, firstparty,
COOKIE_PRIORITY_MEDIUM));
EXPECT_TRUE(cookie->IsEquivalent(*cookie));
// Test that two identical cookies are equivalent.
scoped_ptr<CanonicalCookie> other_cookie(new CanonicalCookie(
url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time,
- expiration_time, last_access_time, secure, httponly,
+ expiration_time, last_access_time, secure, httponly, firstparty,
COOKIE_PRIORITY_MEDIUM));
EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
@@ -186,34 +203,47 @@ TEST(CanonicalCookieTest, IsEquivalent) {
other_cookie.reset(
new CanonicalCookie(url, cookie_name, "2", cookie_domain, cookie_path,
creation_time, expiration_time, last_access_time,
- secure, httponly, COOKIE_PRIORITY_HIGH));
+ secure, httponly, firstparty, COOKIE_PRIORITY_HIGH));
EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
base::Time other_creation_time =
creation_time + base::TimeDelta::FromMinutes(2);
other_cookie.reset(new CanonicalCookie(
url, cookie_name, "2", cookie_domain, cookie_path, other_creation_time,
- expiration_time, last_access_time, secure, httponly,
+ expiration_time, last_access_time, secure, httponly, firstparty,
COOKIE_PRIORITY_MEDIUM));
EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
other_cookie.reset(new CanonicalCookie(
url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time,
- expiration_time, last_access_time, true, httponly, COOKIE_PRIORITY_LOW));
+ expiration_time, last_access_time, true, httponly, firstparty,
+ COOKIE_PRIORITY_LOW));
+ EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
+
+ other_cookie.reset(new CanonicalCookie(
+ url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time,
+ expiration_time, last_access_time, secure, true, firstparty,
+ COOKIE_PRIORITY_LOW));
+ EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
+
+ other_cookie.reset(new CanonicalCookie(
+ url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time,
+ expiration_time, last_access_time, secure, httponly, true,
+ COOKIE_PRIORITY_LOW));
EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
// Tests that use different variations of attribute values that
// DO affect cookie equivalence.
- other_cookie.reset(
- new CanonicalCookie(url, "B", cookie_value, cookie_domain, cookie_path,
- creation_time, expiration_time, last_access_time,
- secure, httponly, COOKIE_PRIORITY_MEDIUM));
+ other_cookie.reset(new CanonicalCookie(
+ url, "B", cookie_value, cookie_domain, cookie_path, creation_time,
+ expiration_time, last_access_time, secure, httponly, firstparty,
+ COOKIE_PRIORITY_MEDIUM));
EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
other_cookie.reset(new CanonicalCookie(
url, cookie_name, cookie_value, "www.example.com", cookie_path,
creation_time, expiration_time, last_access_time, secure, httponly,
- COOKIE_PRIORITY_MEDIUM));
+ firstparty, COOKIE_PRIORITY_MEDIUM));
EXPECT_TRUE(cookie->IsDomainCookie());
EXPECT_FALSE(other_cookie->IsDomainCookie());
EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
@@ -221,12 +251,12 @@ TEST(CanonicalCookieTest, IsEquivalent) {
other_cookie.reset(new CanonicalCookie(
url, cookie_name, cookie_value, ".example.com", cookie_path,
creation_time, expiration_time, last_access_time, secure, httponly,
- COOKIE_PRIORITY_MEDIUM));
+ firstparty, COOKIE_PRIORITY_MEDIUM));
EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
other_cookie.reset(new CanonicalCookie(
url, cookie_name, cookie_value, cookie_domain, "/test/0", creation_time,
- expiration_time, last_access_time, secure, httponly,
+ expiration_time, last_access_time, secure, httponly, firstparty,
COOKIE_PRIORITY_MEDIUM));
EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
}
@@ -329,4 +359,52 @@ TEST(CanonicalCookieTest, IncludeForRequestURL) {
EXPECT_FALSE(cookie->IncludeForRequestURL(url, options));
}
+TEST(CanonicalCookieTest, IncludeFirstPartyForFirstPartyURL) {
+ GURL insecure_url("http://example.test");
+ GURL secure_url("https://example.test");
+ GURL secure_url_with_path("https://example.test/foo/bar/index.html");
+ GURL third_party_url("https://not-example.test");
+ base::Time creation_time = base::Time::Now();
+ CookieOptions options;
+ scoped_ptr<CanonicalCookie> cookie;
+
+ // First-party-only cookies are not inlcuded if a top-level URL is unset.
+ cookie.reset(CanonicalCookie::Create(secure_url, "A=2; First-Party-Only",
+ creation_time, options));
+ EXPECT_TRUE(cookie->IsFirstPartyOnly());
+ options.set_first_party_url(GURL());
+ EXPECT_FALSE(cookie->IncludeForRequestURL(secure_url, options));
+
+ // First-party-only cookies are included only if the cookie's origin matches
+ // the
+ // first-party origin.
+ options.set_first_party_url(secure_url);
+ EXPECT_TRUE(cookie->IncludeForRequestURL(secure_url, options));
+ options.set_first_party_url(insecure_url);
+ EXPECT_FALSE(cookie->IncludeForRequestURL(secure_url, options));
+ options.set_first_party_url(third_party_url);
+ EXPECT_FALSE(cookie->IncludeForRequestURL(secure_url, options));
+
+ // "First-Party-Only" doesn't override the 'secure' flag.
+ cookie.reset(CanonicalCookie::Create(
+ secure_url, "A=2; Secure; First-Party-Only", creation_time, options));
+ options.set_first_party_url(secure_url);
+ EXPECT_TRUE(cookie->IncludeForRequestURL(secure_url, options));
+ EXPECT_FALSE(cookie->IncludeForRequestURL(insecure_url, options));
+ options.set_first_party_url(insecure_url);
+ EXPECT_FALSE(cookie->IncludeForRequestURL(secure_url, options));
+ EXPECT_FALSE(cookie->IncludeForRequestURL(insecure_url, options));
+
+ // "First-Party-Only" doesn't override the 'path' flag.
+ cookie.reset(CanonicalCookie::Create(secure_url_with_path,
+ "A=2; First-Party-Only; path=/foo/bar",
+ creation_time, options));
+ options.set_first_party_url(secure_url_with_path);
+ EXPECT_TRUE(cookie->IncludeForRequestURL(secure_url_with_path, options));
+ EXPECT_FALSE(cookie->IncludeForRequestURL(secure_url, options));
+ options.set_first_party_url(secure_url);
+ EXPECT_TRUE(cookie->IncludeForRequestURL(secure_url_with_path, options));
+ EXPECT_FALSE(cookie->IncludeForRequestURL(secure_url, options));
+}
+
} // namespace net
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index bcf0fd0..63da860 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -414,6 +414,7 @@ class CookieMonster::SetCookieWithDetailsTask : public CookieMonsterTask {
const base::Time& expiration_time,
bool secure,
bool http_only,
+ bool first_party_only,
CookiePriority priority,
const SetCookiesCallback& callback)
: CookieMonsterTask(cookie_monster),
@@ -425,6 +426,7 @@ class CookieMonster::SetCookieWithDetailsTask : public CookieMonsterTask {
expiration_time_(expiration_time),
secure_(secure),
http_only_(http_only),
+ first_party_only_(first_party_only),
priority_(priority),
callback_(callback) {}
@@ -443,6 +445,7 @@ class CookieMonster::SetCookieWithDetailsTask : public CookieMonsterTask {
base::Time expiration_time_;
bool secure_;
bool http_only_;
+ bool first_party_only_;
CookiePriority priority_;
SetCookiesCallback callback_;
@@ -456,7 +459,7 @@ void CookieMonster::SetCookieWithDetailsTask::Run() {
"456373 CookieMonster::SetCookieWithDetailsTask::Run"));
bool success = this->cookie_monster()->SetCookieWithDetails(
url_, name_, value_, domain_, path_, expiration_time_, secure_,
- http_only_, priority_);
+ http_only_, first_party_only_, priority_);
if (!callback_.is_null()) {
this->InvokeCallback(base::Bind(&SetCookiesCallback::Run,
base::Unretained(&callback_), success));
@@ -912,11 +915,12 @@ void CookieMonster::SetCookieWithDetailsAsync(
const Time& expiration_time,
bool secure,
bool http_only,
+ bool first_party_only,
CookiePriority priority,
const SetCookiesCallback& callback) {
scoped_refptr<SetCookieWithDetailsTask> task = new SetCookieWithDetailsTask(
this, url, name, value, domain, path, expiration_time, secure, http_only,
- priority, callback);
+ first_party_only, priority, callback);
DoCookieTaskForURL(task, url);
}
@@ -941,6 +945,7 @@ void CookieMonster::GetAllCookiesForURLAsync(
const GetCookieListCallback& callback) {
CookieOptions options;
options.set_include_httponly();
+ options.set_include_first_party_only();
scoped_refptr<GetAllCookiesForURLWithOptionsTask> task =
new GetAllCookiesForURLWithOptionsTask(this, url, options, callback);
@@ -1093,6 +1098,7 @@ bool CookieMonster::SetCookieWithDetails(const GURL& url,
const base::Time& expiration_time,
bool secure,
bool http_only,
+ bool first_party_only,
CookiePriority priority) {
base::AutoLock autolock(lock_);
@@ -1105,13 +1111,14 @@ bool CookieMonster::SetCookieWithDetails(const GURL& url,
scoped_ptr<CanonicalCookie> cc;
cc.reset(CanonicalCookie::Create(url, name, value, domain, path,
creation_time, expiration_time, secure,
- http_only, priority));
+ http_only, first_party_only, priority));
if (!cc.get())
return false;
CookieOptions options;
options.set_include_httponly();
+ options.set_include_first_party_only();
return SetCanonicalCookie(&cc, creation_time, options);
}
@@ -1123,6 +1130,7 @@ bool CookieMonster::ImportCookies(const CookieList& list) {
scoped_ptr<CanonicalCookie> cookie(new CanonicalCookie(*iter));
net::CookieOptions options;
options.set_include_httponly();
+ options.set_include_first_party_only();
if (!SetCanonicalCookie(&cookie, cookie->CreationDate(), options))
return false;
}
@@ -1181,6 +1189,7 @@ CookieList CookieMonster::GetAllCookiesForURLWithOptions(
CookieList CookieMonster::GetAllCookiesForURL(const GURL& url) {
CookieOptions options;
options.set_include_httponly();
+ options.set_first_party_url(url);
return GetAllCookiesForURLWithOptions(url, options);
}
@@ -1347,6 +1356,7 @@ void CookieMonster::DeleteCookie(const GURL& url,
CookieOptions options;
options.set_include_httponly();
+ options.set_include_first_party_only();
// Get the cookies for this host and its domain(s).
std::vector<CanonicalCookie*> cookies;
FindCookiesForHostAndDomain(url, options, true, &cookies);
@@ -2321,6 +2331,7 @@ void CookieMonster::RunCallbacks(const CanonicalCookie& cookie, bool removed) {
lock_.AssertAcquired();
CookieOptions opts;
opts.set_include_httponly();
+ opts.set_include_first_party_only();
// Note that the callbacks in hook_map_ are wrapped with MakeAsync(), so they
// are guaranteed to not take long - they just post a RunAsync task back to
// the appropriate thread's message loop and return. It is important that this
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index 75c6397..f0b3fd4 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -168,6 +168,7 @@ class NET_EXPORT CookieMonster : public CookieStore {
const base::Time& expiration_time,
bool secure,
bool http_only,
+ bool first_party,
CookiePriority priority,
const SetCookiesCallback& callback);
@@ -421,6 +422,7 @@ class NET_EXPORT CookieMonster : public CookieStore {
const base::Time& expiration_time,
bool secure,
bool http_only,
+ bool first_party,
CookiePriority priority);
CookieList GetAllCookies();
diff --git a/net/cookies/cookie_monster_store_test.cc b/net/cookies/cookie_monster_store_test.cc
index dc371ed..68abc19 100644
--- a/net/cookies/cookie_monster_store_test.cc
+++ b/net/cookies/cookie_monster_store_test.cc
@@ -121,7 +121,8 @@ CanonicalCookie BuildCanonicalCookie(const std::string& key,
return CanonicalCookie(GURL(), pc.Name(), pc.Value(), key, cookie_path,
creation_time, cookie_expires, creation_time,
- pc.IsSecure(), pc.IsHttpOnly(), pc.Priority());
+ pc.IsSecure(), pc.IsHttpOnly(), pc.IsFirstPartyOnly(),
+ pc.Priority());
}
void AddCookieToList(const std::string& key,
@@ -214,7 +215,8 @@ CookieMonster* CreateMonsterFromStoreForGC(int num_cookies,
CanonicalCookie cc(GURL(), "a", "1", base::StringPrintf("h%05d.izzle", i),
"/path", creation_time, expiration_time,
- last_access_time, false, false, COOKIE_PRIORITY_DEFAULT);
+ last_access_time, false, false, false,
+ COOKIE_PRIORITY_DEFAULT);
store->AddCookie(cc);
}
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 51724a9..7ca99a3 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -152,13 +152,15 @@ class CookieMonsterTest : public CookieStoreTest<CookieMonsterTestTraits> {
const base::Time& expiration_time,
bool secure,
bool http_only,
+ bool first_party_only,
CookiePriority priority) {
DCHECK(cm);
ResultSavingCookieCallback<bool> callback;
cm->SetCookieWithDetailsAsync(
url, name, value, domain, path, expiration_time, secure, http_only,
- priority, base::Bind(&ResultSavingCookieCallback<bool>::Run,
- base::Unretained(&callback)));
+ first_party_only, priority,
+ base::Bind(&ResultSavingCookieCallback<bool>::Run,
+ base::Unretained(&callback)));
RunFor(kTimeout);
EXPECT_TRUE(callback.did_run());
return callback.result();
@@ -240,6 +242,7 @@ class CookieMonsterTest : public CookieStoreTest<CookieMonsterTestTraits> {
// * Three levels of domain cookie (.b.a, .c.b.a, .d.c.b.a)
// * Three levels of host cookie (w.b.a, w.c.b.a, w.d.c.b.a)
// * http_only cookie (w.c.b.a)
+ // * first-party cookie (w.c.b.a)
// * Two secure cookies (.c.b.a, w.c.b.a)
// * Two domain path cookies (.c.b.a/dir1, .c.b.a/dir1/dir2)
// * Two host path cookies (w.c.b.a/dir1, w.c.b.a/dir1/dir2)
@@ -247,64 +250,70 @@ class CookieMonsterTest : public CookieStoreTest<CookieMonsterTestTraits> {
// Domain cookies
EXPECT_TRUE(this->SetCookieWithDetails(
cm.get(), url_top_level_domain_plus_1, "dom_1", "X", ".harvard.edu",
- "/", base::Time(), false, false, COOKIE_PRIORITY_DEFAULT));
+ "/", base::Time(), false, false, false, COOKIE_PRIORITY_DEFAULT));
EXPECT_TRUE(this->SetCookieWithDetails(
cm.get(), url_top_level_domain_plus_2, "dom_2", "X",
- ".math.harvard.edu", "/", base::Time(), false, false,
+ ".math.harvard.edu", "/", base::Time(), false, false, false,
COOKIE_PRIORITY_DEFAULT));
EXPECT_TRUE(this->SetCookieWithDetails(
cm.get(), url_top_level_domain_plus_3, "dom_3", "X",
- ".bourbaki.math.harvard.edu", "/", base::Time(), false, false,
+ ".bourbaki.math.harvard.edu", "/", base::Time(), false, false, false,
COOKIE_PRIORITY_DEFAULT));
// Host cookies
EXPECT_TRUE(this->SetCookieWithDetails(
cm.get(), url_top_level_domain_plus_1, "host_1", "X", std::string(),
- "/", base::Time(), false, false, COOKIE_PRIORITY_DEFAULT));
+ "/", base::Time(), false, false, false, COOKIE_PRIORITY_DEFAULT));
EXPECT_TRUE(this->SetCookieWithDetails(
cm.get(), url_top_level_domain_plus_2, "host_2", "X", std::string(),
- "/", base::Time(), false, false, COOKIE_PRIORITY_DEFAULT));
+ "/", base::Time(), false, false, false, COOKIE_PRIORITY_DEFAULT));
EXPECT_TRUE(this->SetCookieWithDetails(
cm.get(), url_top_level_domain_plus_3, "host_3", "X", std::string(),
- "/", base::Time(), false, false, COOKIE_PRIORITY_DEFAULT));
+ "/", base::Time(), false, false, false, COOKIE_PRIORITY_DEFAULT));
+
+ // http_only cookie
+ EXPECT_TRUE(this->SetCookieWithDetails(
+ cm.get(), url_top_level_domain_plus_2, "httpo_check", "x",
+ std::string(), "/", base::Time(), false, true, false,
+ COOKIE_PRIORITY_DEFAULT));
- // Http_only cookie
+ // first-party cookie
EXPECT_TRUE(this->SetCookieWithDetails(
- cm.get(), url_top_level_domain_plus_2, "httpo_check", "X",
- std::string(), "/", base::Time(), false, true,
+ cm.get(), url_top_level_domain_plus_2, "firstp_check", "x",
+ std::string(), "/", base::Time(), false, false, true,
COOKIE_PRIORITY_DEFAULT));
// Secure cookies
EXPECT_TRUE(this->SetCookieWithDetails(
cm.get(), url_top_level_domain_plus_2_secure, "sec_dom", "X",
- ".math.harvard.edu", "/", base::Time(), true, false,
+ ".math.harvard.edu", "/", base::Time(), true, false, false,
COOKIE_PRIORITY_DEFAULT));
EXPECT_TRUE(this->SetCookieWithDetails(
cm.get(), url_top_level_domain_plus_2_secure, "sec_host", "X",
- std::string(), "/", base::Time(), true, false,
+ std::string(), "/", base::Time(), true, false, false,
COOKIE_PRIORITY_DEFAULT));
// Domain path cookies
EXPECT_TRUE(this->SetCookieWithDetails(
cm.get(), url_top_level_domain_plus_2, "dom_path_1", "X",
- ".math.harvard.edu", "/dir1", base::Time(), false, false,
+ ".math.harvard.edu", "/dir1", base::Time(), false, false, false,
COOKIE_PRIORITY_DEFAULT));
EXPECT_TRUE(this->SetCookieWithDetails(
cm.get(), url_top_level_domain_plus_2, "dom_path_2", "X",
- ".math.harvard.edu", "/dir1/dir2", base::Time(), false, false,
+ ".math.harvard.edu", "/dir1/dir2", base::Time(), false, false, false,
COOKIE_PRIORITY_DEFAULT));
// Host path cookies
EXPECT_TRUE(this->SetCookieWithDetails(
cm.get(), url_top_level_domain_plus_2, "host_path_1", "X",
- std::string(), "/dir1", base::Time(), false, false,
+ std::string(), "/dir1", base::Time(), false, false, false,
COOKIE_PRIORITY_DEFAULT));
EXPECT_TRUE(this->SetCookieWithDetails(
cm.get(), url_top_level_domain_plus_2, "host_path_2", "X",
- std::string(), "/dir1/dir2", base::Time(), false, false,
+ std::string(), "/dir1/dir2", base::Time(), false, false, false,
COOKIE_PRIORITY_DEFAULT));
- EXPECT_EQ(13U, this->GetAllCookies(cm.get()).size());
+ EXPECT_EQ(14U, this->GetAllCookies(cm.get()).size());
}
Time GetFirstCookieAccessDate(CookieMonster* cm) {
@@ -594,6 +603,7 @@ struct CookiesInputInfo {
const base::Time expiration_time;
bool secure;
bool http_only;
+ bool first_party_only;
CookiePriority priority;
};
@@ -626,7 +636,8 @@ ACTION_P4(DeleteAllCreatedBetweenAction,
ACTION_P3(SetCookieWithDetailsAction, cookie_monster, cc, callback) {
cookie_monster->SetCookieWithDetailsAsync(
cc.url, cc.name, cc.value, cc.domain, cc.path, cc.expiration_time,
- cc.secure, cc.http_only, cc.priority, callback->AsCallback());
+ cc.secure, cc.http_only, cc.first_party_only, cc.priority,
+ callback->AsCallback());
}
ACTION_P2(GetAllCookiesAction, cookie_monster, callback) {
@@ -842,6 +853,7 @@ TEST_F(DeferredCookieTaskTest, DeferredSetCookieWithDetails) {
base::Time(),
false,
false,
+ false,
COOKIE_PRIORITY_DEFAULT};
BeginWithForDomainKey(
"google.izzle", SetCookieWithDetailsAction(&cookie_monster(), cookie_info,
@@ -857,6 +869,7 @@ TEST_F(DeferredCookieTaskTest, DeferredSetCookieWithDetails) {
base::Time(),
false,
false,
+ false,
COOKIE_PRIORITY_DEFAULT};
EXPECT_CALL(set_cookies_callback, Invoke(true))
.WillOnce(SetCookieWithDetailsAction(&cookie_monster(), cookie_info_exp,
@@ -1537,30 +1550,30 @@ TEST_F(CookieMonsterTest, SetCookieWithDetails) {
EXPECT_TRUE(SetCookieWithDetails(cm.get(), url_google_foo_, "A", "B",
std::string(), "/foo", base::Time(), false,
- false, COOKIE_PRIORITY_DEFAULT));
+ false, false, COOKIE_PRIORITY_DEFAULT));
EXPECT_TRUE(SetCookieWithDetails(cm.get(), url_google_bar_, "C", "D",
"google.izzle", "/bar", base::Time(), false,
- true, COOKIE_PRIORITY_DEFAULT));
- EXPECT_TRUE(SetCookieWithDetails(cm.get(), url_google_, "E", "F",
- std::string(), std::string(), base::Time(),
true, false, COOKIE_PRIORITY_DEFAULT));
+ EXPECT_TRUE(SetCookieWithDetails(
+ cm.get(), url_google_, "E", "F", std::string(), std::string(),
+ base::Time(), true, false, false, COOKIE_PRIORITY_DEFAULT));
// Test that malformed attributes fail to set the cookie.
EXPECT_FALSE(SetCookieWithDetails(cm.get(), url_google_foo_, " A", "B",
std::string(), "/foo", base::Time(), false,
- false, COOKIE_PRIORITY_DEFAULT));
+ false, false, COOKIE_PRIORITY_DEFAULT));
EXPECT_FALSE(SetCookieWithDetails(cm.get(), url_google_foo_, "A;", "B",
std::string(), "/foo", base::Time(), false,
- false, COOKIE_PRIORITY_DEFAULT));
+ false, false, COOKIE_PRIORITY_DEFAULT));
EXPECT_FALSE(SetCookieWithDetails(cm.get(), url_google_foo_, "A=", "B",
std::string(), "/foo", base::Time(), false,
- false, COOKIE_PRIORITY_DEFAULT));
- EXPECT_FALSE(SetCookieWithDetails(cm.get(), url_google_foo_, "A", "B",
- "google.ozzzzzzle", "foo", base::Time(),
false, false, COOKIE_PRIORITY_DEFAULT));
+ EXPECT_FALSE(SetCookieWithDetails(
+ cm.get(), url_google_foo_, "A", "B", "google.ozzzzzzle", "foo",
+ base::Time(), false, false, false, COOKIE_PRIORITY_DEFAULT));
EXPECT_FALSE(SetCookieWithDetails(cm.get(), url_google_foo_, "A=", "B",
std::string(), "foo", base::Time(), false,
- false, COOKIE_PRIORITY_DEFAULT));
+ false, false, COOKIE_PRIORITY_DEFAULT));
CookieList cookies = GetAllCookiesForURL(cm.get(), url_google_foo_);
CookieList::iterator it = cookies.begin();
@@ -1627,7 +1640,7 @@ TEST_F(CookieMonsterTest, DeleteAllForHost) {
GetCookies(cm.get(), GURL(kTopLevelDomainPlus2Secure +
std::string("/dir1/dir2/xxx"))));
- EXPECT_EQ(5, DeleteAllForHost(cm.get(), GURL(kTopLevelDomainPlus2)));
+ EXPECT_EQ(6, DeleteAllForHost(cm.get(), GURL(kTopLevelDomainPlus2)));
EXPECT_EQ(8U, GetAllCookies(cm.get()).size());
EXPECT_EQ("dom_1=X; dom_2=X; dom_3=X; host_3=X",
@@ -1641,7 +1654,7 @@ TEST_F(CookieMonsterTest, DeleteAllForHost) {
std::string("/dir1/dir2/xxx"))));
PopulateCmForDeleteAllForHost(cm);
- EXPECT_EQ(5, DeleteAllForHost(cm.get(), GURL(kTopLevelDomainPlus2Secure)));
+ EXPECT_EQ(6, DeleteAllForHost(cm.get(), GURL(kTopLevelDomainPlus2Secure)));
EXPECT_EQ(8U, GetAllCookies(cm.get()).size());
EXPECT_EQ("dom_1=X; dom_2=X; dom_3=X; host_3=X",
@@ -1655,7 +1668,7 @@ TEST_F(CookieMonsterTest, DeleteAllForHost) {
std::string("/dir1/dir2/xxx"))));
PopulateCmForDeleteAllForHost(cm);
- EXPECT_EQ(5, DeleteAllForHost(cm.get(), GURL(kTopLevelDomainPlus2Secure +
+ EXPECT_EQ(6, DeleteAllForHost(cm.get(), GURL(kTopLevelDomainPlus2Secure +
std::string("/dir1/xxx"))));
EXPECT_EQ(8U, GetAllCookies(cm.get()).size());
@@ -1696,13 +1709,13 @@ TEST_F(CookieMonsterTest, UniqueCreationTime) {
options);
SetCookieWithDetails(cm.get(), url_google_, "setCookieWithDetails1", "A",
- ".google.com", "/", Time(), false, false,
+ ".google.com", "/", Time(), false, false, false,
COOKIE_PRIORITY_DEFAULT);
SetCookieWithDetails(cm.get(), url_google_, "setCookieWithDetails2", "A",
- ".google.com", "/", Time(), false, false,
+ ".google.com", "/", Time(), false, false, false,
COOKIE_PRIORITY_DEFAULT);
SetCookieWithDetails(cm.get(), url_google_, "setCookieWithDetails3", "A",
- ".google.com", "/", Time(), false, false,
+ ".google.com", "/", Time(), false, false, false,
COOKIE_PRIORITY_DEFAULT);
// Now we check
@@ -1770,6 +1783,7 @@ TEST_F(CookieMonsterTest, BackingStoreCommunication) {
expires,
false,
false,
+ false,
COOKIE_PRIORITY_DEFAULT},
{GURL("https://www.google.com"),
"b",
@@ -1779,6 +1793,7 @@ TEST_F(CookieMonsterTest, BackingStoreCommunication) {
expires + TimeDelta::FromSeconds(10),
true,
true,
+ false,
COOKIE_PRIORITY_DEFAULT},
{GURL("https://google.com"),
"c",
@@ -1788,6 +1803,7 @@ TEST_F(CookieMonsterTest, BackingStoreCommunication) {
base::Time::Now() + base::TimeDelta::FromSeconds(100),
true,
false,
+ true,
COOKIE_PRIORITY_DEFAULT}};
const int INPUT_DELETE = 1;
@@ -1798,7 +1814,8 @@ TEST_F(CookieMonsterTest, BackingStoreCommunication) {
p < &input_info[arraysize(input_info)]; p++) {
EXPECT_TRUE(SetCookieWithDetails(cmout.get(), p->url, p->name, p->value,
p->domain, p->path, p->expiration_time,
- p->secure, p->http_only, p->priority));
+ p->secure, p->http_only,
+ p->first_party_only, p->priority));
}
GURL del_url(input_info[INPUT_DELETE]
.url.Resolve(input_info[INPUT_DELETE].path)
@@ -1827,6 +1844,7 @@ TEST_F(CookieMonsterTest, BackingStoreCommunication) {
output->CreationDate().ToInternalValue());
EXPECT_EQ(input->secure, output->IsSecure());
EXPECT_EQ(input->http_only, output->IsHttpOnly());
+ EXPECT_EQ(input->first_party_only, output->IsFirstPartyOnly());
EXPECT_TRUE(output->IsPersistent());
EXPECT_EQ(input->expiration_time.ToInternalValue(),
output->ExpiryDate().ToInternalValue());
@@ -2101,7 +2119,7 @@ TEST_F(CookieMonsterTest, HistogramCheck) {
expired_histogram->SnapshotSamples());
ASSERT_TRUE(SetCookieWithDetails(
cm.get(), GURL("http://fake.a.url"), "a", "b", "a.url", "/",
- base::Time::Now() + base::TimeDelta::FromMinutes(59), false, false,
+ base::Time::Now() + base::TimeDelta::FromMinutes(59), false, false, false,
COOKIE_PRIORITY_DEFAULT));
scoped_ptr<base::HistogramSamples> samples2(
@@ -2158,11 +2176,13 @@ class MultiThreadedCookieMonsterTest : public CookieMonsterTest {
base::Time expiration_time = base::Time();
bool secure = false;
bool http_only = false;
+ bool first_party_only = false;
CookiePriority priority = COOKIE_PRIORITY_DEFAULT;
cm->SetCookieWithDetailsAsync(
url, name, value, domain, path, expiration_time, secure, http_only,
- priority, base::Bind(&ResultSavingCookieCallback<bool>::Run,
- base::Unretained(callback)));
+ first_party_only, priority,
+ base::Bind(&ResultSavingCookieCallback<bool>::Run,
+ base::Unretained(callback)));
}
void DeleteAllCreatedBetweenTask(CookieMonster* cm,
@@ -2288,7 +2308,7 @@ TEST_F(MultiThreadedCookieMonsterTest, ThreadCheckSetCookieWithDetails) {
scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL));
EXPECT_TRUE(SetCookieWithDetails(cm.get(), url_google_foo_, "A", "B",
std::string(), "/foo", base::Time(), false,
- false, COOKIE_PRIORITY_DEFAULT));
+ false, false, COOKIE_PRIORITY_DEFAULT));
ResultSavingCookieCallback<bool> callback(&other_thread_);
base::Closure task =
base::Bind(&net::MultiThreadedCookieMonsterTest::SetCookieWithDetailsTask,
@@ -2555,11 +2575,12 @@ TEST_F(CookieMonsterTest, ControlCharacterPurge) {
// We have to manually build this cookie because it contains a control
// character, and our cookie line parser rejects control characters.
- CanonicalCookie* cc = new CanonicalCookie(
- url, "baz",
- "\x05"
- "boo",
- domain, path, now2, later, now2, false, false, COOKIE_PRIORITY_DEFAULT);
+ CanonicalCookie* cc =
+ new CanonicalCookie(url, "baz",
+ "\x05"
+ "boo",
+ domain, path, now2, later, now2, false, false, false,
+ COOKIE_PRIORITY_DEFAULT);
initial_cookies.push_back(cc);
AddCookieToList(domain, "hello=world; path=" + path, now3, &initial_cookies);
diff --git a/net/cookies/cookie_options.h b/net/cookies/cookie_options.h
index 975b8c8..a7ed5a9 100644
--- a/net/cookies/cookie_options.h
+++ b/net/cookies/cookie_options.h
@@ -7,19 +7,35 @@
#ifndef NET_COOKIES_COOKIE_OPTIONS_H_
#define NET_COOKIES_COOKIE_OPTIONS_H_
+#include "url/gurl.h"
+
namespace net {
class CookieOptions {
public:
- // Default is to exclude httponly, which means:
- // - reading operations will not return httponly cookies.
- // - writing operations will not write httponly cookies.
- CookieOptions() : exclude_httponly_(true), server_time_() {}
+ // Default is to exclude httponly completely, and exclude first-party from
+ // being read, which means:
+ // - reading operations will not return httponly or first-party cookies.
+ // - writing operations will not write httponly cookies (first-party will be
+ // written).
+ //
+ // If a first-party URL is set, then first-party cookies which match that URL
+ // will be returned.
+ CookieOptions()
+ : exclude_httponly_(true),
+ include_first_party_only_(false),
+ server_time_() {}
void set_exclude_httponly() { exclude_httponly_ = true; }
void set_include_httponly() { exclude_httponly_ = false; }
bool exclude_httponly() const { return exclude_httponly_; }
+ void set_include_first_party_only() { include_first_party_only_ = true; }
+ bool include_first_party_only() const { return include_first_party_only_; }
+
+ void set_first_party_url(const GURL& url) { first_party_url_ = url; }
+ GURL first_party_url() const { return first_party_url_; }
+
// |server_time| indicates what the server sending us the Cookie thought the
// current time was when the cookie was produced. This is used to adjust for
// clock skew between server and host.
@@ -31,8 +47,11 @@ class CookieOptions {
private:
bool exclude_httponly_;
+ bool include_first_party_only_;
+ GURL first_party_url_;
base::Time server_time_;
};
+
} // namespace net
#endif // NET_COOKIES_COOKIE_OPTIONS_H_
diff --git a/net/cookies/parsed_cookie.cc b/net/cookies/parsed_cookie.cc
index 8cbca5c..30afe10 100644
--- a/net/cookies/parsed_cookie.cc
+++ b/net/cookies/parsed_cookie.cc
@@ -55,6 +55,7 @@ const char kExpiresTokenName[] = "expires";
const char kMaxAgeTokenName[] = "max-age";
const char kSecureTokenName[] = "secure";
const char kHttpOnlyTokenName[] = "httponly";
+const char kFirstPartyOnlyTokenName[] = "first-party-only";
const char kPriorityTokenName[] = "priority";
const char kTerminator[] = "\n\r\0";
@@ -162,6 +163,7 @@ ParsedCookie::ParsedCookie(const std::string& cookie_line)
maxage_index_(0),
secure_index_(0),
httponly_index_(0),
+ firstpartyonly_index_(0),
priority_index_(0) {
if (cookie_line.size() > kMaxCookieSize) {
VLOG(1) << "Not parsing cookie, too large: " << cookie_line.size();
@@ -228,6 +230,11 @@ bool ParsedCookie::SetIsHttpOnly(bool is_http_only) {
return SetBool(&httponly_index_, kHttpOnlyTokenName, is_http_only);
}
+bool ParsedCookie::SetIsFirstPartyOnly(bool is_first_party_only) {
+ return SetBool(&firstpartyonly_index_, kFirstPartyOnlyTokenName,
+ is_first_party_only);
+}
+
bool ParsedCookie::SetPriority(const std::string& priority) {
return SetString(&priority_index_, kPriorityTokenName, priority);
}
@@ -238,7 +245,8 @@ std::string ParsedCookie::ToCookieLine() const {
if (!out.empty())
out.append("; ");
out.append(it->first);
- if (it->first != kSecureTokenName && it->first != kHttpOnlyTokenName) {
+ if (it->first != kSecureTokenName && it->first != kHttpOnlyTokenName &&
+ it->first != kFirstPartyOnlyTokenName) {
out.append("=");
out.append(it->second);
}
@@ -429,6 +437,8 @@ void ParsedCookie::SetupAttributes() {
secure_index_ = i;
} else if (pairs_[i].first == kHttpOnlyTokenName) {
httponly_index_ = i;
+ } else if (pairs_[i].first == kFirstPartyOnlyTokenName) {
+ firstpartyonly_index_ = i;
} else if (pairs_[i].first == kPriorityTokenName) {
priority_index_ = i;
} else {
@@ -486,6 +496,7 @@ void ParsedCookie::ClearAttributePair(size_t index) {
&maxage_index_,
&secure_index_,
&httponly_index_,
+ &firstpartyonly_index_,
&priority_index_};
for (size_t i = 0; i < arraysize(indexes); ++i) {
if (*indexes[i] == index)
diff --git a/net/cookies/parsed_cookie.h b/net/cookies/parsed_cookie.h
index 971cf37..029728d 100644
--- a/net/cookies/parsed_cookie.h
+++ b/net/cookies/parsed_cookie.h
@@ -48,6 +48,7 @@ class NET_EXPORT ParsedCookie {
const std::string& MaxAge() const { return pairs_[maxage_index_].second; }
bool IsSecure() const { return secure_index_ != 0; }
bool IsHttpOnly() const { return httponly_index_ != 0; }
+ bool IsFirstPartyOnly() const { return firstpartyonly_index_ != 0; }
CookiePriority Priority() const;
// Returns the number of attributes, for example, returning 2 for:
@@ -67,6 +68,7 @@ class NET_EXPORT ParsedCookie {
bool SetMaxAge(const std::string& maxage);
bool SetIsSecure(bool is_secure);
bool SetIsHttpOnly(bool is_http_only);
+ bool SetIsFirstPartyOnly(bool is_first_party_only);
bool SetPriority(const std::string& priority);
// Returns the cookie description as it appears in a HTML response header.
@@ -136,6 +138,7 @@ class NET_EXPORT ParsedCookie {
size_t maxage_index_;
size_t secure_index_;
size_t httponly_index_;
+ size_t firstpartyonly_index_;
size_t priority_index_;
DISALLOW_COPY_AND_ASSIGN(ParsedCookie);
diff --git a/net/cookies/parsed_cookie_unittest.cc b/net/cookies/parsed_cookie_unittest.cc
index 9f231e8..61a6167 100644
--- a/net/cookies/parsed_cookie_unittest.cc
+++ b/net/cookies/parsed_cookie_unittest.cc
@@ -92,16 +92,18 @@ TEST(ParsedCookieTest, TestNameless) {
}
TEST(ParsedCookieTest, TestAttributeCase) {
- ParsedCookie pc("BLAHHH; Path=/; sECuRe; httpONLY; pRIoRitY=hIgH");
+ ParsedCookie pc(
+ "BLAHHH; Path=/; sECuRe; httpONLY; first-PaRty-only; pRIoRitY=hIgH");
EXPECT_TRUE(pc.IsValid());
EXPECT_TRUE(pc.IsSecure());
EXPECT_TRUE(pc.IsHttpOnly());
+ EXPECT_TRUE(pc.IsFirstPartyOnly());
EXPECT_TRUE(pc.HasPath());
EXPECT_EQ("/", pc.Path());
EXPECT_EQ("", pc.Name());
EXPECT_EQ("BLAHHH", pc.Value());
EXPECT_EQ(COOKIE_PRIORITY_HIGH, pc.Priority());
- EXPECT_EQ(4U, pc.NumberOfAttributes());
+ EXPECT_EQ(5U, pc.NumberOfAttributes());
}
TEST(ParsedCookieTest, TestDoubleQuotedNameless) {
@@ -146,14 +148,15 @@ TEST(ParsedCookieTest, MissingValue) {
}
TEST(ParsedCookieTest, Whitespace) {
- ParsedCookie pc(" A = BC ;secure;;; httponly");
+ ParsedCookie pc(" A = BC ;secure;;; first-party-only ");
EXPECT_TRUE(pc.IsValid());
EXPECT_EQ("A", pc.Name());
EXPECT_EQ("BC", pc.Value());
EXPECT_FALSE(pc.HasPath());
EXPECT_FALSE(pc.HasDomain());
EXPECT_TRUE(pc.IsSecure());
- EXPECT_TRUE(pc.IsHttpOnly());
+ EXPECT_FALSE(pc.IsHttpOnly());
+ EXPECT_TRUE(pc.IsFirstPartyOnly());
EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
// We parse anything between ; as attributes, so we end up with two
// attributes with an empty string name and value.
@@ -168,6 +171,7 @@ TEST(ParsedCookieTest, MultipleEquals) {
EXPECT_FALSE(pc.HasDomain());
EXPECT_TRUE(pc.IsSecure());
EXPECT_TRUE(pc.IsHttpOnly());
+ EXPECT_FALSE(pc.IsFirstPartyOnly());
EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
EXPECT_EQ(4U, pc.NumberOfAttributes());
}
@@ -353,11 +357,12 @@ TEST(ParsedCookieTest, SetAttributes) {
EXPECT_TRUE(pc.SetIsSecure(true));
EXPECT_TRUE(pc.SetIsHttpOnly(true));
EXPECT_TRUE(pc.SetIsHttpOnly(true));
+ EXPECT_TRUE(pc.SetIsFirstPartyOnly(true));
EXPECT_TRUE(pc.SetPriority("HIGH"));
EXPECT_EQ(
"name=value; domain=domain.com; path=/; "
"expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
- "httponly; priority=HIGH",
+ "httponly; first-party-only; priority=HIGH",
pc.ToCookieLine());
EXPECT_TRUE(pc.HasDomain());
EXPECT_TRUE(pc.HasPath());
@@ -365,6 +370,7 @@ TEST(ParsedCookieTest, SetAttributes) {
EXPECT_TRUE(pc.HasMaxAge());
EXPECT_TRUE(pc.IsSecure());
EXPECT_TRUE(pc.IsHttpOnly());
+ EXPECT_TRUE(pc.IsFirstPartyOnly());
EXPECT_EQ(COOKIE_PRIORITY_HIGH, pc.Priority());
// Clear one attribute from the middle.
@@ -377,7 +383,7 @@ TEST(ParsedCookieTest, SetAttributes) {
EXPECT_EQ(
"name=value; domain=domain.com; path=/foo; "
"expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
- "httponly; priority=HIGH",
+ "httponly; first-party-only; priority=HIGH",
pc.ToCookieLine());
// Set priority to medium.
@@ -385,7 +391,7 @@ TEST(ParsedCookieTest, SetAttributes) {
EXPECT_EQ(
"name=value; domain=domain.com; path=/foo; "
"expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
- "httponly; priority=medium",
+ "httponly; first-party-only; priority=medium",
pc.ToCookieLine());
// Clear the rest and change the name and value.
@@ -395,6 +401,7 @@ TEST(ParsedCookieTest, SetAttributes) {
EXPECT_TRUE(pc.SetMaxAge(std::string()));
EXPECT_TRUE(pc.SetIsSecure(false));
EXPECT_TRUE(pc.SetIsHttpOnly(false));
+ EXPECT_TRUE(pc.SetIsFirstPartyOnly(false));
EXPECT_TRUE(pc.SetName("name2"));
EXPECT_TRUE(pc.SetValue("value2"));
EXPECT_TRUE(pc.SetPriority(std::string()));
@@ -404,6 +411,7 @@ TEST(ParsedCookieTest, SetAttributes) {
EXPECT_FALSE(pc.HasMaxAge());
EXPECT_FALSE(pc.IsSecure());
EXPECT_FALSE(pc.IsHttpOnly());
+ EXPECT_FALSE(pc.IsFirstPartyOnly());
EXPECT_EQ("name2=value2", pc.ToCookieLine());
}
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index c763a05..958ef82 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -629,6 +629,12 @@ void URLRequestHttpJob::AddCookieHeaderAndStart() {
void URLRequestHttpJob::DoLoadCookies() {
CookieOptions options;
options.set_include_httponly();
+ options.set_include_first_party_only();
+
+ // TODO(mkwst): Pipe a switch down here to allow us to decide whether we
+ // should enforce "first-party only" cookies or not (by setting |options|'s
+ // first-party-url to the first-party-for-cookies value. crbug.com/459154
+
GetCookieStore()->GetCookiesWithOptionsAsync(
request_->url(), options,
base::Bind(&URLRequestHttpJob::OnCookiesLoaded,
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 1ed23b8..6173e3d 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -3542,15 +3542,25 @@ Therefore, the affected-histogram name has to have at least one dot in it.
</histogram>
<histogram name="Cookie.TimeDatabaseMigrationToV5" units="ms">
- <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+ <owner>erikwright@chromium.org</owner>
<summary>The amount of time (ms) to migrate a v4 database to v5.</summary>
</histogram>
<histogram name="Cookie.TimeDatabaseMigrationToV6" units="ms">
- <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+ <owner>erikwright@chromium.org</owner>
<summary>The amount of time (ms) to migrate a v5 database to v6.</summary>
</histogram>
+<histogram name="Cookie.TimeDatabaseMigrationToV7" units="ms">
+ <owner>erikwright@chromium.org</owner>
+ <summary>The amount of time (ms) to migrate a v6 database to v7.</summary>
+</histogram>
+
+<histogram name="Cookie.TimeDatabaseMigrationToV8" units="ms">
+ <owner>erikwright@chromium.org</owner>
+ <summary>The amount of time (ms) to migrate a v7 database to v8.</summary>
+</histogram>
+
<histogram name="Cookie.TimeGet" units="ms">
<obsolete>
Deprecated as of 11/2014.