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 "utility" 8 9 #include "base/bind.h" 10 #include "base/logging.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/stl_util.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "content/public/browser/browser_thread.h" 15 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 16 #include "net/cookies/canonical_cookie.h" 17 #include "net/cookies/cookie_util.h" 18 #include "net/cookies/parsed_cookie.h" 19 #include "net/url_request/url_request_context.h" 20 #include "net/url_request/url_request_context_getter.h" 21 #include "url/gurl.h" 22 23 using content::BrowserThread; 24 25 namespace { 26 const char kGlobalCookieListURL[] = "chrome://cookielist"; 27 } 28 29 BrowsingDataCookieHelper::BrowsingDataCookieHelper( 30 net::URLRequestContextGetter* request_context_getter) 31 : is_fetching_(false), 32 request_context_getter_(request_context_getter) { 33 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 34 } 35 36 BrowsingDataCookieHelper::~BrowsingDataCookieHelper() { 37 } 38 39 void BrowsingDataCookieHelper::StartFetching( 40 const base::Callback<void(const net::CookieList& cookies)>& callback) { 41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 42 DCHECK(!is_fetching_); 43 DCHECK(!callback.is_null()); 44 DCHECK(completion_callback_.is_null()); 45 is_fetching_ = true; 46 completion_callback_ = callback; 47 BrowserThread::PostTask( 48 BrowserThread::IO, FROM_HERE, 49 base::Bind(&BrowsingDataCookieHelper::FetchCookiesOnIOThread, this)); 50 } 51 52 void BrowsingDataCookieHelper::DeleteCookie( 53 const net::CanonicalCookie& cookie) { 54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 55 BrowserThread::PostTask( 56 BrowserThread::IO, FROM_HERE, 57 base::Bind(&BrowsingDataCookieHelper::DeleteCookieOnIOThread, 58 this, cookie)); 59 } 60 61 void BrowsingDataCookieHelper::FetchCookiesOnIOThread() { 62 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 63 scoped_refptr<net::CookieMonster> cookie_monster = 64 request_context_getter_->GetURLRequestContext()-> 65 cookie_store()->GetCookieMonster(); 66 if (cookie_monster.get()) { 67 cookie_monster->GetAllCookiesAsync( 68 base::Bind(&BrowsingDataCookieHelper::OnFetchComplete, this)); 69 } else { 70 OnFetchComplete(net::CookieList()); 71 } 72 } 73 74 void BrowsingDataCookieHelper::OnFetchComplete(const net::CookieList& cookies) { 75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 76 BrowserThread::PostTask( 77 BrowserThread::UI, FROM_HERE, 78 base::Bind(&BrowsingDataCookieHelper::NotifyInUIThread, this, cookies)); 79 } 80 81 void BrowsingDataCookieHelper::NotifyInUIThread( 82 const net::CookieList& cookies) { 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 84 DCHECK(is_fetching_); 85 is_fetching_ = false; 86 completion_callback_.Run(cookies); 87 completion_callback_.Reset(); 88 } 89 90 void BrowsingDataCookieHelper::DeleteCookieOnIOThread( 91 const net::CanonicalCookie& cookie) { 92 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 93 scoped_refptr<net::CookieMonster> cookie_monster = 94 request_context_getter_->GetURLRequestContext()-> 95 cookie_store()->GetCookieMonster(); 96 if (cookie_monster.get()) { 97 cookie_monster->DeleteCanonicalCookieAsync( 98 cookie, net::CookieMonster::DeleteCookieCallback()); 99 } 100 } 101 102 CannedBrowsingDataCookieHelper::CannedBrowsingDataCookieHelper( 103 net::URLRequestContextGetter* request_context_getter) 104 : BrowsingDataCookieHelper(request_context_getter) { 105 } 106 107 CannedBrowsingDataCookieHelper::~CannedBrowsingDataCookieHelper() { 108 Reset(); 109 } 110 111 CannedBrowsingDataCookieHelper* CannedBrowsingDataCookieHelper::Clone() { 112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 113 CannedBrowsingDataCookieHelper* clone = 114 new CannedBrowsingDataCookieHelper(request_context_getter()); 115 116 for (OriginCookieListMap::iterator it = origin_cookie_list_map_.begin(); 117 it != origin_cookie_list_map_.end(); 118 ++it) { 119 net::CookieList* cookies = clone->GetCookiesFor(it->first); 120 cookies->insert(cookies->begin(), it->second->begin(), it->second->end()); 121 } 122 return clone; 123 } 124 125 void CannedBrowsingDataCookieHelper::AddReadCookies( 126 const GURL& frame_url, 127 const GURL& url, 128 const net::CookieList& cookie_list) { 129 typedef net::CookieList::const_iterator cookie_iterator; 130 for (cookie_iterator add_cookie = cookie_list.begin(); 131 add_cookie != cookie_list.end(); ++add_cookie) { 132 AddCookie(frame_url, *add_cookie); 133 } 134 } 135 136 void CannedBrowsingDataCookieHelper::AddChangedCookie( 137 const GURL& frame_url, 138 const GURL& url, 139 const std::string& cookie_line, 140 const net::CookieOptions& options) { 141 scoped_ptr<net::CanonicalCookie> cookie(net::CanonicalCookie::Create( 142 url, cookie_line, base::Time::Now(), options)); 143 if (cookie.get()) 144 AddCookie(frame_url, *cookie); 145 } 146 147 void CannedBrowsingDataCookieHelper::Reset() { 148 STLDeleteContainerPairSecondPointers(origin_cookie_list_map_.begin(), 149 origin_cookie_list_map_.end()); 150 origin_cookie_list_map_.clear(); 151 } 152 153 bool CannedBrowsingDataCookieHelper::empty() const { 154 for (OriginCookieListMap::const_iterator it = 155 origin_cookie_list_map_.begin(); 156 it != origin_cookie_list_map_.end(); 157 ++it) { 158 if (!it->second->empty()) 159 return false; 160 } 161 return true; 162 } 163 164 165 size_t CannedBrowsingDataCookieHelper::GetCookieCount() const { 166 size_t count = 0; 167 for (OriginCookieListMap::const_iterator it = origin_cookie_list_map_.begin(); 168 it != origin_cookie_list_map_.end(); 169 ++it) { 170 count += it->second->size(); 171 } 172 return count; 173 } 174 175 176 void CannedBrowsingDataCookieHelper::StartFetching( 177 const net::CookieMonster::GetCookieListCallback& callback) { 178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 179 net::CookieList cookie_list; 180 for (OriginCookieListMap::iterator it = origin_cookie_list_map_.begin(); 181 it != origin_cookie_list_map_.end(); 182 ++it) { 183 cookie_list.insert(cookie_list.begin(), 184 it->second->begin(), 185 it->second->end()); 186 } 187 callback.Run(cookie_list); 188 } 189 190 bool CannedBrowsingDataCookieHelper::DeleteMatchingCookie( 191 const net::CanonicalCookie& add_cookie, 192 net::CookieList* cookie_list) { 193 typedef net::CookieList::iterator cookie_iterator; 194 for (cookie_iterator cookie = cookie_list->begin(); 195 cookie != cookie_list->end(); ++cookie) { 196 if (cookie->Name() == add_cookie.Name() && 197 cookie->Domain() == add_cookie.Domain()&& 198 cookie->Path() == add_cookie.Path()) { 199 cookie_list->erase(cookie); 200 return true; 201 } 202 } 203 return false; 204 } 205 206 net::CookieList* CannedBrowsingDataCookieHelper::GetCookiesFor( 207 const GURL& first_party_origin) { 208 OriginCookieListMap::iterator it = 209 origin_cookie_list_map_.find(first_party_origin); 210 if (it == origin_cookie_list_map_.end()) { 211 net::CookieList* cookies = new net::CookieList(); 212 origin_cookie_list_map_.insert( 213 std::pair<GURL, net::CookieList*>(first_party_origin, cookies)); 214 return cookies; 215 } 216 return it->second; 217 } 218 219 void CannedBrowsingDataCookieHelper::AddCookie( 220 const GURL& frame_url, 221 const net::CanonicalCookie& cookie) { 222 // Storing cookies in separate cookie lists per frame origin makes the 223 // GetCookieCount method count a cookie multiple times if it is stored in 224 // multiple lists. 225 // E.g. let "example.com" be redirected to "www.example.com". A cookie set 226 // with the cookie string "A=B; Domain=.example.com" would be sent to both 227 // hosts. This means it would be stored in the separate cookie lists for both 228 // hosts ("example.com", "www.example.com"). The method GetCookieCount would 229 // count this cookie twice. To prevent this, we us a single global cookie 230 // list as a work-around to store all added cookies. Per frame URL cookie 231 // lists are currently not used. In the future they will be used for 232 // collecting cookies per origin in redirect chains. 233 // TODO(markusheintz): A) Change the GetCookiesCount method to prevent 234 // counting cookies multiple times if they are stored in multiple cookie 235 // lists. B) Replace the GetCookieFor method call below with: 236 // "GetCookiesFor(frame_url.GetOrigin());" 237 net::CookieList* cookie_list = 238 GetCookiesFor(GURL(kGlobalCookieListURL)); 239 DeleteMatchingCookie(cookie, cookie_list); 240 cookie_list->push_back(cookie); 241 } 242