1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/browsing_data/browsing_data_cookie_helper.h" 6 7#include "base/bind.h" 8#include "base/run_loop.h" 9#include "chrome/test/base/testing_profile.h" 10#include "content/public/test/test_browser_thread_bundle.h" 11#include "net/cookies/canonical_cookie.h" 12#include "net/cookies/parsed_cookie.h" 13#include "net/url_request/url_request_context_getter.h" 14#include "testing/gtest/include/gtest/gtest.h" 15 16namespace { 17 18// Test expectations for a given cookie. 19class CookieExpectation { 20 public: 21 CookieExpectation() : matched_(false) {} 22 23 bool MatchesCookie(const net::CanonicalCookie& cookie) const { 24 if (!source_.empty() && source_ != cookie.Source()) 25 return false; 26 if (!domain_.empty() && domain_ != cookie.Domain()) 27 return false; 28 if (!path_.empty() && path_ != cookie.Path()) 29 return false; 30 if (!name_.empty() && name_ != cookie.Name()) 31 return false; 32 if (!value_.empty() && value_ != cookie.Value()) 33 return false; 34 return true; 35 } 36 37 std::string source_; 38 std::string domain_; 39 std::string path_; 40 std::string name_; 41 std::string value_; 42 bool matched_; 43}; 44 45// Matches a CookieExpectation against a Cookie. 46class CookieMatcher { 47 public: 48 explicit CookieMatcher(const net::CanonicalCookie& cookie) 49 : cookie_(cookie) {} 50 bool operator()(const CookieExpectation& expectation) { 51 return expectation.MatchesCookie(cookie_); 52 } 53 net::CanonicalCookie cookie_; 54}; 55 56// Unary predicate to determine whether an expectation has been matched. 57bool ExpectationIsMatched(const CookieExpectation& expectation) { 58 return expectation.matched_; 59} 60 61class BrowsingDataCookieHelperTest : public testing::Test { 62 public: 63 BrowsingDataCookieHelperTest() 64 : testing_profile_(new TestingProfile()) { 65 } 66 67 virtual void SetUp() OVERRIDE { cookie_expectations_.clear(); } 68 69 // Adds an expectation for a cookie that satisfies the given parameters. 70 void AddCookieExpectation(const char* source, 71 const char* domain, 72 const char* path, 73 const char* name, 74 const char* value) { 75 CookieExpectation matcher; 76 if (source) 77 matcher.source_ = source; 78 if (domain) 79 matcher.domain_ = domain; 80 if (path) 81 matcher.path_ = path; 82 if (name) 83 matcher.name_ = name; 84 if (value) 85 matcher.value_ = value; 86 cookie_expectations_.push_back(matcher); 87 } 88 89 // Checks the existing expectations, and then clears all existing 90 // expectations. 91 void CheckCookieExpectations() { 92 ASSERT_EQ(cookie_expectations_.size(), cookie_list_.size()); 93 94 // For each cookie, look for a matching expectation. 95 for (net::CookieList::iterator it = cookie_list_.begin(); 96 it != cookie_list_.end(); 97 ++it) { 98 CookieMatcher matcher(*it); 99 std::vector<CookieExpectation>::iterator match = std::find_if( 100 cookie_expectations_.begin(), cookie_expectations_.end(), matcher); 101 if (match != cookie_expectations_.end()) 102 match->matched_ = true; 103 } 104 105 // Check that each expectation has been matched. 106 unsigned long match_count = std::count_if(cookie_expectations_.begin(), 107 cookie_expectations_.end(), 108 ExpectationIsMatched); 109 EXPECT_EQ(cookie_expectations_.size(), match_count); 110 111 cookie_expectations_.clear(); 112 } 113 114 void CreateCookiesForTest() { 115 scoped_refptr<net::CookieMonster> cookie_monster = 116 testing_profile_->GetCookieMonster(); 117 cookie_monster->SetCookieWithOptionsAsync( 118 GURL("http://www.google.com"), "A=1", net::CookieOptions(), 119 net::CookieMonster::SetCookiesCallback()); 120 cookie_monster->SetCookieWithOptionsAsync( 121 GURL("http://www.gmail.google.com"), "B=1", net::CookieOptions(), 122 net::CookieMonster::SetCookiesCallback()); 123 } 124 125 void CreateCookiesForDomainCookieTest() { 126 scoped_refptr<net::CookieMonster> cookie_monster = 127 testing_profile_->GetCookieMonster(); 128 cookie_monster->SetCookieWithOptionsAsync( 129 GURL("http://www.google.com"), "A=1", net::CookieOptions(), 130 net::CookieMonster::SetCookiesCallback()); 131 cookie_monster->SetCookieWithOptionsAsync( 132 GURL("http://www.google.com"), "A=2; Domain=.www.google.com ", 133 net::CookieOptions(), net::CookieMonster::SetCookiesCallback()); 134 } 135 136 void FetchCallback(const net::CookieList& cookies) { 137 cookie_list_ = cookies; 138 139 AddCookieExpectation(NULL, "www.google.com", NULL, "A", NULL); 140 AddCookieExpectation(NULL, "www.gmail.google.com", NULL, "B", NULL); 141 CheckCookieExpectations(); 142 } 143 144 void DomainCookieCallback(const net::CookieList& cookies) { 145 cookie_list_ = cookies; 146 147 AddCookieExpectation(NULL, "www.google.com", NULL, "A", "1"); 148 AddCookieExpectation(NULL, ".www.google.com", NULL, "A", "2"); 149 CheckCookieExpectations(); 150 } 151 152 void DeleteCallback(const net::CookieList& cookies) { 153 cookie_list_ = cookies; 154 AddCookieExpectation(NULL, "www.gmail.google.com", NULL, "B", NULL); 155 CheckCookieExpectations(); 156 } 157 158 void CannedUniqueCallback(const net::CookieList& cookies) { 159 cookie_list_ = cookies; 160 AddCookieExpectation( 161 "http://www.google.com/", "www.google.com", "/", "A", NULL); 162 CheckCookieExpectations(); 163 } 164 165 void CannedReplaceCookieCallback(const net::CookieList& cookies) { 166 cookie_list_ = cookies; 167 AddCookieExpectation( 168 "http://www.google.com/", "www.google.com", "/", "A", "2"); 169 AddCookieExpectation( 170 "http://www.google.com/", "www.google.com", "/example/0", "A", "4"); 171 AddCookieExpectation( 172 "http://www.google.com/", ".google.com", "/", "A", "6"); 173 AddCookieExpectation( 174 "http://www.google.com/", ".google.com", "/example/1", "A", "8"); 175 AddCookieExpectation( 176 "http://www.google.com/", ".www.google.com", "/", "A", "10"); 177 CheckCookieExpectations(); 178 } 179 180 void CannedDomainCookieCallback(const net::CookieList& cookies) { 181 cookie_list_ = cookies; 182 AddCookieExpectation( 183 "http://www.google.com/", "www.google.com", NULL, "A", NULL); 184 AddCookieExpectation( 185 "http://www.google.com/", ".www.google.com", NULL, "A", NULL); 186 CheckCookieExpectations(); 187 } 188 189 void CannedDifferentFramesCallback(const net::CookieList& cookie_list) { 190 ASSERT_EQ(3U, cookie_list.size()); 191 } 192 193 void DeleteCookie(BrowsingDataCookieHelper* helper, const GURL origin) { 194 for (net::CookieList::iterator it = cookie_list_.begin(); 195 it != cookie_list_.end(); 196 ++it) { 197 if (it->Source() == net::CanonicalCookie::GetCookieSourceFromURL(origin)) 198 helper->DeleteCookie(*it); 199 } 200 } 201 202 protected: 203 content::TestBrowserThreadBundle thread_bundle_; 204 scoped_ptr<TestingProfile> testing_profile_; 205 206 std::vector<CookieExpectation> cookie_expectations_; 207 net::CookieList cookie_list_; 208}; 209 210TEST_F(BrowsingDataCookieHelperTest, FetchData) { 211 CreateCookiesForTest(); 212 scoped_refptr<BrowsingDataCookieHelper> cookie_helper( 213 new BrowsingDataCookieHelper(testing_profile_->GetRequestContext())); 214 215 cookie_helper->StartFetching( 216 base::Bind(&BrowsingDataCookieHelperTest::FetchCallback, 217 base::Unretained(this))); 218 base::RunLoop().RunUntilIdle(); 219} 220 221TEST_F(BrowsingDataCookieHelperTest, DomainCookie) { 222 CreateCookiesForDomainCookieTest(); 223 scoped_refptr<BrowsingDataCookieHelper> cookie_helper( 224 new BrowsingDataCookieHelper(testing_profile_->GetRequestContext())); 225 226 cookie_helper->StartFetching( 227 base::Bind(&BrowsingDataCookieHelperTest::DomainCookieCallback, 228 base::Unretained(this))); 229 base::RunLoop().RunUntilIdle(); 230} 231 232TEST_F(BrowsingDataCookieHelperTest, DeleteCookie) { 233 CreateCookiesForTest(); 234 scoped_refptr<BrowsingDataCookieHelper> cookie_helper( 235 new BrowsingDataCookieHelper(testing_profile_->GetRequestContext())); 236 237 cookie_helper->StartFetching( 238 base::Bind(&BrowsingDataCookieHelperTest::FetchCallback, 239 base::Unretained(this))); 240 base::RunLoop().RunUntilIdle(); 241 242 net::CanonicalCookie cookie = cookie_list_[0]; 243 cookie_helper->DeleteCookie(cookie); 244 245 cookie_helper->StartFetching( 246 base::Bind(&BrowsingDataCookieHelperTest::DeleteCallback, 247 base::Unretained(this))); 248 base::RunLoop().RunUntilIdle(); 249} 250 251TEST_F(BrowsingDataCookieHelperTest, CannedDeleteCookie) { 252 CreateCookiesForTest(); 253 scoped_refptr<CannedBrowsingDataCookieHelper> helper( 254 new CannedBrowsingDataCookieHelper( 255 testing_profile_->GetRequestContext())); 256 257 ASSERT_TRUE(helper->empty()); 258 259 const GURL origin1("http://www.google.com"); 260 const GURL origin2("http://www.gmail.google.com"); 261 helper->AddChangedCookie(origin1, origin1, "A=1", net::CookieOptions()); 262 helper->AddChangedCookie(origin2, origin2, "B=1", net::CookieOptions()); 263 264 helper->StartFetching( 265 base::Bind(&BrowsingDataCookieHelperTest::FetchCallback, 266 base::Unretained(this))); 267 base::RunLoop().RunUntilIdle(); 268 269 EXPECT_EQ(2u, helper->GetCookieCount()); 270 271 DeleteCookie(helper.get(), origin1); 272 273 EXPECT_EQ(1u, helper->GetCookieCount()); 274 helper->StartFetching( 275 base::Bind(&BrowsingDataCookieHelperTest::DeleteCallback, 276 base::Unretained(this))); 277 base::RunLoop().RunUntilIdle(); 278} 279 280TEST_F(BrowsingDataCookieHelperTest, CannedDomainCookie) { 281 const GURL origin("http://www.google.com"); 282 net::CookieList cookie; 283 284 scoped_refptr<CannedBrowsingDataCookieHelper> helper( 285 new CannedBrowsingDataCookieHelper( 286 testing_profile_->GetRequestContext())); 287 288 ASSERT_TRUE(helper->empty()); 289 helper->AddChangedCookie(origin, origin, "A=1", net::CookieOptions()); 290 helper->AddChangedCookie(origin, origin, "A=1; Domain=.www.google.com", 291 net::CookieOptions()); 292 // Try adding invalid cookies that will be ignored. 293 helper->AddChangedCookie(origin, origin, std::string(), net::CookieOptions()); 294 helper->AddChangedCookie(origin, 295 origin, 296 "C=bad guy; Domain=wrongdomain.com", 297 net::CookieOptions()); 298 299 helper->StartFetching( 300 base::Bind(&BrowsingDataCookieHelperTest::CannedDomainCookieCallback, 301 base::Unretained(this))); 302 cookie = cookie_list_; 303 304 helper->Reset(); 305 ASSERT_TRUE(helper->empty()); 306 307 helper->AddReadCookies(origin, origin, cookie); 308 helper->StartFetching( 309 base::Bind(&BrowsingDataCookieHelperTest::CannedDomainCookieCallback, 310 base::Unretained(this))); 311} 312 313TEST_F(BrowsingDataCookieHelperTest, CannedUnique) { 314 const GURL origin("http://www.google.com"); 315 net::CookieList cookie; 316 317 scoped_refptr<CannedBrowsingDataCookieHelper> helper( 318 new CannedBrowsingDataCookieHelper( 319 testing_profile_->GetRequestContext())); 320 321 ASSERT_TRUE(helper->empty()); 322 helper->AddChangedCookie(origin, origin, "A=1", net::CookieOptions()); 323 helper->AddChangedCookie(origin, origin, "A=1", net::CookieOptions()); 324 helper->StartFetching( 325 base::Bind(&BrowsingDataCookieHelperTest::CannedUniqueCallback, 326 base::Unretained(this))); 327 328 cookie = cookie_list_; 329 helper->Reset(); 330 ASSERT_TRUE(helper->empty()); 331 332 helper->AddReadCookies(origin, origin, cookie); 333 helper->AddReadCookies(origin, origin, cookie); 334 helper->StartFetching( 335 base::Bind(&BrowsingDataCookieHelperTest::CannedUniqueCallback, 336 base::Unretained(this))); 337} 338 339TEST_F(BrowsingDataCookieHelperTest, CannedReplaceCookie) { 340 const GURL origin("http://www.google.com"); 341 net::CookieList cookie; 342 343 scoped_refptr<CannedBrowsingDataCookieHelper> helper( 344 new CannedBrowsingDataCookieHelper( 345 testing_profile_->GetRequestContext())); 346 347 ASSERT_TRUE(helper->empty()); 348 helper->AddChangedCookie(origin, origin, "A=1", net::CookieOptions()); 349 helper->AddChangedCookie(origin, origin, "A=2", net::CookieOptions()); 350 helper->AddChangedCookie(origin, origin, "A=3; Path=/example/0", 351 net::CookieOptions()); 352 helper->AddChangedCookie(origin, origin, "A=4; Path=/example/0", 353 net::CookieOptions()); 354 helper->AddChangedCookie(origin, origin, "A=5; Domain=google.com", 355 net::CookieOptions()); 356 helper->AddChangedCookie(origin, origin, "A=6; Domain=google.com", 357 net::CookieOptions()); 358 helper->AddChangedCookie(origin, origin, 359 "A=7; Domain=google.com; Path=/example/1", 360 net::CookieOptions()); 361 helper->AddChangedCookie(origin, origin, 362 "A=8; Domain=google.com; Path=/example/1", 363 net::CookieOptions()); 364 365 helper->AddChangedCookie(origin, origin, 366 "A=9; Domain=www.google.com", 367 net::CookieOptions()); 368 helper->AddChangedCookie(origin, origin, 369 "A=10; Domain=www.google.com", 370 net::CookieOptions()); 371 372 helper->StartFetching( 373 base::Bind(&BrowsingDataCookieHelperTest::CannedReplaceCookieCallback, 374 base::Unretained(this))); 375 376 cookie = cookie_list_; 377 helper->Reset(); 378 ASSERT_TRUE(helper->empty()); 379 380 helper->AddReadCookies(origin, origin, cookie); 381 helper->AddReadCookies(origin, origin, cookie); 382 helper->StartFetching( 383 base::Bind(&BrowsingDataCookieHelperTest::CannedReplaceCookieCallback, 384 base::Unretained(this))); 385} 386 387TEST_F(BrowsingDataCookieHelperTest, CannedEmpty) { 388 const GURL url_google("http://www.google.com"); 389 390 scoped_refptr<CannedBrowsingDataCookieHelper> helper( 391 new CannedBrowsingDataCookieHelper( 392 testing_profile_->GetRequestContext())); 393 394 ASSERT_TRUE(helper->empty()); 395 helper->AddChangedCookie(url_google, url_google, "a=1", 396 net::CookieOptions()); 397 ASSERT_FALSE(helper->empty()); 398 helper->Reset(); 399 ASSERT_TRUE(helper->empty()); 400 401 net::CookieList cookies; 402 net::ParsedCookie pc("a=1"); 403 scoped_ptr<net::CanonicalCookie> cookie( 404 new net::CanonicalCookie(url_google, pc)); 405 cookies.push_back(*cookie); 406 407 helper->AddReadCookies(url_google, url_google, cookies); 408 ASSERT_FALSE(helper->empty()); 409 helper->Reset(); 410 ASSERT_TRUE(helper->empty()); 411} 412 413TEST_F(BrowsingDataCookieHelperTest, CannedDifferentFrames) { 414 GURL frame1_url("http://www.google.com"); 415 GURL frame2_url("http://www.google.de"); 416 GURL request_url("http://www.google.com"); 417 418 scoped_refptr<CannedBrowsingDataCookieHelper> helper( 419 new CannedBrowsingDataCookieHelper( 420 testing_profile_->GetRequestContext())); 421 422 ASSERT_TRUE(helper->empty()); 423 helper->AddChangedCookie(frame1_url, request_url, "a=1", 424 net::CookieOptions()); 425 helper->AddChangedCookie(frame1_url, request_url, "b=1", 426 net::CookieOptions()); 427 helper->AddChangedCookie(frame2_url, request_url, "c=1", 428 net::CookieOptions()); 429 430 helper->StartFetching( 431 base::Bind(&BrowsingDataCookieHelperTest::CannedDifferentFramesCallback, 432 base::Unretained(this))); 433} 434 435TEST_F(BrowsingDataCookieHelperTest, CannedGetCookieCount) { 436 // The URL in the omnibox is a frame URL. This is not necessarily the request 437 // URL, since websites usually include other resources. 438 GURL frame1_url("http://www.google.com"); 439 GURL frame2_url("http://www.google.de"); 440 // The request URL used for all cookies that are added to the |helper|. 441 GURL request1_url("http://static.google.com/foo/res1.html"); 442 GURL request2_url("http://static.google.com/bar/res2.html"); 443 std::string cookie_domain(".www.google.com"); 444 std::string cookie_pair1("A=1"); 445 std::string cookie_pair2("B=1"); 446 // The cookie pair used for adding a cookie that overrides the cookie created 447 // with |cookie_pair1|. The cookie-name of |cookie_pair3| must match the 448 // cookie-name of |cookie-pair1|. 449 std::string cookie_pair3("A=2"); 450 // The cookie pair used for adding a non host-only cookie. The cookie-name 451 // must match the cookie-name of |cookie_pair1| in order to add a host-only 452 // and a non host-only cookie with the same name below. 453 std::string cookie_pair4("A=3"); 454 455 scoped_refptr<CannedBrowsingDataCookieHelper> helper( 456 new CannedBrowsingDataCookieHelper( 457 testing_profile_->GetRequestContext())); 458 459 // Add two different cookies (distinguished by the tuple [cookie-name, 460 // domain-value, path-value]) for a HTTP request to |frame1_url| and verify 461 // that the cookie count is increased to two. The set-cookie-string consists 462 // only of the cookie-pair. This means that the host and the default-path of 463 // the |request_url| are used as domain-value and path-value for the added 464 // cookies. 465 EXPECT_EQ(0U, helper->GetCookieCount()); 466 helper->AddChangedCookie(frame1_url, frame1_url, cookie_pair1, 467 net::CookieOptions()); 468 EXPECT_EQ(1U, helper->GetCookieCount()); 469 helper->AddChangedCookie(frame1_url, frame1_url, cookie_pair2, 470 net::CookieOptions()); 471 EXPECT_EQ(2U, helper->GetCookieCount()); 472 473 // Use a different frame URL for adding another cookie that will replace one 474 // of the previously added cookies. This could happen during an automatic 475 // redirect e.g. |frame1_url| redirects to |frame2_url| and a cookie set by a 476 // request to |frame1_url| is updated. 477 helper->AddChangedCookie(frame2_url, frame1_url, cookie_pair3, 478 net::CookieOptions()); 479 EXPECT_EQ(2U, helper->GetCookieCount()); 480 481 // Add two more cookies that are set while loading resources. The two cookies 482 // below have a differnt path-value since the request URLs have different 483 // paths. 484 helper->AddChangedCookie(frame2_url, request1_url, cookie_pair3, 485 net::CookieOptions()); 486 EXPECT_EQ(3U, helper->GetCookieCount()); 487 helper->AddChangedCookie(frame2_url, request2_url, cookie_pair3, 488 net::CookieOptions()); 489 EXPECT_EQ(4U, helper->GetCookieCount()); 490 491 // Host-only and domain cookies are treated as seperate items. This means that 492 // the following two cookie-strings are stored as two separate cookies, even 493 // though they have the same name and are send with the same request: 494 // "A=1; 495 // "A=3; Domain=www.google.com" 496 // Add a domain cookie and check if it increases the cookie count. 497 helper->AddChangedCookie(frame2_url, frame1_url, 498 cookie_pair4 + "; Domain=" + cookie_domain, 499 net::CookieOptions()); 500 EXPECT_EQ(5U, helper->GetCookieCount()); 501} 502 503} // namespace 504