// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/browsing_data/browsing_data_cookie_helper.h" #include "utility" #include "base/bind.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/stl_util.h" #include "chrome/browser/profiles/profile.h" #include "content/public/browser/browser_thread.h" #include "googleurl/src/gurl.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/cookies/canonical_cookie.h" #include "net/cookies/cookie_util.h" #include "net/cookies/parsed_cookie.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" using content::BrowserThread; namespace { const char kGlobalCookieListURL[] = "chrome://cookielist"; } BrowsingDataCookieHelper::BrowsingDataCookieHelper( net::URLRequestContextGetter* request_context_getter) : is_fetching_(false), request_context_getter_(request_context_getter) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } BrowsingDataCookieHelper::~BrowsingDataCookieHelper() { } void BrowsingDataCookieHelper::StartFetching( const base::Callback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(!is_fetching_); DCHECK(!callback.is_null()); DCHECK(completion_callback_.is_null()); is_fetching_ = true; completion_callback_ = callback; BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&BrowsingDataCookieHelper::FetchCookiesOnIOThread, this)); } void BrowsingDataCookieHelper::DeleteCookie( const net::CanonicalCookie& cookie) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&BrowsingDataCookieHelper::DeleteCookieOnIOThread, this, cookie)); } void BrowsingDataCookieHelper::FetchCookiesOnIOThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); scoped_refptr cookie_monster = request_context_getter_->GetURLRequestContext()-> cookie_store()->GetCookieMonster(); if (cookie_monster.get()) { cookie_monster->GetAllCookiesAsync( base::Bind(&BrowsingDataCookieHelper::OnFetchComplete, this)); } else { OnFetchComplete(net::CookieList()); } } void BrowsingDataCookieHelper::OnFetchComplete(const net::CookieList& cookies) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&BrowsingDataCookieHelper::NotifyInUIThread, this, cookies)); } void BrowsingDataCookieHelper::NotifyInUIThread( const net::CookieList& cookies) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(is_fetching_); is_fetching_ = false; completion_callback_.Run(cookies); completion_callback_.Reset(); } void BrowsingDataCookieHelper::DeleteCookieOnIOThread( const net::CanonicalCookie& cookie) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); scoped_refptr cookie_monster = request_context_getter_->GetURLRequestContext()-> cookie_store()->GetCookieMonster(); if (cookie_monster.get()) { cookie_monster->DeleteCanonicalCookieAsync( cookie, net::CookieMonster::DeleteCookieCallback()); } } CannedBrowsingDataCookieHelper::CannedBrowsingDataCookieHelper( net::URLRequestContextGetter* request_context_getter) : BrowsingDataCookieHelper(request_context_getter) { } CannedBrowsingDataCookieHelper::~CannedBrowsingDataCookieHelper() { Reset(); } CannedBrowsingDataCookieHelper* CannedBrowsingDataCookieHelper::Clone() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CannedBrowsingDataCookieHelper* clone = new CannedBrowsingDataCookieHelper(request_context_getter()); for (OriginCookieListMap::iterator it = origin_cookie_list_map_.begin(); it != origin_cookie_list_map_.end(); ++it) { net::CookieList* cookies = clone->GetCookiesFor(it->first); cookies->insert(cookies->begin(), it->second->begin(), it->second->end()); } return clone; } void CannedBrowsingDataCookieHelper::AddReadCookies( const GURL& frame_url, const GURL& url, const net::CookieList& cookie_list) { typedef net::CookieList::const_iterator cookie_iterator; for (cookie_iterator add_cookie = cookie_list.begin(); add_cookie != cookie_list.end(); ++add_cookie) { AddCookie(frame_url, *add_cookie); } } void CannedBrowsingDataCookieHelper::AddChangedCookie( const GURL& frame_url, const GURL& url, const std::string& cookie_line, const net::CookieOptions& options) { scoped_ptr cookie(net::CanonicalCookie::Create( url, cookie_line, base::Time::Now(), options)); if (cookie.get()) AddCookie(frame_url, *cookie); } void CannedBrowsingDataCookieHelper::Reset() { STLDeleteContainerPairSecondPointers(origin_cookie_list_map_.begin(), origin_cookie_list_map_.end()); origin_cookie_list_map_.clear(); } bool CannedBrowsingDataCookieHelper::empty() const { for (OriginCookieListMap::const_iterator it = origin_cookie_list_map_.begin(); it != origin_cookie_list_map_.end(); ++it) { if (!it->second->empty()) return false; } return true; } size_t CannedBrowsingDataCookieHelper::GetCookieCount() const { size_t count = 0; for (OriginCookieListMap::const_iterator it = origin_cookie_list_map_.begin(); it != origin_cookie_list_map_.end(); ++it) { count += it->second->size(); } return count; } void CannedBrowsingDataCookieHelper::StartFetching( const net::CookieMonster::GetCookieListCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); net::CookieList cookie_list; for (OriginCookieListMap::iterator it = origin_cookie_list_map_.begin(); it != origin_cookie_list_map_.end(); ++it) { cookie_list.insert(cookie_list.begin(), it->second->begin(), it->second->end()); } callback.Run(cookie_list); } bool CannedBrowsingDataCookieHelper::DeleteMatchingCookie( const net::CanonicalCookie& add_cookie, net::CookieList* cookie_list) { typedef net::CookieList::iterator cookie_iterator; for (cookie_iterator cookie = cookie_list->begin(); cookie != cookie_list->end(); ++cookie) { if (cookie->Name() == add_cookie.Name() && cookie->Domain() == add_cookie.Domain()&& cookie->Path() == add_cookie.Path()) { cookie_list->erase(cookie); return true; } } return false; } net::CookieList* CannedBrowsingDataCookieHelper::GetCookiesFor( const GURL& first_party_origin) { OriginCookieListMap::iterator it = origin_cookie_list_map_.find(first_party_origin); if (it == origin_cookie_list_map_.end()) { net::CookieList* cookies = new net::CookieList(); origin_cookie_list_map_.insert( std::pair(first_party_origin, cookies)); return cookies; } return it->second; } void CannedBrowsingDataCookieHelper::AddCookie( const GURL& frame_url, const net::CanonicalCookie& cookie) { // Storing cookies in separate cookie lists per frame origin makes the // GetCookieCount method count a cookie multiple times if it is stored in // multiple lists. // E.g. let "example.com" be redirected to "www.example.com". A cookie set // with the cookie string "A=B; Domain=.example.com" would be sent to both // hosts. This means it would be stored in the separate cookie lists for both // hosts ("example.com", "www.example.com"). The method GetCookieCount would // count this cookie twice. To prevent this, we us a single global cookie // list as a work-around to store all added cookies. Per frame URL cookie // lists are currently not used. In the future they will be used for // collecting cookies per origin in redirect chains. // TODO(markusheintz): A) Change the GetCookiesCount method to prevent // counting cookies multiple times if they are stored in multiple cookie // lists. B) Replace the GetCookieFor method call below with: // "GetCookiesFor(frame_url.GetOrigin());" net::CookieList* cookie_list = GetCookiesFor(GURL(kGlobalCookieListURL)); DeleteMatchingCookie(cookie, cookie_list); cookie_list->push_back(cookie); }