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#ifndef NET_COOKIES_COOKIE_STORE_UNITTEST_H_
6#define NET_COOKIES_COOKIE_STORE_UNITTEST_H_
7
8#include "base/bind.h"
9#include "base/message_loop/message_loop.h"
10#include "base/strings/string_tokenizer.h"
11#include "base/threading/thread.h"
12#include "net/cookies/cookie_monster.h"
13#include "net/cookies/cookie_store.h"
14#include "net/cookies/cookie_store_test_callbacks.h"
15#include "testing/gtest/include/gtest/gtest.h"
16#include "url/gurl.h"
17
18// This file declares unittest templates that can be used to test common
19// behavior of any CookieStore implementation.
20// See cookie_monster_unittest.cc for an example of an implementation.
21
22namespace net {
23
24using base::Thread;
25
26const int kTimeout = 1000;
27
28const char kUrlFtp[] = "ftp://ftp.google.izzle/";
29const char kUrlGoogle[] = "http://www.google.izzle";
30const char kUrlGoogleFoo[] = "http://www.google.izzle/foo";
31const char kUrlGoogleBar[] = "http://www.google.izzle/bar";
32const char kUrlGoogleSecure[] = "https://www.google.izzle";
33const char kValidCookieLine[] = "A=B; path=/";
34const char kValidDomainCookieLine[] = "A=B; path=/; domain=google.izzle";
35
36// The CookieStoreTestTraits must have the following members:
37// struct CookieStoreTestTraits {
38//   // Factory function.
39//   static scoped_refptr<CookieStore> Create();
40//
41//   // The cookie store is a CookieMonster. Only used to test
42//   // GetCookieMonster().
43//   static const bool is_cookie_monster;
44//
45//   // The cookie store supports cookies with the exclude_httponly() option.
46//   static const bool supports_http_only;
47//
48//   // The cookie store is able to make the difference between the ".com"
49//   // and the "com" domains.
50//   static const bool supports_non_dotted_domains;
51//
52//   // The cookie store handles the domains with trailing dots (such as "com.")
53//   // correctly.
54//   static const bool supports_trailing_dots;
55//
56//   // The cookie store rejects cookies for invalid schemes such as ftp.
57//   static const bool filters_schemes;
58//
59//   // The cookie store has a bug happening when a path is a substring of
60//   // another.
61//   static const bool has_path_prefix_bug;
62//
63//   // Time to wait between two cookie insertions to ensure that cookies have
64//   // different creation times.
65//   static const int creation_time_granularity_in_ms;
66// };
67
68template <class CookieStoreTestTraits>
69class CookieStoreTest : public testing::Test {
70 protected:
71  CookieStoreTest()
72      : url_google_(kUrlGoogle),
73        url_google_secure_(kUrlGoogleSecure),
74        url_google_foo_(kUrlGoogleFoo),
75        url_google_bar_(kUrlGoogleBar) {
76    // This test may be used outside of the net test suite, and thus may not
77    // have a message loop.
78    if (!base::MessageLoop::current())
79      message_loop_.reset(new base::MessageLoop);
80    weak_factory_.reset(new base::WeakPtrFactory<base::MessageLoop>(
81        base::MessageLoop::current()));
82  }
83
84  // Helper methods for the asynchronous Cookie Store API that call the
85  // asynchronous method and then pump the loop until the callback is invoked,
86  // finally returning the value.
87
88  std::string GetCookies(CookieStore* cs, const GURL& url) {
89    DCHECK(cs);
90    CookieOptions options;
91    if (!CookieStoreTestTraits::supports_http_only)
92      options.set_include_httponly();
93    StringResultCookieCallback callback;
94    cs->GetCookiesWithOptionsAsync(
95        url, options,
96        base::Bind(&StringResultCookieCallback::Run,
97                   base::Unretained(&callback)));
98    RunFor(kTimeout);
99    EXPECT_TRUE(callback.did_run());
100    return callback.result();
101  }
102
103  std::string GetCookiesWithOptions(CookieStore* cs,
104                                    const GURL& url,
105                                    const CookieOptions& options) {
106    DCHECK(cs);
107    StringResultCookieCallback callback;
108    cs->GetCookiesWithOptionsAsync(
109        url, options, base::Bind(&StringResultCookieCallback::Run,
110                                 base::Unretained(&callback)));
111    RunFor(kTimeout);
112    EXPECT_TRUE(callback.did_run());
113    return callback.result();
114  }
115
116  bool SetCookieWithOptions(CookieStore* cs,
117                            const GURL& url,
118                            const std::string& cookie_line,
119                            const CookieOptions& options) {
120    DCHECK(cs);
121    ResultSavingCookieCallback<bool> callback;
122    cs->SetCookieWithOptionsAsync(
123        url, cookie_line, options,
124        base::Bind(
125            &ResultSavingCookieCallback<bool>::Run,
126            base::Unretained(&callback)));
127    RunFor(kTimeout);
128    EXPECT_TRUE(callback.did_run());
129    return callback.result();
130  }
131
132  bool SetCookieWithServerTime(CookieStore* cs,
133                               const GURL& url,
134                               const std::string& cookie_line,
135                               const base::Time& server_time) {
136    CookieOptions options;
137    if (!CookieStoreTestTraits::supports_http_only)
138      options.set_include_httponly();
139    options.set_server_time(server_time);
140    return SetCookieWithOptions(cs, url, cookie_line, options);
141  }
142
143  bool SetCookie(CookieStore* cs,
144                 const GURL& url,
145                 const std::string& cookie_line) {
146    CookieOptions options;
147    if (!CookieStoreTestTraits::supports_http_only)
148      options.set_include_httponly();
149    return SetCookieWithOptions(cs, url, cookie_line, options);
150  }
151
152  void DeleteCookie(CookieStore* cs,
153                    const GURL& url,
154                    const std::string& cookie_name) {
155    DCHECK(cs);
156    NoResultCookieCallback callback;
157    cs->DeleteCookieAsync(
158        url, cookie_name,
159        base::Bind(&NoResultCookieCallback::Run, base::Unretained(&callback)));
160    RunFor(kTimeout);
161    EXPECT_TRUE(callback.did_run());
162  }
163
164  int DeleteCreatedBetween(CookieStore* cs,
165                            const base::Time& delete_begin,
166                            const base::Time& delete_end) {
167    DCHECK(cs);
168    ResultSavingCookieCallback<int> callback;
169    cs->DeleteAllCreatedBetweenAsync(
170        delete_begin, delete_end,
171        base::Bind(
172            &ResultSavingCookieCallback<int>::Run,
173            base::Unretained(&callback)));
174    RunFor(kTimeout);
175    EXPECT_TRUE(callback.did_run());
176    return callback.result();
177  }
178
179  int DeleteAllCreatedBetweenForHost(CookieStore* cs,
180                                     const base::Time delete_begin,
181                                     const base::Time delete_end,
182                                     const GURL& url) {
183    DCHECK(cs);
184    ResultSavingCookieCallback<int> callback;
185    cs->DeleteAllCreatedBetweenForHostAsync(
186        delete_begin, delete_end, url,
187        base::Bind(
188            &ResultSavingCookieCallback<int>::Run,
189            base::Unretained(&callback)));
190    RunFor(kTimeout);
191    EXPECT_TRUE(callback.did_run());
192    return callback.result();
193  }
194
195  int DeleteSessionCookies(CookieStore* cs) {
196    DCHECK(cs);
197    ResultSavingCookieCallback<int> callback;
198    cs->DeleteSessionCookiesAsync(
199        base::Bind(
200            &ResultSavingCookieCallback<int>::Run,
201            base::Unretained(&callback)));
202    RunFor(kTimeout);
203    EXPECT_TRUE(callback.did_run());
204    return callback.result();
205  }
206
207  void RunFor(int ms) {
208    // Runs the test thread message loop for up to |ms| milliseconds.
209    base::MessageLoop::current()->PostDelayedTask(
210        FROM_HERE,
211        base::Bind(&base::MessageLoop::Quit, weak_factory_->GetWeakPtr()),
212        base::TimeDelta::FromMilliseconds(ms));
213    base::MessageLoop::current()->Run();
214    weak_factory_->InvalidateWeakPtrs();
215  }
216
217  scoped_refptr<CookieStore> GetCookieStore() {
218    return CookieStoreTestTraits::Create();
219  }
220
221  // Compares two cookie lines.
222  void MatchCookieLines(const std::string& line1, const std::string& line2) {
223    EXPECT_EQ(TokenizeCookieLine(line1), TokenizeCookieLine(line2));
224  }
225
226  // Check the cookie line by polling until equality or a timeout is reached.
227  void MatchCookieLineWithTimeout(CookieStore* cs,
228                                  const GURL& url,
229                                  const std::string& line) {
230    std::string cookies = GetCookies(cs, url);
231    bool matched = (TokenizeCookieLine(line) == TokenizeCookieLine(cookies));
232    base::Time polling_end_date = base::Time::Now() +
233        base::TimeDelta::FromMilliseconds(
234            CookieStoreTestTraits::creation_time_granularity_in_ms);
235
236    while (!matched &&  base::Time::Now() <= polling_end_date) {
237      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
238      cookies = GetCookies(cs, url);
239      matched = (TokenizeCookieLine(line) == TokenizeCookieLine(cookies));
240    }
241
242    EXPECT_TRUE(matched) << "\"" << cookies
243                         << "\" does not match \"" << line << "\"";
244  }
245
246  GURL url_google_;
247  GURL url_google_secure_;
248  GURL url_google_foo_;
249  GURL url_google_bar_;
250
251  scoped_ptr<base::WeakPtrFactory<base::MessageLoop> > weak_factory_;
252  scoped_ptr<base::MessageLoop> message_loop_;
253
254 private:
255  // Returns a set of strings of type "name=value". Fails in case of duplicate.
256  std::set<std::string> TokenizeCookieLine(const std::string& line) {
257    std::set<std::string> tokens;
258    base::StringTokenizer tokenizer(line, " ;");
259    while (tokenizer.GetNext())
260      EXPECT_TRUE(tokens.insert(tokenizer.token()).second);
261    return tokens;
262  }
263};
264
265TYPED_TEST_CASE_P(CookieStoreTest);
266
267TYPED_TEST_P(CookieStoreTest, TypeTest) {
268  scoped_refptr<CookieStore> cs(this->GetCookieStore());
269  EXPECT_EQ(cs->GetCookieMonster(),
270            (TypeParam::is_cookie_monster) ?
271                static_cast<CookieMonster*>(cs.get()) : NULL);
272}
273
274TYPED_TEST_P(CookieStoreTest, DomainTest) {
275  scoped_refptr<CookieStore> cs(this->GetCookieStore());
276  EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, "A=B"));
277  this->MatchCookieLines("A=B", this->GetCookies(cs.get(), this->url_google_));
278  EXPECT_TRUE(this->SetCookie(
279      cs.get(), this->url_google_, "C=D; domain=.google.izzle"));
280  this->MatchCookieLines("A=B; C=D",
281                         this->GetCookies(cs.get(), this->url_google_));
282
283  // Verify that A=B was set as a host cookie rather than a domain
284  // cookie -- should not be accessible from a sub sub-domain.
285  this->MatchCookieLines(
286      "C=D", this->GetCookies(cs.get(), GURL("http://foo.www.google.izzle")));
287
288  // Test and make sure we find domain cookies on the same domain.
289  EXPECT_TRUE(this->SetCookie(
290      cs.get(), this->url_google_, "E=F; domain=.www.google.izzle"));
291  this->MatchCookieLines("A=B; C=D; E=F",
292                         this->GetCookies(cs.get(), this->url_google_));
293
294  // Test setting a domain= that doesn't start w/ a dot, should
295  // treat it as a domain cookie, as if there was a pre-pended dot.
296  EXPECT_TRUE(this->SetCookie(
297      cs.get(), this->url_google_, "G=H; domain=www.google.izzle"));
298  this->MatchCookieLines("A=B; C=D; E=F; G=H",
299                         this->GetCookies(cs.get(), this->url_google_));
300
301  // Test domain enforcement, should fail on a sub-domain or something too deep.
302  EXPECT_FALSE(
303      this->SetCookie(cs.get(), this->url_google_, "I=J; domain=.izzle"));
304  this->MatchCookieLines(std::string(),
305                         this->GetCookies(cs.get(), GURL("http://a.izzle")));
306  EXPECT_FALSE(this->SetCookie(
307      cs.get(), this->url_google_, "K=L; domain=.bla.www.google.izzle"));
308  this->MatchCookieLines(
309      "C=D; E=F; G=H",
310      this->GetCookies(cs.get(), GURL("http://bla.www.google.izzle")));
311  this->MatchCookieLines("A=B; C=D; E=F; G=H",
312                         this->GetCookies(cs.get(), this->url_google_));
313}
314
315// FireFox recognizes domains containing trailing periods as valid.
316// IE and Safari do not. Assert the expected policy here.
317TYPED_TEST_P(CookieStoreTest, DomainWithTrailingDotTest) {
318  scoped_refptr<CookieStore> cs(this->GetCookieStore());
319  EXPECT_FALSE(this->SetCookie(
320      cs.get(), this->url_google_, "a=1; domain=.www.google.com."));
321  EXPECT_FALSE(this->SetCookie(
322      cs.get(), this->url_google_, "b=2; domain=.www.google.com.."));
323  this->MatchCookieLines(std::string(),
324                         this->GetCookies(cs.get(), this->url_google_));
325}
326
327// Test that cookies can bet set on higher level domains.
328// http://b/issue?id=896491
329TYPED_TEST_P(CookieStoreTest, ValidSubdomainTest) {
330  scoped_refptr<CookieStore> cs(this->GetCookieStore());
331  GURL url_abcd("http://a.b.c.d.com");
332  GURL url_bcd("http://b.c.d.com");
333  GURL url_cd("http://c.d.com");
334  GURL url_d("http://d.com");
335
336  EXPECT_TRUE(this->SetCookie(cs.get(), url_abcd, "a=1; domain=.a.b.c.d.com"));
337  EXPECT_TRUE(this->SetCookie(cs.get(), url_abcd, "b=2; domain=.b.c.d.com"));
338  EXPECT_TRUE(this->SetCookie(cs.get(), url_abcd, "c=3; domain=.c.d.com"));
339  EXPECT_TRUE(this->SetCookie(cs.get(), url_abcd, "d=4; domain=.d.com"));
340
341  this->MatchCookieLines("a=1; b=2; c=3; d=4",
342                         this->GetCookies(cs.get(), url_abcd));
343  this->MatchCookieLines("b=2; c=3; d=4", this->GetCookies(cs.get(), url_bcd));
344  this->MatchCookieLines("c=3; d=4", this->GetCookies(cs.get(), url_cd));
345  this->MatchCookieLines("d=4", this->GetCookies(cs.get(), url_d));
346
347  // Check that the same cookie can exist on different sub-domains.
348  EXPECT_TRUE(this->SetCookie(cs.get(), url_bcd, "X=bcd; domain=.b.c.d.com"));
349  EXPECT_TRUE(this->SetCookie(cs.get(), url_bcd, "X=cd; domain=.c.d.com"));
350  this->MatchCookieLines("b=2; c=3; d=4; X=bcd; X=cd",
351                         this->GetCookies(cs.get(), url_bcd));
352  this->MatchCookieLines("c=3; d=4; X=cd", this->GetCookies(cs.get(), url_cd));
353}
354
355// Test that setting a cookie which specifies an invalid domain has
356// no side-effect. An invalid domain in this context is one which does
357// not match the originating domain.
358// http://b/issue?id=896472
359TYPED_TEST_P(CookieStoreTest, InvalidDomainTest) {
360  {
361    scoped_refptr<CookieStore> cs(this->GetCookieStore());
362    GURL url_foobar("http://foo.bar.com");
363
364    // More specific sub-domain than allowed.
365    EXPECT_FALSE(
366        this->SetCookie(cs.get(), url_foobar, "a=1; domain=.yo.foo.bar.com"));
367
368    EXPECT_FALSE(this->SetCookie(cs.get(), url_foobar, "b=2; domain=.foo.com"));
369    EXPECT_FALSE(
370        this->SetCookie(cs.get(), url_foobar, "c=3; domain=.bar.foo.com"));
371
372    // Different TLD, but the rest is a substring.
373    EXPECT_FALSE(
374        this->SetCookie(cs.get(), url_foobar, "d=4; domain=.foo.bar.com.net"));
375
376    // A substring that isn't really a parent domain.
377    EXPECT_FALSE(this->SetCookie(cs.get(), url_foobar, "e=5; domain=ar.com"));
378
379    // Completely invalid domains:
380    EXPECT_FALSE(this->SetCookie(cs.get(), url_foobar, "f=6; domain=."));
381    EXPECT_FALSE(this->SetCookie(cs.get(), url_foobar, "g=7; domain=/"));
382    EXPECT_FALSE(this->SetCookie(
383        cs.get(), url_foobar, "h=8; domain=http://foo.bar.com"));
384    EXPECT_FALSE(
385        this->SetCookie(cs.get(), url_foobar, "i=9; domain=..foo.bar.com"));
386    EXPECT_FALSE(
387        this->SetCookie(cs.get(), url_foobar, "j=10; domain=..bar.com"));
388
389    // Make sure there isn't something quirky in the domain canonicalization
390    // that supports full URL semantics.
391    EXPECT_FALSE(this->SetCookie(
392        cs.get(), url_foobar, "k=11; domain=.foo.bar.com?blah"));
393    EXPECT_FALSE(this->SetCookie(
394        cs.get(), url_foobar, "l=12; domain=.foo.bar.com/blah"));
395    EXPECT_FALSE(
396        this->SetCookie(cs.get(), url_foobar, "m=13; domain=.foo.bar.com:80"));
397    EXPECT_FALSE(
398        this->SetCookie(cs.get(), url_foobar, "n=14; domain=.foo.bar.com:"));
399    EXPECT_FALSE(
400        this->SetCookie(cs.get(), url_foobar, "o=15; domain=.foo.bar.com#sup"));
401
402    this->MatchCookieLines(std::string(),
403                           this->GetCookies(cs.get(), url_foobar));
404  }
405
406  {
407    // Make sure the cookie code hasn't gotten its subdomain string handling
408    // reversed, missed a suffix check, etc.  It's important here that the two
409    // hosts below have the same domain + registry.
410    scoped_refptr<CookieStore> cs(this->GetCookieStore());
411    GURL url_foocom("http://foo.com.com");
412    EXPECT_FALSE(
413        this->SetCookie(cs.get(), url_foocom, "a=1; domain=.foo.com.com.com"));
414    this->MatchCookieLines(std::string(),
415                           this->GetCookies(cs.get(), url_foocom));
416  }
417}
418
419// Test the behavior of omitting dot prefix from domain, should
420// function the same as FireFox.
421// http://b/issue?id=889898
422TYPED_TEST_P(CookieStoreTest, DomainWithoutLeadingDotTest) {
423  {  // The omission of dot results in setting a domain cookie.
424    scoped_refptr<CookieStore> cs(this->GetCookieStore());
425    GURL url_hosted("http://manage.hosted.filefront.com");
426    GURL url_filefront("http://www.filefront.com");
427    EXPECT_TRUE(
428        this->SetCookie(cs.get(), url_hosted, "sawAd=1; domain=filefront.com"));
429    this->MatchCookieLines("sawAd=1", this->GetCookies(cs.get(), url_hosted));
430    this->MatchCookieLines("sawAd=1",
431                           this->GetCookies(cs.get(), url_filefront));
432  }
433
434  {  // Even when the domains match exactly, don't consider it host cookie.
435    scoped_refptr<CookieStore> cs(this->GetCookieStore());
436    GURL url("http://www.google.com");
437    EXPECT_TRUE(this->SetCookie(cs.get(), url, "a=1; domain=www.google.com"));
438    this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url));
439    this->MatchCookieLines(
440        "a=1", this->GetCookies(cs.get(), GURL("http://sub.www.google.com")));
441    this->MatchCookieLines(
442        std::string(),
443        this->GetCookies(cs.get(), GURL("http://something-else.com")));
444  }
445}
446
447// Test that the domain specified in cookie string is treated case-insensitive
448// http://b/issue?id=896475.
449TYPED_TEST_P(CookieStoreTest, CaseInsensitiveDomainTest) {
450    scoped_refptr<CookieStore> cs(this->GetCookieStore());
451  GURL url("http://www.google.com");
452  EXPECT_TRUE(this->SetCookie(cs.get(), url, "a=1; domain=.GOOGLE.COM"));
453  EXPECT_TRUE(this->SetCookie(cs.get(), url, "b=2; domain=.wWw.gOOgLE.coM"));
454  this->MatchCookieLines("a=1; b=2", this->GetCookies(cs.get(), url));
455}
456
457TYPED_TEST_P(CookieStoreTest, TestIpAddress) {
458  GURL url_ip("http://1.2.3.4/weee");
459  {
460    scoped_refptr<CookieStore> cs(this->GetCookieStore());
461    EXPECT_TRUE(this->SetCookie(cs.get(), url_ip, kValidCookieLine));
462    this->MatchCookieLines("A=B", this->GetCookies(cs.get(), url_ip));
463  }
464
465  {  // IP addresses should not be able to set domain cookies.
466    scoped_refptr<CookieStore> cs(this->GetCookieStore());
467    EXPECT_FALSE(this->SetCookie(cs.get(), url_ip, "b=2; domain=.1.2.3.4"));
468    EXPECT_FALSE(this->SetCookie(cs.get(), url_ip, "c=3; domain=.3.4"));
469    this->MatchCookieLines(std::string(), this->GetCookies(cs.get(), url_ip));
470    // It should be allowed to set a cookie if domain= matches the IP address
471    // exactly.  This matches IE/Firefox, even though it seems a bit wrong.
472    EXPECT_FALSE(this->SetCookie(cs.get(), url_ip, "b=2; domain=1.2.3.3"));
473    this->MatchCookieLines(std::string(), this->GetCookies(cs.get(), url_ip));
474    EXPECT_TRUE(this->SetCookie(cs.get(), url_ip, "b=2; domain=1.2.3.4"));
475    this->MatchCookieLines("b=2", this->GetCookies(cs.get(), url_ip));
476  }
477}
478
479// Test host cookies, and setting of cookies on TLD.
480TYPED_TEST_P(CookieStoreTest, TestNonDottedAndTLD) {
481  {
482    scoped_refptr<CookieStore> cs(this->GetCookieStore());
483    GURL url("http://com/");
484    // Allow setting on "com", (but only as a host cookie).
485    EXPECT_TRUE(this->SetCookie(cs.get(), url, "a=1"));
486    EXPECT_FALSE(this->SetCookie(cs.get(), url, "b=2; domain=.com"));
487    EXPECT_FALSE(this->SetCookie(cs.get(), url, "c=3; domain=com"));
488    this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url));
489    // Make sure it doesn't show up for a normal .com, it should be a host
490    // not a domain cookie.
491    this->MatchCookieLines(
492        std::string(),
493        this->GetCookies(cs.get(), GURL("http://hopefully-no-cookies.com/")));
494    if (TypeParam::supports_non_dotted_domains) {
495      this->MatchCookieLines(std::string(),
496                             this->GetCookies(cs.get(), GURL("http://.com/")));
497    }
498  }
499
500  {
501    // http://com. should be treated the same as http://com.
502    scoped_refptr<CookieStore> cs(this->GetCookieStore());
503    GURL url("http://com./index.html");
504    if (TypeParam::supports_trailing_dots) {
505      EXPECT_TRUE(this->SetCookie(cs.get(), url, "a=1"));
506      this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url));
507      this->MatchCookieLines(
508          std::string(),
509          this->GetCookies(cs.get(),
510                           GURL("http://hopefully-no-cookies.com./")));
511    } else {
512      EXPECT_FALSE(this->SetCookie(cs.get(), url, "a=1"));
513    }
514  }
515
516  {  // Should not be able to set host cookie from a subdomain.
517    scoped_refptr<CookieStore> cs(this->GetCookieStore());
518    GURL url("http://a.b");
519    EXPECT_FALSE(this->SetCookie(cs.get(), url, "a=1; domain=.b"));
520    EXPECT_FALSE(this->SetCookie(cs.get(), url, "b=2; domain=b"));
521    this->MatchCookieLines(std::string(), this->GetCookies(cs.get(), url));
522  }
523
524  {  // Same test as above, but explicitly on a known TLD (com).
525    scoped_refptr<CookieStore> cs(this->GetCookieStore());
526    GURL url("http://google.com");
527    EXPECT_FALSE(this->SetCookie(cs.get(), url, "a=1; domain=.com"));
528    EXPECT_FALSE(this->SetCookie(cs.get(), url, "b=2; domain=com"));
529    this->MatchCookieLines(std::string(), this->GetCookies(cs.get(), url));
530  }
531
532  {  // Make sure can't set cookie on TLD which is dotted.
533    scoped_refptr<CookieStore> cs(this->GetCookieStore());
534    GURL url("http://google.co.uk");
535    EXPECT_FALSE(this->SetCookie(cs.get(), url, "a=1; domain=.co.uk"));
536    EXPECT_FALSE(this->SetCookie(cs.get(), url, "b=2; domain=.uk"));
537    this->MatchCookieLines(std::string(), this->GetCookies(cs.get(), url));
538    this->MatchCookieLines(
539        std::string(),
540        this->GetCookies(cs.get(), GURL("http://something-else.co.uk")));
541    this->MatchCookieLines(
542        std::string(),
543        this->GetCookies(cs.get(), GURL("http://something-else.uk")));
544  }
545
546  {  // Intranet URLs should only be able to set host cookies.
547    scoped_refptr<CookieStore> cs(this->GetCookieStore());
548    GURL url("http://b");
549    EXPECT_TRUE(this->SetCookie(cs.get(), url, "a=1"));
550    EXPECT_FALSE(this->SetCookie(cs.get(), url, "b=2; domain=.b"));
551    EXPECT_FALSE(this->SetCookie(cs.get(), url, "c=3; domain=b"));
552    this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url));
553  }
554}
555
556// Test reading/writing cookies when the domain ends with a period,
557// as in "www.google.com."
558TYPED_TEST_P(CookieStoreTest, TestHostEndsWithDot) {
559  scoped_refptr<CookieStore> cs(this->GetCookieStore());
560  GURL url("http://www.google.com");
561  GURL url_with_dot("http://www.google.com.");
562  EXPECT_TRUE(this->SetCookie(cs.get(), url, "a=1"));
563  this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url));
564
565  if (TypeParam::supports_trailing_dots) {
566    // Do not share cookie space with the dot version of domain.
567    // Note: this is not what FireFox does, but it _is_ what IE+Safari do.
568    EXPECT_FALSE(
569        this->SetCookie(cs.get(), url, "b=2; domain=.www.google.com."));
570    this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url));
571
572    EXPECT_TRUE(
573        this->SetCookie(cs.get(), url_with_dot, "b=2; domain=.google.com."));
574    this->MatchCookieLines("b=2", this->GetCookies(cs.get(), url_with_dot));
575  } else {
576    EXPECT_TRUE(this->SetCookie(cs.get(), url, "b=2; domain=.www.google.com."));
577    EXPECT_FALSE(
578        this->SetCookie(cs.get(), url_with_dot, "b=2; domain=.google.com."));
579  }
580
581  // Make sure there weren't any side effects.
582  this->MatchCookieLines(
583      std::string(),
584      this->GetCookies(cs.get(), GURL("http://hopefully-no-cookies.com/")));
585  this->MatchCookieLines(std::string(),
586                         this->GetCookies(cs.get(), GURL("http://.com/")));
587}
588
589TYPED_TEST_P(CookieStoreTest, InvalidScheme) {
590  if (!TypeParam::filters_schemes)
591    return;
592
593  scoped_refptr<CookieStore> cs(this->GetCookieStore());
594  EXPECT_FALSE(this->SetCookie(cs.get(), GURL(kUrlFtp), kValidCookieLine));
595}
596
597TYPED_TEST_P(CookieStoreTest, InvalidScheme_Read) {
598  if (!TypeParam::filters_schemes)
599    return;
600
601  scoped_refptr<CookieStore> cs(this->GetCookieStore());
602  EXPECT_TRUE(
603      this->SetCookie(cs.get(), GURL(kUrlGoogle), kValidDomainCookieLine));
604  this->MatchCookieLines(std::string(),
605                         this->GetCookies(cs.get(), GURL(kUrlFtp)));
606}
607
608TYPED_TEST_P(CookieStoreTest, PathTest) {
609  scoped_refptr<CookieStore> cs(this->GetCookieStore());
610  std::string url("http://www.google.izzle");
611  EXPECT_TRUE(this->SetCookie(cs.get(), GURL(url), "A=B; path=/wee"));
612  this->MatchCookieLines("A=B", this->GetCookies(cs.get(), GURL(url + "/wee")));
613  this->MatchCookieLines("A=B",
614                         this->GetCookies(cs.get(), GURL(url + "/wee/")));
615  this->MatchCookieLines("A=B",
616                         this->GetCookies(cs.get(), GURL(url + "/wee/war")));
617  this->MatchCookieLines(
618      "A=B", this->GetCookies(cs.get(), GURL(url + "/wee/war/more/more")));
619  if (!TypeParam::has_path_prefix_bug)
620    this->MatchCookieLines(std::string(),
621                           this->GetCookies(cs.get(), GURL(url + "/weehee")));
622  this->MatchCookieLines(std::string(),
623                         this->GetCookies(cs.get(), GURL(url + "/")));
624
625  // If we add a 0 length path, it should default to /
626  EXPECT_TRUE(this->SetCookie(cs.get(), GURL(url), "A=C; path="));
627  this->MatchCookieLines("A=B; A=C",
628                         this->GetCookies(cs.get(), GURL(url + "/wee")));
629  this->MatchCookieLines("A=C", this->GetCookies(cs.get(), GURL(url + "/")));
630}
631
632TYPED_TEST_P(CookieStoreTest, EmptyExpires) {
633  scoped_refptr<CookieStore> cs(this->GetCookieStore());
634  CookieOptions options;
635  if (!TypeParam::supports_http_only)
636    options.set_include_httponly();
637  GURL url("http://www7.ipdl.inpit.go.jp/Tokujitu/tjkta.ipdl?N0000=108");
638  std::string set_cookie_line =
639      "ACSTM=20130308043820420042; path=/; domain=ipdl.inpit.go.jp; Expires=";
640  std::string cookie_line = "ACSTM=20130308043820420042";
641
642  this->SetCookieWithOptions(cs.get(), url, set_cookie_line, options);
643  this->MatchCookieLines(cookie_line,
644                         this->GetCookiesWithOptions(cs.get(), url, options));
645
646  options.set_server_time(base::Time::Now() - base::TimeDelta::FromHours(1));
647  this->SetCookieWithOptions(cs.get(), url, set_cookie_line, options);
648  this->MatchCookieLines(cookie_line,
649                         this->GetCookiesWithOptions(cs.get(), url, options));
650
651  options.set_server_time(base::Time::Now() + base::TimeDelta::FromHours(1));
652  this->SetCookieWithOptions(cs.get(), url, set_cookie_line, options);
653  this->MatchCookieLines(cookie_line,
654                         this->GetCookiesWithOptions(cs.get(), url, options));
655}
656
657TYPED_TEST_P(CookieStoreTest, HttpOnlyTest) {
658  if (!TypeParam::supports_http_only)
659    return;
660
661  scoped_refptr<CookieStore> cs(this->GetCookieStore());
662  CookieOptions options;
663  options.set_include_httponly();
664
665  // Create a httponly cookie.
666  EXPECT_TRUE(this->SetCookieWithOptions(
667      cs.get(), this->url_google_, "A=B; httponly", options));
668
669  // Check httponly read protection.
670  this->MatchCookieLines(std::string(),
671                         this->GetCookies(cs.get(), this->url_google_));
672  this->MatchCookieLines(
673      "A=B", this->GetCookiesWithOptions(cs.get(), this->url_google_, options));
674
675  // Check httponly overwrite protection.
676  EXPECT_FALSE(this->SetCookie(cs.get(), this->url_google_, "A=C"));
677  this->MatchCookieLines(std::string(),
678                         this->GetCookies(cs.get(), this->url_google_));
679  this->MatchCookieLines(
680      "A=B", this->GetCookiesWithOptions(cs.get(), this->url_google_, options));
681  EXPECT_TRUE(
682      this->SetCookieWithOptions(cs.get(), this->url_google_, "A=C", options));
683  this->MatchCookieLines("A=C", this->GetCookies(cs.get(), this->url_google_));
684
685  // Check httponly create protection.
686  EXPECT_FALSE(this->SetCookie(cs.get(), this->url_google_, "B=A; httponly"));
687  this->MatchCookieLines(
688      "A=C", this->GetCookiesWithOptions(cs.get(), this->url_google_, options));
689  EXPECT_TRUE(this->SetCookieWithOptions(
690      cs.get(), this->url_google_, "B=A; httponly", options));
691  this->MatchCookieLines(
692      "A=C; B=A",
693      this->GetCookiesWithOptions(cs.get(), this->url_google_, options));
694  this->MatchCookieLines("A=C", this->GetCookies(cs.get(), this->url_google_));
695}
696
697TYPED_TEST_P(CookieStoreTest, TestCookieDeletion) {
698  scoped_refptr<CookieStore> cs(this->GetCookieStore());
699
700  // Create a session cookie.
701  EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, kValidCookieLine));
702  this->MatchCookieLines("A=B", this->GetCookies(cs.get(), this->url_google_));
703  // Delete it via Max-Age.
704  EXPECT_TRUE(this->SetCookie(cs.get(),
705                              this->url_google_,
706                              std::string(kValidCookieLine) + "; max-age=0"));
707  this->MatchCookieLineWithTimeout(cs.get(), this->url_google_, std::string());
708
709  // Create a session cookie.
710  EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, kValidCookieLine));
711  this->MatchCookieLines("A=B", this->GetCookies(cs.get(), this->url_google_));
712  // Delete it via Expires.
713  EXPECT_TRUE(this->SetCookie(cs.get(),
714                              this->url_google_,
715                              std::string(kValidCookieLine) +
716                                  "; expires=Mon, 18-Apr-1977 22:50:13 GMT"));
717  this->MatchCookieLines(std::string(),
718                         this->GetCookies(cs.get(), this->url_google_));
719
720  // Create a persistent cookie.
721  EXPECT_TRUE(this->SetCookie(
722      cs.get(),
723      this->url_google_,
724      std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT"));
725
726  this->MatchCookieLines("A=B", this->GetCookies(cs.get(), this->url_google_));
727  // Delete it via Max-Age.
728  EXPECT_TRUE(this->SetCookie(cs.get(),
729                              this->url_google_,
730                              std::string(kValidCookieLine) + "; max-age=0"));
731  this->MatchCookieLineWithTimeout(cs.get(), this->url_google_, std::string());
732
733  // Create a persistent cookie.
734  EXPECT_TRUE(this->SetCookie(
735      cs.get(),
736      this->url_google_,
737      std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT"));
738  this->MatchCookieLines("A=B", this->GetCookies(cs.get(), this->url_google_));
739  // Delete it via Expires.
740  EXPECT_TRUE(this->SetCookie(cs.get(),
741                              this->url_google_,
742                              std::string(kValidCookieLine) +
743                                  "; expires=Mon, 18-Apr-1977 22:50:13 GMT"));
744  this->MatchCookieLines(std::string(),
745                         this->GetCookies(cs.get(), this->url_google_));
746
747  // Create a persistent cookie.
748  EXPECT_TRUE(this->SetCookie(
749      cs.get(),
750      this->url_google_,
751      std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT"));
752  this->MatchCookieLines("A=B", this->GetCookies(cs.get(), this->url_google_));
753  // Check that it is not deleted with significant enough clock skew.
754  base::Time server_time;
755  EXPECT_TRUE(base::Time::FromString("Sun, 17-Apr-1977 22:50:13 GMT",
756                                     &server_time));
757  EXPECT_TRUE(this->SetCookieWithServerTime(
758      cs.get(),
759      this->url_google_,
760      std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-1977 22:50:13 GMT",
761      server_time));
762  this->MatchCookieLines("A=B", this->GetCookies(cs.get(), this->url_google_));
763
764  // Create a persistent cookie.
765  EXPECT_TRUE(this->SetCookie(
766      cs.get(),
767      this->url_google_,
768      std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT"));
769  this->MatchCookieLines("A=B", this->GetCookies(cs.get(), this->url_google_));
770  // Delete it via Expires, with a unix epoch of 0.
771  EXPECT_TRUE(this->SetCookie(cs.get(),
772                              this->url_google_,
773                              std::string(kValidCookieLine) +
774                                  "; expires=Thu, 1-Jan-1970 00:00:00 GMT"));
775  this->MatchCookieLines(std::string(),
776                         this->GetCookies(cs.get(), this->url_google_));
777}
778
779TYPED_TEST_P(CookieStoreTest, TestDeleteAllCreatedBetween) {
780  scoped_refptr<CookieStore> cs(this->GetCookieStore());
781  const base::Time last_month = base::Time::Now() -
782                                base::TimeDelta::FromDays(30);
783  const base::Time last_minute = base::Time::Now() -
784                                 base::TimeDelta::FromMinutes(1);
785  const base::Time next_minute = base::Time::Now() +
786                                 base::TimeDelta::FromMinutes(1);
787  const base::Time next_month = base::Time::Now() +
788                                base::TimeDelta::FromDays(30);
789
790  // Add a cookie.
791  EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, "A=B"));
792  // Check that the cookie is in the store.
793  this->MatchCookieLines("A=B", this->GetCookies(cs.get(), this->url_google_));
794
795  // Remove cookies in empty intervals.
796  EXPECT_EQ(0, this->DeleteCreatedBetween(cs.get(), last_month, last_minute));
797  EXPECT_EQ(0, this->DeleteCreatedBetween(cs.get(), next_minute, next_month));
798  // Check that the cookie is still there.
799  this->MatchCookieLines("A=B", this->GetCookies(cs.get(), this->url_google_));
800
801  // Remove the cookie with an interval defined by two dates.
802  EXPECT_EQ(1, this->DeleteCreatedBetween(cs.get(), last_minute, next_minute));
803  // Check that the cookie disappeared.
804  this->MatchCookieLines(std::string(),
805                         this->GetCookies(cs.get(), this->url_google_));
806
807  // Add another cookie.
808  EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, "C=D"));
809  // Check that the cookie is in the store.
810  this->MatchCookieLines("C=D", this->GetCookies(cs.get(), this->url_google_));
811
812  // Remove the cookie with a null ending time.
813  EXPECT_EQ(1, this->DeleteCreatedBetween(cs.get(), last_minute, base::Time()));
814  // Check that the cookie disappeared.
815  this->MatchCookieLines(std::string(),
816                         this->GetCookies(cs.get(), this->url_google_));
817}
818
819TYPED_TEST_P(CookieStoreTest, TestDeleteAllCreatedBetweenForHost) {
820  scoped_refptr<CookieStore> cs(this->GetCookieStore());
821  GURL url_not_google("http://www.notgoogle.com");
822  base::Time now = base::Time::Now();
823
824  // These 3 cookies match the time range and host.
825  EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, "A=B"));
826  EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, "C=D"));
827  EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, "Y=Z"));
828
829  // This cookie does not match host.
830  EXPECT_TRUE(this->SetCookie(cs.get(), url_not_google, "E=F"));
831
832  // Delete cookies.
833  EXPECT_EQ(
834      3,  // Deletes A=B, C=D, Y=Z
835      this->DeleteAllCreatedBetweenForHost(
836          cs.get(), now, base::Time::Max(), this->url_google_));
837}
838
839TYPED_TEST_P(CookieStoreTest, TestSecure) {
840    scoped_refptr<CookieStore> cs(this->GetCookieStore());
841
842    EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, "A=B"));
843    this->MatchCookieLines("A=B",
844                           this->GetCookies(cs.get(), this->url_google_));
845    this->MatchCookieLines(
846        "A=B", this->GetCookies(cs.get(), this->url_google_secure_));
847
848  EXPECT_TRUE(
849      this->SetCookie(cs.get(), this->url_google_secure_, "A=B; secure"));
850  // The secure should overwrite the non-secure.
851  this->MatchCookieLines(std::string(),
852                         this->GetCookies(cs.get(), this->url_google_));
853  this->MatchCookieLines("A=B",
854                         this->GetCookies(cs.get(), this->url_google_secure_));
855
856  EXPECT_TRUE(
857      this->SetCookie(cs.get(), this->url_google_secure_, "D=E; secure"));
858  this->MatchCookieLines(std::string(),
859                         this->GetCookies(cs.get(), this->url_google_));
860  this->MatchCookieLines("A=B; D=E",
861                         this->GetCookies(cs.get(), this->url_google_secure_));
862
863  EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_secure_, "A=B"));
864  // The non-secure should overwrite the secure.
865  this->MatchCookieLines("A=B", this->GetCookies(cs.get(), this->url_google_));
866  this->MatchCookieLines("D=E; A=B",
867                         this->GetCookies(cs.get(), this->url_google_secure_));
868}
869
870static const int kLastAccessThresholdMilliseconds = 200;
871
872// Formerly NetUtilTest.CookieTest back when we used wininet's cookie handling.
873TYPED_TEST_P(CookieStoreTest, NetUtilCookieTest) {
874  const GURL test_url("http://mojo.jojo.google.izzle/");
875
876  scoped_refptr<CookieStore> cs(this->GetCookieStore());
877
878  EXPECT_TRUE(this->SetCookie(cs.get(), test_url, "foo=bar"));
879  std::string value = this->GetCookies(cs.get(), test_url);
880  this->MatchCookieLines("foo=bar", value);
881
882  // test that we can retrieve all cookies:
883  EXPECT_TRUE(this->SetCookie(cs.get(), test_url, "x=1"));
884  EXPECT_TRUE(this->SetCookie(cs.get(), test_url, "y=2"));
885
886  std::string result = this->GetCookies(cs.get(), test_url);
887  EXPECT_FALSE(result.empty());
888  EXPECT_NE(result.find("x=1"), std::string::npos) << result;
889  EXPECT_NE(result.find("y=2"), std::string::npos) << result;
890}
891
892TYPED_TEST_P(CookieStoreTest, OverwritePersistentCookie) {
893  GURL url_google("http://www.google.com/");
894  GURL url_chromium("http://chromium.org");
895  scoped_refptr<CookieStore> cs(this->GetCookieStore());
896
897  // Insert a cookie "a" for path "/path1"
898  EXPECT_TRUE(this->SetCookie(cs.get(),
899                              url_google,
900                              "a=val1; path=/path1; "
901                              "expires=Mon, 18-Apr-22 22:50:13 GMT"));
902
903  // Insert a cookie "b" for path "/path1"
904  EXPECT_TRUE(this->SetCookie(cs.get(),
905                              url_google,
906                              "b=val1; path=/path1; "
907                              "expires=Mon, 18-Apr-22 22:50:14 GMT"));
908
909  // Insert a cookie "b" for path "/path1", that is httponly. This should
910  // overwrite the non-http-only version.
911  CookieOptions allow_httponly;
912  allow_httponly.set_include_httponly();
913  EXPECT_TRUE(this->SetCookieWithOptions(cs.get(),
914                                         url_google,
915                                         "b=val2; path=/path1; httponly; "
916                                         "expires=Mon, 18-Apr-22 22:50:14 GMT",
917                                         allow_httponly));
918
919  // Insert a cookie "a" for path "/path1". This should overwrite.
920  EXPECT_TRUE(this->SetCookie(cs.get(),
921                              url_google,
922                              "a=val33; path=/path1; "
923                              "expires=Mon, 18-Apr-22 22:50:14 GMT"));
924
925  // Insert a cookie "a" for path "/path2". This should NOT overwrite
926  // cookie "a", since the path is different.
927  EXPECT_TRUE(this->SetCookie(cs.get(),
928                              url_google,
929                              "a=val9; path=/path2; "
930                              "expires=Mon, 18-Apr-22 22:50:14 GMT"));
931
932  // Insert a cookie "a" for path "/path1", but this time for "chromium.org".
933  // Although the name and path match, the hostnames do not, so shouldn't
934  // overwrite.
935  EXPECT_TRUE(this->SetCookie(cs.get(),
936                              url_chromium,
937                              "a=val99; path=/path1; "
938                              "expires=Mon, 18-Apr-22 22:50:14 GMT"));
939
940  if (TypeParam::supports_http_only) {
941    this->MatchCookieLines(
942        "a=val33",
943        this->GetCookies(cs.get(), GURL("http://www.google.com/path1")));
944  } else {
945    this->MatchCookieLines(
946        "a=val33; b=val2",
947        this->GetCookies(cs.get(), GURL("http://www.google.com/path1")));
948  }
949  this->MatchCookieLines(
950      "a=val9",
951      this->GetCookies(cs.get(), GURL("http://www.google.com/path2")));
952  this->MatchCookieLines(
953      "a=val99", this->GetCookies(cs.get(), GURL("http://chromium.org/path1")));
954}
955
956TYPED_TEST_P(CookieStoreTest, CookieOrdering) {
957  // Put a random set of cookies into a store and make sure they're returned in
958  // the right order.
959  // Cookies should be sorted by path length and creation time, as per RFC6265.
960  scoped_refptr<CookieStore> cs(this->GetCookieStore());
961  EXPECT_TRUE(this->SetCookie(
962      cs.get(), GURL("http://d.c.b.a.google.com/aa/x.html"), "c=1"));
963  EXPECT_TRUE(this->SetCookie(cs.get(),
964                              GURL("http://b.a.google.com/aa/bb/cc/x.html"),
965                              "d=1; domain=b.a.google.com"));
966  base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(
967      TypeParam::creation_time_granularity_in_ms));
968  EXPECT_TRUE(this->SetCookie(cs.get(),
969                              GURL("http://b.a.google.com/aa/bb/cc/x.html"),
970                              "a=4; domain=b.a.google.com"));
971  base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(
972      TypeParam::creation_time_granularity_in_ms));
973  EXPECT_TRUE(this->SetCookie(cs.get(),
974                              GURL("http://c.b.a.google.com/aa/bb/cc/x.html"),
975                              "e=1; domain=c.b.a.google.com"));
976  EXPECT_TRUE(this->SetCookie(
977      cs.get(), GURL("http://d.c.b.a.google.com/aa/bb/x.html"), "b=1"));
978  EXPECT_TRUE(this->SetCookie(
979      cs.get(), GURL("http://news.bbc.co.uk/midpath/x.html"), "g=10"));
980  EXPECT_EQ("d=1; a=4; e=1; b=1; c=1",
981            this->GetCookies(cs.get(),
982                             GURL("http://d.c.b.a.google.com/aa/bb/cc/dd")));
983}
984
985TYPED_TEST_P(CookieStoreTest, DeleteSessionCookie) {
986  scoped_refptr<CookieStore> cs(this->GetCookieStore());
987  // Create a session cookie and a persistent cookie.
988  EXPECT_TRUE(this->SetCookie(
989      cs.get(), this->url_google_, std::string(kValidCookieLine)));
990  EXPECT_TRUE(this->SetCookie(cs.get(),
991                              this->url_google_,
992                              "C=D; path=/; domain=google.izzle;"
993                              "expires=Mon, 18-Apr-22 22:50:13 GMT"));
994  this->MatchCookieLines("A=B; C=D",
995                         this->GetCookies(cs.get(), this->url_google_));
996  // Delete the session cookie.
997  this->DeleteSessionCookies(cs.get());
998  // Check that the session cookie has been deleted but not the persistent one.
999  EXPECT_EQ("C=D", this->GetCookies(cs.get(), this->url_google_));
1000}
1001
1002REGISTER_TYPED_TEST_CASE_P(CookieStoreTest,
1003                           TypeTest,
1004                           DomainTest,
1005                           DomainWithTrailingDotTest,
1006                           ValidSubdomainTest,
1007                           InvalidDomainTest,
1008                           DomainWithoutLeadingDotTest,
1009                           CaseInsensitiveDomainTest,
1010                           TestIpAddress,
1011                           TestNonDottedAndTLD,
1012                           TestHostEndsWithDot,
1013                           InvalidScheme,
1014                           InvalidScheme_Read,
1015                           PathTest,
1016                           EmptyExpires,
1017                           HttpOnlyTest,
1018                           TestCookieDeletion,
1019                           TestDeleteAllCreatedBetween,
1020                           TestDeleteAllCreatedBetweenForHost,
1021                           TestSecure,
1022                           NetUtilCookieTest,
1023                           OverwritePersistentCookie,
1024                           CookieOrdering,
1025                           DeleteSessionCookie);
1026
1027template<class CookieStoreTestTraits>
1028class MultiThreadedCookieStoreTest :
1029    public CookieStoreTest<CookieStoreTestTraits> {
1030 public:
1031  MultiThreadedCookieStoreTest() : other_thread_("CMTthread") {}
1032
1033  // Helper methods for calling the asynchronous CookieStore methods
1034  // from a different thread.
1035
1036  void GetCookiesTask(CookieStore* cs,
1037                      const GURL& url,
1038                      StringResultCookieCallback* callback) {
1039    CookieOptions options;
1040    if (!CookieStoreTestTraits::supports_http_only)
1041      options.set_include_httponly();
1042    cs->GetCookiesWithOptionsAsync(
1043        url, options,
1044        base::Bind(&StringResultCookieCallback::Run,
1045                   base::Unretained(callback)));
1046  }
1047
1048  void GetCookiesWithOptionsTask(CookieStore* cs,
1049                                 const GURL& url,
1050                                 const CookieOptions& options,
1051                                 StringResultCookieCallback* callback) {
1052    cs->GetCookiesWithOptionsAsync(
1053        url, options,
1054        base::Bind(&StringResultCookieCallback::Run,
1055                   base::Unretained(callback)));
1056  }
1057
1058  void SetCookieWithOptionsTask(CookieStore* cs,
1059                                const GURL& url,
1060                                const std::string& cookie_line,
1061                                const CookieOptions& options,
1062                                ResultSavingCookieCallback<bool>* callback) {
1063    cs->SetCookieWithOptionsAsync(
1064        url, cookie_line, options,
1065        base::Bind(
1066            &ResultSavingCookieCallback<bool>::Run,
1067            base::Unretained(callback)));
1068  }
1069
1070  void DeleteCookieTask(CookieStore* cs,
1071                        const GURL& url,
1072                        const std::string& cookie_name,
1073                        NoResultCookieCallback* callback) {
1074    cs->DeleteCookieAsync(
1075        url, cookie_name,
1076        base::Bind(&NoResultCookieCallback::Run, base::Unretained(callback)));
1077  }
1078
1079    void DeleteSessionCookiesTask(CookieStore* cs,
1080                                  ResultSavingCookieCallback<int>* callback) {
1081    cs->DeleteSessionCookiesAsync(
1082        base::Bind(
1083            &ResultSavingCookieCallback<int>::Run,
1084            base::Unretained(callback)));
1085  }
1086
1087 protected:
1088  void RunOnOtherThread(const base::Closure& task) {
1089    other_thread_.Start();
1090    other_thread_.message_loop()->PostTask(FROM_HERE, task);
1091    CookieStoreTest<CookieStoreTestTraits>::RunFor(kTimeout);
1092    other_thread_.Stop();
1093  }
1094
1095  Thread other_thread_;
1096};
1097
1098TYPED_TEST_CASE_P(MultiThreadedCookieStoreTest);
1099
1100// TODO(ycxiao): Eventually, we will need to create a separate thread, create
1101// the cookie store on that thread (or at least its store, i.e., the DB
1102// thread).
1103TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckGetCookies) {
1104  scoped_refptr<CookieStore> cs(this->GetCookieStore());
1105  EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, "A=B"));
1106  this->MatchCookieLines("A=B", this->GetCookies(cs.get(), this->url_google_));
1107  StringResultCookieCallback callback(&this->other_thread_);
1108  base::Closure task = base::Bind(
1109      &net::MultiThreadedCookieStoreTest<TypeParam>::GetCookiesTask,
1110      base::Unretained(this),
1111      cs, this->url_google_, &callback);
1112  this->RunOnOtherThread(task);
1113  EXPECT_TRUE(callback.did_run());
1114  EXPECT_EQ("A=B", callback.result());
1115}
1116
1117TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckGetCookiesWithOptions) {
1118  scoped_refptr<CookieStore> cs(this->GetCookieStore());
1119  CookieOptions options;
1120  if (!TypeParam::supports_http_only)
1121    options.set_include_httponly();
1122  EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, "A=B"));
1123  this->MatchCookieLines(
1124      "A=B", this->GetCookiesWithOptions(cs.get(), this->url_google_, options));
1125  StringResultCookieCallback callback(&this->other_thread_);
1126  base::Closure task = base::Bind(
1127      &net::MultiThreadedCookieStoreTest<TypeParam>::GetCookiesWithOptionsTask,
1128      base::Unretained(this),
1129      cs, this->url_google_, options, &callback);
1130  this->RunOnOtherThread(task);
1131  EXPECT_TRUE(callback.did_run());
1132  EXPECT_EQ("A=B", callback.result());
1133}
1134
1135TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckSetCookieWithOptions) {
1136  scoped_refptr<CookieStore> cs(this->GetCookieStore());
1137  CookieOptions options;
1138  if (!TypeParam::supports_http_only)
1139    options.set_include_httponly();
1140  EXPECT_TRUE(
1141      this->SetCookieWithOptions(cs.get(), this->url_google_, "A=B", options));
1142  ResultSavingCookieCallback<bool> callback(&this->other_thread_);
1143  base::Closure task = base::Bind(
1144      &net::MultiThreadedCookieStoreTest<TypeParam>::SetCookieWithOptionsTask,
1145      base::Unretained(this),
1146      cs, this->url_google_, "A=B", options, &callback);
1147  this->RunOnOtherThread(task);
1148  EXPECT_TRUE(callback.did_run());
1149  EXPECT_TRUE(callback.result());
1150}
1151
1152TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckDeleteCookie) {
1153  scoped_refptr<CookieStore> cs(this->GetCookieStore());
1154  CookieOptions options;
1155  if (!TypeParam::supports_http_only)
1156    options.set_include_httponly();
1157  EXPECT_TRUE(
1158      this->SetCookieWithOptions(cs.get(), this->url_google_, "A=B", options));
1159  this->DeleteCookie(cs.get(), this->url_google_, "A");
1160  EXPECT_TRUE(
1161      this->SetCookieWithOptions(cs.get(), this->url_google_, "A=B", options));
1162  NoResultCookieCallback callback(&this->other_thread_);
1163  base::Closure task = base::Bind(
1164      &net::MultiThreadedCookieStoreTest<TypeParam>::DeleteCookieTask,
1165      base::Unretained(this),
1166      cs, this->url_google_, "A", &callback);
1167  this->RunOnOtherThread(task);
1168  EXPECT_TRUE(callback.did_run());
1169}
1170
1171TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckDeleteSessionCookies) {
1172  scoped_refptr<CookieStore> cs(this->GetCookieStore());
1173  CookieOptions options;
1174  if (!TypeParam::supports_http_only)
1175    options.set_include_httponly();
1176  EXPECT_TRUE(
1177      this->SetCookieWithOptions(cs.get(), this->url_google_, "A=B", options));
1178  EXPECT_TRUE(
1179      this->SetCookieWithOptions(cs.get(),
1180                                 this->url_google_,
1181                                 "B=C; expires=Mon, 18-Apr-22 22:50:13 GMT",
1182                                 options));
1183  EXPECT_EQ(1, this->DeleteSessionCookies(cs.get()));
1184  EXPECT_EQ(0, this->DeleteSessionCookies(cs.get()));
1185  EXPECT_TRUE(
1186      this->SetCookieWithOptions(cs.get(), this->url_google_, "A=B", options));
1187  ResultSavingCookieCallback<int> callback(&this->other_thread_);
1188  base::Closure task = base::Bind(
1189      &net::MultiThreadedCookieStoreTest<TypeParam>::DeleteSessionCookiesTask,
1190      base::Unretained(this),
1191      cs, &callback);
1192  this->RunOnOtherThread(task);
1193  EXPECT_TRUE(callback.did_run());
1194  EXPECT_EQ(1, callback.result());
1195}
1196
1197REGISTER_TYPED_TEST_CASE_P(MultiThreadedCookieStoreTest,
1198                           ThreadCheckGetCookies,
1199                           ThreadCheckGetCookiesWithOptions,
1200                           ThreadCheckSetCookieWithOptions,
1201                           ThreadCheckDeleteCookie,
1202                           ThreadCheckDeleteSessionCookies);
1203
1204}  // namespace net
1205
1206#endif  // NET_COOKIES_COOKIE_STORE_UNITTEST_H_
1207