Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 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/extension_cookies_api.h"
      8 
      9 #include "base/json/json_writer.h"
     10 #include "base/task.h"
     11 #include "base/values.h"
     12 #include "chrome/browser/extensions/extension_cookies_api_constants.h"
     13 #include "chrome/browser/extensions/extension_cookies_helpers.h"
     14 #include "chrome/browser/extensions/extension_event_router.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "chrome/browser/ui/browser_list.h"
     17 #include "chrome/common/extensions/extension.h"
     18 #include "chrome/common/extensions/extension_error_utils.h"
     19 #include "content/browser/browser_thread.h"
     20 #include "content/common/notification_service.h"
     21 #include "content/common/notification_type.h"
     22 #include "net/base/cookie_monster.h"
     23 #include "net/url_request/url_request_context.h"
     24 #include "net/url_request/url_request_context_getter.h"
     25 
     26 namespace keys = extension_cookies_api_constants;
     27 
     28 // static
     29 ExtensionCookiesEventRouter* ExtensionCookiesEventRouter::GetInstance() {
     30   return Singleton<ExtensionCookiesEventRouter>::get();
     31 }
     32 
     33 void ExtensionCookiesEventRouter::Init() {
     34   if (registrar_.IsEmpty()) {
     35     registrar_.Add(this,
     36                    NotificationType::COOKIE_CHANGED,
     37                    NotificationService::AllSources());
     38   }
     39 }
     40 
     41 void ExtensionCookiesEventRouter::Observe(NotificationType type,
     42                                           const NotificationSource& source,
     43                                           const NotificationDetails& details) {
     44   switch (type.value) {
     45     case NotificationType::COOKIE_CHANGED:
     46       CookieChanged(
     47           Source<Profile>(source).ptr(),
     48           Details<ChromeCookieDetails>(details).ptr());
     49       break;
     50 
     51     default:
     52       NOTREACHED();
     53   }
     54 }
     55 
     56 void ExtensionCookiesEventRouter::CookieChanged(
     57     Profile* profile,
     58     ChromeCookieDetails* details) {
     59   ListValue args;
     60   DictionaryValue* dict = new DictionaryValue();
     61   dict->SetBoolean(keys::kRemovedKey, details->removed);
     62   dict->Set(
     63       keys::kCookieKey,
     64       extension_cookies_helpers::CreateCookieValue(*details->cookie,
     65           extension_cookies_helpers::GetStoreIdFromProfile(profile)));
     66 
     67   // Map the interal cause to an external string.
     68   std::string cause;
     69   switch (details->cause) {
     70     case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT:
     71       cause = keys::kExplicitChangeCause;
     72       break;
     73 
     74     case net::CookieMonster::Delegate::CHANGE_COOKIE_OVERWRITE:
     75       cause = keys::kOverwriteChangeCause;
     76       break;
     77 
     78     case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED:
     79       cause = keys::kExpiredChangeCause;
     80       break;
     81 
     82     case net::CookieMonster::Delegate::CHANGE_COOKIE_EVICTED:
     83       cause = keys::kEvictedChangeCause;
     84       break;
     85 
     86     case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED_OVERWRITE:
     87       cause = keys::kExpiredOverwriteChangeCause;
     88       break;
     89 
     90     default:
     91       NOTREACHED();
     92   }
     93   dict->SetString(keys::kCauseKey, cause);
     94 
     95   args.Append(dict);
     96 
     97   std::string json_args;
     98   base::JSONWriter::Write(&args, false, &json_args);
     99   GURL cookie_domain =
    100       extension_cookies_helpers::GetURLFromCanonicalCookie(*details->cookie);
    101   DispatchEvent(profile, keys::kOnChanged, json_args, cookie_domain);
    102 }
    103 
    104 void ExtensionCookiesEventRouter::DispatchEvent(Profile* profile,
    105                                                 const char* event_name,
    106                                                 const std::string& json_args,
    107                                                 GURL& cookie_domain) {
    108   if (profile && profile->GetExtensionEventRouter()) {
    109     profile->GetExtensionEventRouter()->DispatchEventToRenderers(
    110         event_name, json_args, profile, cookie_domain);
    111   }
    112 }
    113 
    114 bool CookiesFunction::ParseUrl(const DictionaryValue* details, GURL* url,
    115                                bool check_host_permissions) {
    116   DCHECK(details && url);
    117   std::string url_string;
    118   // Get the URL string or return false.
    119   EXTENSION_FUNCTION_VALIDATE(details->GetString(keys::kUrlKey, &url_string));
    120   *url = GURL(url_string);
    121   if (!url->is_valid()) {
    122     error_ = ExtensionErrorUtils::FormatErrorMessage(
    123         keys::kInvalidUrlError, url_string);
    124     return false;
    125   }
    126   // Check against host permissions if needed.
    127   if (check_host_permissions &&
    128       !GetExtension()->HasHostPermission(*url)) {
    129     error_ = ExtensionErrorUtils::FormatErrorMessage(
    130         keys::kNoHostPermissionsError, url->spec());
    131     return false;
    132   }
    133   return true;
    134 }
    135 
    136 bool CookiesFunction::ParseStoreContext(const DictionaryValue* details,
    137                                         net::URLRequestContextGetter** context,
    138                                         std::string* store_id) {
    139   DCHECK(details && (context || store_id));
    140   Profile* store_profile = NULL;
    141   if (details->HasKey(keys::kStoreIdKey)) {
    142     // The store ID was explicitly specified in the details dictionary.
    143     // Retrieve its corresponding cookie store.
    144     std::string store_id_value;
    145     // Get the store ID string or return false.
    146     EXTENSION_FUNCTION_VALIDATE(
    147         details->GetString(keys::kStoreIdKey, &store_id_value));
    148     store_profile = extension_cookies_helpers::ChooseProfileFromStoreId(
    149         store_id_value, profile(), include_incognito());
    150     if (!store_profile) {
    151       error_ = ExtensionErrorUtils::FormatErrorMessage(
    152           keys::kInvalidStoreIdError, store_id_value);
    153       return false;
    154     }
    155   } else {
    156     // The store ID was not specified; use the current execution context's
    157     // cookie store by default.
    158     // GetCurrentBrowser() already takes into account incognito settings.
    159     Browser* current_browser = GetCurrentBrowser();
    160     if (!current_browser) {
    161       error_ = keys::kNoCookieStoreFoundError;
    162       return false;
    163     }
    164     store_profile = current_browser->profile();
    165   }
    166   DCHECK(store_profile);
    167 
    168   if (context)
    169     *context = store_profile->GetRequestContext();
    170   if (store_id)
    171     *store_id = extension_cookies_helpers::GetStoreIdFromProfile(store_profile);
    172 
    173   return true;
    174 }
    175 
    176 GetCookieFunction::GetCookieFunction() {}
    177 
    178 GetCookieFunction::~GetCookieFunction() {}
    179 
    180 bool GetCookieFunction::RunImpl() {
    181   // Return false if the arguments are malformed.
    182   DictionaryValue* details;
    183   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &details));
    184   DCHECK(details);
    185 
    186   // Read/validate input parameters.
    187   if (!ParseUrl(details, &url_, true))
    188     return false;
    189 
    190   // Get the cookie name string or return false.
    191   EXTENSION_FUNCTION_VALIDATE(details->GetString(keys::kNameKey, &name_));
    192 
    193   net::URLRequestContextGetter* store_context = NULL;
    194   if (!ParseStoreContext(details, &store_context, &store_id_))
    195     return false;
    196 
    197   DCHECK(store_context && !store_id_.empty());
    198   store_context_ = store_context;
    199 
    200   bool rv = BrowserThread::PostTask(
    201       BrowserThread::IO, FROM_HERE,
    202       NewRunnableMethod(this, &GetCookieFunction::GetCookieOnIOThread));
    203   DCHECK(rv);
    204 
    205   // Will finish asynchronously.
    206   return true;
    207 }
    208 
    209 void GetCookieFunction::GetCookieOnIOThread() {
    210   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    211   net::CookieStore* cookie_store =
    212       store_context_->GetURLRequestContext()->cookie_store();
    213   net::CookieList cookie_list =
    214       extension_cookies_helpers::GetCookieListFromStore(cookie_store, url_);
    215   net::CookieList::iterator it;
    216   for (it = cookie_list.begin(); it != cookie_list.end(); ++it) {
    217     // Return the first matching cookie. Relies on the fact that the
    218     // CookieMonster returns them in canonical order (longest path, then
    219     // earliest creation time).
    220     if (it->Name() == name_) {
    221       result_.reset(
    222           extension_cookies_helpers::CreateCookieValue(*it, store_id_));
    223       break;
    224     }
    225   }
    226 
    227   // The cookie doesn't exist; return null.
    228   if (it == cookie_list.end())
    229     result_.reset(Value::CreateNullValue());
    230 
    231   bool rv = BrowserThread::PostTask(
    232       BrowserThread::UI, FROM_HERE,
    233       NewRunnableMethod(this, &GetCookieFunction::RespondOnUIThread));
    234   DCHECK(rv);
    235 }
    236 
    237 void GetCookieFunction::RespondOnUIThread() {
    238   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    239   SendResponse(true);
    240 }
    241 
    242 GetAllCookiesFunction::GetAllCookiesFunction() : details_(NULL) {}
    243 
    244 GetAllCookiesFunction::~GetAllCookiesFunction() {}
    245 
    246 bool GetAllCookiesFunction::RunImpl() {
    247   // Return false if the arguments are malformed.
    248   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &details_));
    249   DCHECK(details_);
    250 
    251   // Read/validate input parameters.
    252   if (details_->HasKey(keys::kUrlKey) && !ParseUrl(details_, &url_, false))
    253     return false;
    254 
    255   net::URLRequestContextGetter* store_context = NULL;
    256   if (!ParseStoreContext(details_, &store_context, &store_id_))
    257     return false;
    258   DCHECK(store_context);
    259   store_context_ = store_context;
    260 
    261   bool rv = BrowserThread::PostTask(
    262       BrowserThread::IO, FROM_HERE,
    263       NewRunnableMethod(this, &GetAllCookiesFunction::GetAllCookiesOnIOThread));
    264   DCHECK(rv);
    265 
    266   // Will finish asynchronously.
    267   return true;
    268 }
    269 
    270 void GetAllCookiesFunction::GetAllCookiesOnIOThread() {
    271   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    272   net::CookieStore* cookie_store =
    273       store_context_->GetURLRequestContext()->cookie_store();
    274   net::CookieList cookie_list =
    275       extension_cookies_helpers::GetCookieListFromStore(cookie_store, url_);
    276 
    277   const Extension* extension = GetExtension();
    278   if (extension) {
    279     ListValue* matching_list = new ListValue();
    280     extension_cookies_helpers::AppendMatchingCookiesToList(
    281         cookie_list, store_id_, url_, details_,
    282         GetExtension(), matching_list);
    283     result_.reset(matching_list);
    284   }
    285   bool rv = BrowserThread::PostTask(
    286       BrowserThread::UI, FROM_HERE,
    287       NewRunnableMethod(this, &GetAllCookiesFunction::RespondOnUIThread));
    288   DCHECK(rv);
    289 }
    290 
    291 void GetAllCookiesFunction::RespondOnUIThread() {
    292   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    293   SendResponse(true);
    294 }
    295 
    296 SetCookieFunction::SetCookieFunction()
    297     : secure_(false),
    298       http_only_(false),
    299       success_(false) {
    300 }
    301 
    302 SetCookieFunction::~SetCookieFunction() {
    303 }
    304 
    305 bool SetCookieFunction::RunImpl() {
    306   // Return false if the arguments are malformed.
    307   DictionaryValue* details;
    308   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &details));
    309   DCHECK(details);
    310 
    311   // Read/validate input parameters.
    312   if (!ParseUrl(details, &url_, true))
    313       return false;
    314   // The macros below return false if argument types are not as expected.
    315   if (details->HasKey(keys::kNameKey))
    316     EXTENSION_FUNCTION_VALIDATE(details->GetString(keys::kNameKey, &name_));
    317   if (details->HasKey(keys::kValueKey))
    318     EXTENSION_FUNCTION_VALIDATE(details->GetString(keys::kValueKey, &value_));
    319   if (details->HasKey(keys::kDomainKey))
    320     EXTENSION_FUNCTION_VALIDATE(details->GetString(keys::kDomainKey, &domain_));
    321   if (details->HasKey(keys::kPathKey))
    322     EXTENSION_FUNCTION_VALIDATE(details->GetString(keys::kPathKey, &path_));
    323 
    324   if (details->HasKey(keys::kSecureKey)) {
    325     EXTENSION_FUNCTION_VALIDATE(
    326         details->GetBoolean(keys::kSecureKey, &secure_));
    327   }
    328   if (details->HasKey(keys::kHttpOnlyKey)) {
    329     EXTENSION_FUNCTION_VALIDATE(
    330         details->GetBoolean(keys::kHttpOnlyKey, &http_only_));
    331   }
    332   if (details->HasKey(keys::kExpirationDateKey)) {
    333     Value* expiration_date_value;
    334     EXTENSION_FUNCTION_VALIDATE(details->Get(keys::kExpirationDateKey,
    335                                              &expiration_date_value));
    336     double expiration_date;
    337     if (expiration_date_value->IsType(Value::TYPE_INTEGER)) {
    338       int expiration_date_int;
    339       EXTENSION_FUNCTION_VALIDATE(
    340           expiration_date_value->GetAsInteger(&expiration_date_int));
    341       expiration_date = static_cast<double>(expiration_date_int);
    342     } else {
    343       EXTENSION_FUNCTION_VALIDATE(
    344           expiration_date_value->GetAsDouble(&expiration_date));
    345     }
    346     // Time::FromDoubleT converts double time 0 to empty Time object. So we need
    347     // to do special handling here.
    348     expiration_time_ = (expiration_date == 0) ?
    349         base::Time::UnixEpoch() : base::Time::FromDoubleT(expiration_date);
    350   }
    351 
    352   net::URLRequestContextGetter* store_context = NULL;
    353   if (!ParseStoreContext(details, &store_context, NULL))
    354     return false;
    355   DCHECK(store_context);
    356   store_context_ = store_context;
    357 
    358   bool rv = BrowserThread::PostTask(
    359       BrowserThread::IO, FROM_HERE,
    360       NewRunnableMethod(this, &SetCookieFunction::SetCookieOnIOThread));
    361   DCHECK(rv);
    362 
    363   // Will finish asynchronously.
    364   return true;
    365 }
    366 
    367 void SetCookieFunction::SetCookieOnIOThread() {
    368   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    369   net::CookieMonster* cookie_monster =
    370       store_context_->GetURLRequestContext()->cookie_store()->
    371       GetCookieMonster();
    372   success_ = cookie_monster->SetCookieWithDetails(
    373       url_, name_, value_, domain_, path_, expiration_time_,
    374       secure_, http_only_);
    375 
    376   // Pull the newly set cookie.
    377   net::CookieList cookie_list =
    378       extension_cookies_helpers::GetCookieListFromStore(cookie_monster, url_);
    379   net::CookieList::iterator it;
    380   for (it = cookie_list.begin(); it != cookie_list.end(); ++it) {
    381     // Return the first matching cookie. Relies on the fact that the
    382     // CookieMonster returns them in canonical order (longest path, then
    383     // earliest creation time).
    384     if (it->Name() == name_) {
    385       result_.reset(
    386           extension_cookies_helpers::CreateCookieValue(*it, store_id_));
    387       break;
    388     }
    389   }
    390 
    391   bool rv = BrowserThread::PostTask(
    392       BrowserThread::UI, FROM_HERE,
    393       NewRunnableMethod(this, &SetCookieFunction::RespondOnUIThread));
    394   DCHECK(rv);
    395 }
    396 
    397 void SetCookieFunction::RespondOnUIThread() {
    398   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    399   if (!success_) {
    400     error_ = ExtensionErrorUtils::FormatErrorMessage(
    401         keys::kCookieSetFailedError, name_);
    402   }
    403   SendResponse(success_);
    404 }
    405 
    406 RemoveCookieFunction::RemoveCookieFunction() : success_(false) {
    407 }
    408 
    409 RemoveCookieFunction::~RemoveCookieFunction() {
    410 }
    411 
    412 bool RemoveCookieFunction::RunImpl() {
    413   // Return false if the arguments are malformed.
    414   DictionaryValue* details;
    415   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &details));
    416   DCHECK(details);
    417 
    418   // Read/validate input parameters.
    419   if (!ParseUrl(details, &url_, true))
    420     return false;
    421 
    422   // Get the cookie name string or return false.
    423   EXTENSION_FUNCTION_VALIDATE(details->GetString(keys::kNameKey, &name_));
    424 
    425   net::URLRequestContextGetter* store_context = NULL;
    426   if (!ParseStoreContext(details, &store_context, &store_id_))
    427     return false;
    428   DCHECK(store_context);
    429   store_context_ = store_context;
    430 
    431   // Pass the work off to the IO thread.
    432   bool rv = BrowserThread::PostTask(
    433       BrowserThread::IO, FROM_HERE,
    434       NewRunnableMethod(this, &RemoveCookieFunction::RemoveCookieOnIOThread));
    435   DCHECK(rv);
    436 
    437   // Will return asynchronously.
    438   return true;
    439 }
    440 
    441 void RemoveCookieFunction::RemoveCookieOnIOThread() {
    442   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    443 
    444   // Remove the cookie
    445   net::CookieStore* cookie_store =
    446       store_context_->GetURLRequestContext()->cookie_store();
    447   cookie_store->DeleteCookie(url_, name_);
    448 
    449   // Build the callback result
    450   DictionaryValue* resultDictionary = new DictionaryValue();
    451   resultDictionary->SetString(keys::kNameKey, name_);
    452   resultDictionary->SetString(keys::kUrlKey, url_.spec());
    453   resultDictionary->SetString(keys::kStoreIdKey, store_id_);
    454   result_.reset(resultDictionary);
    455 
    456   // Return to UI thread
    457   bool rv = BrowserThread::PostTask(
    458       BrowserThread::UI, FROM_HERE,
    459       NewRunnableMethod(this, &RemoveCookieFunction::RespondOnUIThread));
    460   DCHECK(rv);
    461 }
    462 
    463 void RemoveCookieFunction::RespondOnUIThread() {
    464   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    465   SendResponse(true);
    466 }
    467 
    468 bool GetAllCookieStoresFunction::RunImpl() {
    469   Profile* original_profile = profile();
    470   DCHECK(original_profile);
    471   scoped_ptr<ListValue> original_tab_ids(new ListValue());
    472   Profile* incognito_profile = NULL;
    473   scoped_ptr<ListValue> incognito_tab_ids;
    474   if (include_incognito() && profile()->HasOffTheRecordProfile()) {
    475     incognito_profile = profile()->GetOffTheRecordProfile();
    476     if (incognito_profile)
    477       incognito_tab_ids.reset(new ListValue());
    478   }
    479   DCHECK(original_profile != incognito_profile);
    480 
    481   // Iterate through all browser instances, and for each browser,
    482   // add its tab IDs to either the regular or incognito tab ID list depending
    483   // whether the browser is regular or incognito.
    484   for (BrowserList::const_iterator iter = BrowserList::begin();
    485        iter != BrowserList::end(); ++iter) {
    486     Browser* browser = *iter;
    487     if (browser->profile() == original_profile) {
    488       extension_cookies_helpers::AppendToTabIdList(browser,
    489                                                    original_tab_ids.get());
    490     } else if (incognito_tab_ids.get() &&
    491                browser->profile() == incognito_profile) {
    492       extension_cookies_helpers::AppendToTabIdList(browser,
    493                                                    incognito_tab_ids.get());
    494     }
    495   }
    496   // Return a list of all cookie stores with at least one open tab.
    497   ListValue* cookie_store_list = new ListValue();
    498   if (original_tab_ids->GetSize() > 0) {
    499     cookie_store_list->Append(
    500         extension_cookies_helpers::CreateCookieStoreValue(
    501             original_profile, original_tab_ids.release()));
    502   }
    503   if (incognito_tab_ids.get() && incognito_tab_ids->GetSize() > 0) {
    504     cookie_store_list->Append(
    505         extension_cookies_helpers::CreateCookieStoreValue(
    506             incognito_profile, incognito_tab_ids.release()));
    507   }
    508   result_.reset(cookie_store_list);
    509   return true;
    510 }
    511 
    512 void GetAllCookieStoresFunction::Run() {
    513   SendResponse(RunImpl());
    514 }
    515