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