Home | History | Annotate | Download | only in cookies
      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 // Implements the Chrome Extensions Cookies API.
      6 
      7 #include "chrome/browser/extensions/api/cookies/cookies_api.h"
      8 
      9 #include <vector>
     10 
     11 #include "base/bind.h"
     12 #include "base/json/json_writer.h"
     13 #include "base/lazy_instance.h"
     14 #include "base/memory/linked_ptr.h"
     15 #include "base/memory/scoped_ptr.h"
     16 #include "base/time/time.h"
     17 #include "base/values.h"
     18 #include "chrome/browser/chrome_notification_types.h"
     19 #include "chrome/browser/extensions/api/cookies/cookies_api_constants.h"
     20 #include "chrome/browser/extensions/api/cookies/cookies_helpers.h"
     21 #include "chrome/browser/extensions/event_router.h"
     22 #include "chrome/browser/extensions/extension_system.h"
     23 #include "chrome/browser/profiles/profile.h"
     24 #include "chrome/browser/ui/browser.h"
     25 #include "chrome/browser/ui/browser_iterator.h"
     26 #include "chrome/common/extensions/api/cookies.h"
     27 #include "chrome/common/extensions/extension.h"
     28 #include "chrome/common/extensions/permissions/permissions_data.h"
     29 #include "content/public/browser/browser_thread.h"
     30 #include "content/public/browser/notification_service.h"
     31 #include "extensions/common/error_utils.h"
     32 #include "net/cookies/canonical_cookie.h"
     33 #include "net/cookies/cookie_constants.h"
     34 #include "net/cookies/cookie_monster.h"
     35 #include "net/url_request/url_request_context.h"
     36 #include "net/url_request/url_request_context_getter.h"
     37 
     38 using content::BrowserThread;
     39 using extensions::api::cookies::Cookie;
     40 using extensions::api::cookies::CookieStore;
     41 
     42 namespace Get = extensions::api::cookies::Get;
     43 namespace GetAll = extensions::api::cookies::GetAll;
     44 namespace GetAllCookieStores = extensions::api::cookies::GetAllCookieStores;
     45 namespace Remove = extensions::api::cookies::Remove;
     46 namespace Set = extensions::api::cookies::Set;
     47 
     48 namespace extensions {
     49 namespace keys = cookies_api_constants;
     50 
     51 CookiesEventRouter::CookiesEventRouter(Profile* profile)
     52     : profile_(profile) {
     53   CHECK(registrar_.IsEmpty());
     54   registrar_.Add(this,
     55                  chrome::NOTIFICATION_COOKIE_CHANGED,
     56                  content::NotificationService::AllBrowserContextsAndSources());
     57 }
     58 
     59 CookiesEventRouter::~CookiesEventRouter() {
     60 }
     61 
     62 void CookiesEventRouter::Observe(
     63     int type,
     64     const content::NotificationSource& source,
     65     const content::NotificationDetails& details) {
     66   Profile* profile = content::Source<Profile>(source).ptr();
     67   if (!profile_->IsSameProfile(profile))
     68     return;
     69 
     70   switch (type) {
     71     case chrome::NOTIFICATION_COOKIE_CHANGED:
     72       CookieChanged(
     73           profile,
     74           content::Details<ChromeCookieDetails>(details).ptr());
     75       break;
     76 
     77     default:
     78       NOTREACHED();
     79   }
     80 }
     81 
     82 void CookiesEventRouter::CookieChanged(
     83     Profile* profile,
     84     ChromeCookieDetails* details) {
     85   scoped_ptr<base::ListValue> args(new base::ListValue());
     86   base::DictionaryValue* dict = new base::DictionaryValue();
     87   dict->SetBoolean(keys::kRemovedKey, details->removed);
     88 
     89   scoped_ptr<Cookie> cookie(
     90       cookies_helpers::CreateCookie(*details->cookie,
     91           cookies_helpers::GetStoreIdFromProfile(profile_)));
     92   dict->Set(keys::kCookieKey, cookie->ToValue().release());
     93 
     94   // Map the internal cause to an external string.
     95   std::string cause;
     96   switch (details->cause) {
     97     case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT:
     98       cause = keys::kExplicitChangeCause;
     99       break;
    100 
    101     case net::CookieMonster::Delegate::CHANGE_COOKIE_OVERWRITE:
    102       cause = keys::kOverwriteChangeCause;
    103       break;
    104 
    105     case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED:
    106       cause = keys::kExpiredChangeCause;
    107       break;
    108 
    109     case net::CookieMonster::Delegate::CHANGE_COOKIE_EVICTED:
    110       cause = keys::kEvictedChangeCause;
    111       break;
    112 
    113     case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED_OVERWRITE:
    114       cause = keys::kExpiredOverwriteChangeCause;
    115       break;
    116 
    117     default:
    118       NOTREACHED();
    119   }
    120   dict->SetString(keys::kCauseKey, cause);
    121 
    122   args->Append(dict);
    123 
    124   GURL cookie_domain =
    125       cookies_helpers::GetURLFromCanonicalCookie(*details->cookie);
    126   DispatchEvent(profile, keys::kOnChanged, args.Pass(), cookie_domain);
    127 }
    128 
    129 void CookiesEventRouter::DispatchEvent(
    130     Profile* profile,
    131     const std::string& event_name,
    132     scoped_ptr<base::ListValue> event_args,
    133     GURL& cookie_domain) {
    134   EventRouter* router = profile ?
    135       extensions::ExtensionSystem::Get(profile)->event_router() : NULL;
    136   if (!router)
    137     return;
    138   scoped_ptr<Event> event(new Event(event_name, event_args.Pass()));
    139   event->restrict_to_profile = profile;
    140   event->event_url = cookie_domain;
    141   router->BroadcastEvent(event.Pass());
    142 }
    143 
    144 bool CookiesFunction::ParseUrl(const std::string& url_string, GURL* url,
    145                                bool check_host_permissions) {
    146   *url = GURL(url_string);
    147   if (!url->is_valid()) {
    148     error_ = ErrorUtils::FormatErrorMessage(
    149         keys::kInvalidUrlError, url_string);
    150     return false;
    151   }
    152   // Check against host permissions if needed.
    153   if (check_host_permissions &&
    154       !PermissionsData::HasHostPermission(GetExtension(), *url)) {
    155     error_ = ErrorUtils::FormatErrorMessage(
    156         keys::kNoHostPermissionsError, url->spec());
    157     return false;
    158   }
    159   return true;
    160 }
    161 
    162 bool CookiesFunction::ParseStoreContext(
    163     std::string* store_id,
    164     net::URLRequestContextGetter** context) {
    165   DCHECK((context || store_id->empty()));
    166   Profile* store_profile = NULL;
    167   if (!store_id->empty()) {
    168     store_profile = cookies_helpers::ChooseProfileFromStoreId(
    169         *store_id, profile(), include_incognito());
    170     if (!store_profile) {
    171       error_ = ErrorUtils::FormatErrorMessage(
    172           keys::kInvalidStoreIdError, *store_id);
    173       return false;
    174     }
    175   } else {
    176     // The store ID was not specified; use the current execution context's
    177     // cookie store by default.
    178     // GetCurrentBrowser() already takes into account incognito settings.
    179     Browser* current_browser = GetCurrentBrowser();
    180     if (!current_browser) {
    181       error_ = keys::kNoCookieStoreFoundError;
    182       return false;
    183     }
    184     store_profile = current_browser->profile();
    185     *store_id = cookies_helpers::GetStoreIdFromProfile(store_profile);
    186   }
    187 
    188   if (context)
    189     *context = store_profile->GetRequestContext();
    190   DCHECK(context);
    191 
    192   return true;
    193 }
    194 
    195 CookiesGetFunction::CookiesGetFunction() {
    196 }
    197 
    198 CookiesGetFunction::~CookiesGetFunction() {
    199 }
    200 
    201 bool CookiesGetFunction::RunImpl() {
    202   parsed_args_ = Get::Params::Create(*args_);
    203   EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
    204 
    205   // Read/validate input parameters.
    206   if (!ParseUrl(parsed_args_->details.url, &url_, true))
    207     return false;
    208 
    209   std::string store_id =
    210       parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
    211                                            : std::string();
    212   net::URLRequestContextGetter* store_context = NULL;
    213   if (!ParseStoreContext(&store_id, &store_context))
    214     return false;
    215   store_context_ = store_context;
    216   if (!parsed_args_->details.store_id.get())
    217     parsed_args_->details.store_id.reset(new std::string(store_id));
    218 
    219   store_context_ = store_context;
    220 
    221   bool rv = BrowserThread::PostTask(
    222       BrowserThread::IO, FROM_HERE,
    223       base::Bind(&CookiesGetFunction::GetCookieOnIOThread, this));
    224   DCHECK(rv);
    225 
    226   // Will finish asynchronously.
    227   return true;
    228 }
    229 
    230 void CookiesGetFunction::GetCookieOnIOThread() {
    231   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    232   net::CookieStore* cookie_store =
    233       store_context_->GetURLRequestContext()->cookie_store();
    234   cookies_helpers::GetCookieListFromStore(
    235       cookie_store, url_,
    236       base::Bind(&CookiesGetFunction::GetCookieCallback, this));
    237 }
    238 
    239 void CookiesGetFunction::GetCookieCallback(const net::CookieList& cookie_list) {
    240   net::CookieList::const_iterator it;
    241   for (it = cookie_list.begin(); it != cookie_list.end(); ++it) {
    242     // Return the first matching cookie. Relies on the fact that the
    243     // CookieMonster returns them in canonical order (longest path, then
    244     // earliest creation time).
    245     if (it->Name() == parsed_args_->details.name) {
    246       scoped_ptr<Cookie> cookie(
    247           cookies_helpers::CreateCookie(*it, *parsed_args_->details.store_id));
    248       results_ = Get::Results::Create(*cookie);
    249       break;
    250     }
    251   }
    252 
    253   // The cookie doesn't exist; return null.
    254   if (it == cookie_list.end())
    255     SetResult(Value::CreateNullValue());
    256 
    257   bool rv = BrowserThread::PostTask(
    258       BrowserThread::UI, FROM_HERE,
    259       base::Bind(&CookiesGetFunction::RespondOnUIThread, this));
    260   DCHECK(rv);
    261 }
    262 
    263 void CookiesGetFunction::RespondOnUIThread() {
    264   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    265   SendResponse(true);
    266 }
    267 
    268 CookiesGetAllFunction::CookiesGetAllFunction() {
    269 }
    270 
    271 CookiesGetAllFunction::~CookiesGetAllFunction() {
    272 }
    273 
    274 bool CookiesGetAllFunction::RunImpl() {
    275   parsed_args_ = GetAll::Params::Create(*args_);
    276   EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
    277 
    278   if (parsed_args_->details.url.get() &&
    279       !ParseUrl(*parsed_args_->details.url, &url_, false)) {
    280     return false;
    281   }
    282 
    283   std::string store_id =
    284       parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
    285                                            : std::string();
    286   net::URLRequestContextGetter* store_context = NULL;
    287   if (!ParseStoreContext(&store_id, &store_context))
    288     return false;
    289   store_context_ = store_context;
    290   if (!parsed_args_->details.store_id.get())
    291     parsed_args_->details.store_id.reset(new std::string(store_id));
    292 
    293   bool rv = BrowserThread::PostTask(
    294       BrowserThread::IO, FROM_HERE,
    295       base::Bind(&CookiesGetAllFunction::GetAllCookiesOnIOThread, this));
    296   DCHECK(rv);
    297 
    298   // Will finish asynchronously.
    299   return true;
    300 }
    301 
    302 void CookiesGetAllFunction::GetAllCookiesOnIOThread() {
    303   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    304   net::CookieStore* cookie_store =
    305       store_context_->GetURLRequestContext()->cookie_store();
    306   cookies_helpers::GetCookieListFromStore(
    307       cookie_store, url_,
    308       base::Bind(&CookiesGetAllFunction::GetAllCookiesCallback, this));
    309 }
    310 
    311 void CookiesGetAllFunction::GetAllCookiesCallback(
    312     const net::CookieList& cookie_list) {
    313   const extensions::Extension* extension = GetExtension();
    314   if (extension) {
    315     std::vector<linked_ptr<Cookie> > match_vector;
    316     cookies_helpers::AppendMatchingCookiesToVector(
    317         cookie_list, url_, &parsed_args_->details,
    318         GetExtension(), &match_vector);
    319 
    320     results_ = GetAll::Results::Create(match_vector);
    321   }
    322   bool rv = BrowserThread::PostTask(
    323       BrowserThread::UI, FROM_HERE,
    324       base::Bind(&CookiesGetAllFunction::RespondOnUIThread, this));
    325   DCHECK(rv);
    326 }
    327 
    328 void CookiesGetAllFunction::RespondOnUIThread() {
    329   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    330   SendResponse(true);
    331 }
    332 
    333 CookiesSetFunction::CookiesSetFunction() : success_(false) {
    334 }
    335 
    336 CookiesSetFunction::~CookiesSetFunction() {
    337 }
    338 
    339 bool CookiesSetFunction::RunImpl() {
    340   parsed_args_ = Set::Params::Create(*args_);
    341   EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
    342 
    343   // Read/validate input parameters.
    344   if (!ParseUrl(parsed_args_->details.url, &url_, true))
    345       return false;
    346 
    347   std::string store_id =
    348       parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
    349                                            : std::string();
    350   net::URLRequestContextGetter* store_context = NULL;
    351   if (!ParseStoreContext(&store_id, &store_context))
    352     return false;
    353   store_context_ = store_context;
    354   if (!parsed_args_->details.store_id.get())
    355     parsed_args_->details.store_id.reset(new std::string(store_id));
    356 
    357   bool rv = BrowserThread::PostTask(
    358       BrowserThread::IO, FROM_HERE,
    359       base::Bind(&CookiesSetFunction::SetCookieOnIOThread, this));
    360   DCHECK(rv);
    361 
    362   // Will finish asynchronously.
    363   return true;
    364 }
    365 
    366 void CookiesSetFunction::SetCookieOnIOThread() {
    367   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    368   net::CookieMonster* cookie_monster =
    369       store_context_->GetURLRequestContext()->cookie_store()->
    370       GetCookieMonster();
    371 
    372   base::Time expiration_time;
    373   if (parsed_args_->details.expiration_date.get()) {
    374     // Time::FromDoubleT converts double time 0 to empty Time object. So we need
    375     // to do special handling here.
    376     expiration_time = (*parsed_args_->details.expiration_date == 0) ?
    377         base::Time::UnixEpoch() :
    378         base::Time::FromDoubleT(*parsed_args_->details.expiration_date);
    379   }
    380 
    381   cookie_monster->SetCookieWithDetailsAsync(
    382       url_,
    383       parsed_args_->details.name.get() ? *parsed_args_->details.name
    384                                        : std::string(),
    385       parsed_args_->details.value.get() ? *parsed_args_->details.value
    386                                         : std::string(),
    387       parsed_args_->details.domain.get() ? *parsed_args_->details.domain
    388                                          : std::string(),
    389       parsed_args_->details.path.get() ? *parsed_args_->details.path
    390                                        : std::string(),
    391       expiration_time,
    392       parsed_args_->details.secure.get() ? *parsed_args_->details.secure.get()
    393                                          : false,
    394       parsed_args_->details.http_only.get() ? *parsed_args_->details.http_only
    395                                             : false,
    396       net::COOKIE_PRIORITY_DEFAULT,
    397       base::Bind(&CookiesSetFunction::PullCookie, this));
    398 }
    399 
    400 void CookiesSetFunction::PullCookie(bool set_cookie_result) {
    401   // Pull the newly set cookie.
    402   net::CookieMonster* cookie_monster =
    403       store_context_->GetURLRequestContext()->cookie_store()->
    404       GetCookieMonster();
    405   success_ = set_cookie_result;
    406   cookies_helpers::GetCookieListFromStore(
    407       cookie_monster, url_,
    408       base::Bind(&CookiesSetFunction::PullCookieCallback, this));
    409 }
    410 
    411 void CookiesSetFunction::PullCookieCallback(
    412     const net::CookieList& cookie_list) {
    413   net::CookieList::const_iterator it;
    414   for (it = cookie_list.begin(); it != cookie_list.end(); ++it) {
    415     // Return the first matching cookie. Relies on the fact that the
    416     // CookieMonster returns them in canonical order (longest path, then
    417     // earliest creation time).
    418     std::string name =
    419         parsed_args_->details.name.get() ? *parsed_args_->details.name
    420                                          : std::string();
    421     if (it->Name() == name) {
    422       scoped_ptr<Cookie> cookie(
    423           cookies_helpers::CreateCookie(*it, *parsed_args_->details.store_id));
    424       results_ = Set::Results::Create(*cookie);
    425       break;
    426     }
    427   }
    428 
    429   bool rv = BrowserThread::PostTask(
    430       BrowserThread::UI, FROM_HERE,
    431       base::Bind(&CookiesSetFunction::RespondOnUIThread, this));
    432   DCHECK(rv);
    433 }
    434 
    435 void CookiesSetFunction::RespondOnUIThread() {
    436   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    437   if (!success_) {
    438     std::string name =
    439         parsed_args_->details.name.get() ? *parsed_args_->details.name
    440                                          : std::string();
    441     error_ = ErrorUtils::FormatErrorMessage(keys::kCookieSetFailedError, name);
    442   }
    443   SendResponse(success_);
    444 }
    445 
    446 CookiesRemoveFunction::CookiesRemoveFunction() {
    447 }
    448 
    449 CookiesRemoveFunction::~CookiesRemoveFunction() {
    450 }
    451 
    452 bool CookiesRemoveFunction::RunImpl() {
    453   parsed_args_ = Remove::Params::Create(*args_);
    454   EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
    455 
    456   // Read/validate input parameters.
    457   if (!ParseUrl(parsed_args_->details.url, &url_, true))
    458     return false;
    459 
    460   std::string store_id =
    461       parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
    462                                            : std::string();
    463   net::URLRequestContextGetter* store_context = NULL;
    464   if (!ParseStoreContext(&store_id, &store_context))
    465     return false;
    466   store_context_ = store_context;
    467   if (!parsed_args_->details.store_id.get())
    468     parsed_args_->details.store_id.reset(new std::string(store_id));
    469 
    470   // Pass the work off to the IO thread.
    471   bool rv = BrowserThread::PostTask(
    472       BrowserThread::IO, FROM_HERE,
    473       base::Bind(&CookiesRemoveFunction::RemoveCookieOnIOThread, this));
    474   DCHECK(rv);
    475 
    476   // Will return asynchronously.
    477   return true;
    478 }
    479 
    480 void CookiesRemoveFunction::RemoveCookieOnIOThread() {
    481   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    482 
    483   // Remove the cookie
    484   net::CookieStore* cookie_store =
    485       store_context_->GetURLRequestContext()->cookie_store();
    486   cookie_store->DeleteCookieAsync(
    487       url_, parsed_args_->details.name,
    488       base::Bind(&CookiesRemoveFunction::RemoveCookieCallback, this));
    489 }
    490 
    491 void CookiesRemoveFunction::RemoveCookieCallback() {
    492   // Build the callback result
    493   Remove::Results::Details details;
    494   details.name = parsed_args_->details.name;
    495   details.url = url_.spec();
    496   details.store_id = *parsed_args_->details.store_id;
    497   results_ = Remove::Results::Create(details);
    498 
    499   // Return to UI thread
    500   bool rv = BrowserThread::PostTask(
    501       BrowserThread::UI, FROM_HERE,
    502       base::Bind(&CookiesRemoveFunction::RespondOnUIThread, this));
    503   DCHECK(rv);
    504 }
    505 
    506 void CookiesRemoveFunction::RespondOnUIThread() {
    507   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    508   SendResponse(true);
    509 }
    510 
    511 bool CookiesGetAllCookieStoresFunction::RunImpl() {
    512   Profile* original_profile = profile();
    513   DCHECK(original_profile);
    514   scoped_ptr<base::ListValue> original_tab_ids(new base::ListValue());
    515   Profile* incognito_profile = NULL;
    516   scoped_ptr<base::ListValue> incognito_tab_ids;
    517   if (include_incognito() && profile()->HasOffTheRecordProfile()) {
    518     incognito_profile = profile()->GetOffTheRecordProfile();
    519     if (incognito_profile)
    520       incognito_tab_ids.reset(new base::ListValue());
    521   }
    522   DCHECK(original_profile != incognito_profile);
    523 
    524   // Iterate through all browser instances, and for each browser,
    525   // add its tab IDs to either the regular or incognito tab ID list depending
    526   // whether the browser is regular or incognito.
    527   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
    528     Browser* browser = *it;
    529     if (browser->profile() == original_profile) {
    530       cookies_helpers::AppendToTabIdList(browser, original_tab_ids.get());
    531     } else if (incognito_tab_ids.get() &&
    532                browser->profile() == incognito_profile) {
    533       cookies_helpers::AppendToTabIdList(browser, incognito_tab_ids.get());
    534     }
    535   }
    536   // Return a list of all cookie stores with at least one open tab.
    537   std::vector<linked_ptr<CookieStore> > cookie_stores;
    538   if (original_tab_ids->GetSize() > 0) {
    539     cookie_stores.push_back(make_linked_ptr(
    540         cookies_helpers::CreateCookieStore(
    541             original_profile, original_tab_ids.release()).release()));
    542   }
    543   if (incognito_tab_ids.get() && incognito_tab_ids->GetSize() > 0 &&
    544       incognito_profile) {
    545     cookie_stores.push_back(make_linked_ptr(
    546         cookies_helpers::CreateCookieStore(
    547             incognito_profile, incognito_tab_ids.release()).release()));
    548   }
    549   results_ = GetAllCookieStores::Results::Create(cookie_stores);
    550   return true;
    551 }
    552 
    553 void CookiesGetAllCookieStoresFunction::Run() {
    554   SendResponse(RunImpl());
    555 }
    556 
    557 CookiesAPI::CookiesAPI(Profile* profile)
    558     : profile_(profile) {
    559   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
    560       this, keys::kOnChanged);
    561 }
    562 
    563 CookiesAPI::~CookiesAPI() {
    564 }
    565 
    566 void CookiesAPI::Shutdown() {
    567   ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
    568 }
    569 
    570 static base::LazyInstance<ProfileKeyedAPIFactory<CookiesAPI> >
    571 g_factory = LAZY_INSTANCE_INITIALIZER;
    572 
    573 // static
    574 ProfileKeyedAPIFactory<CookiesAPI>* CookiesAPI::GetFactoryInstance() {
    575   return &g_factory.Get();
    576 }
    577 
    578 void CookiesAPI::OnListenerAdded(
    579     const extensions::EventListenerInfo& details) {
    580   cookies_event_router_.reset(new CookiesEventRouter(profile_));
    581   ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
    582 }
    583 
    584 }  // namespace extensions
    585