• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
GetInstance()29 ExtensionCookiesEventRouter* ExtensionCookiesEventRouter::GetInstance() {
30   return Singleton<ExtensionCookiesEventRouter>::get();
31 }
32 
Init()33 void ExtensionCookiesEventRouter::Init() {
34   if (registrar_.IsEmpty()) {
35     registrar_.Add(this,
36                    NotificationType::COOKIE_CHANGED,
37                    NotificationService::AllSources());
38   }
39 }
40 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
CookieChanged(Profile * profile,ChromeCookieDetails * details)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 
DispatchEvent(Profile * profile,const char * event_name,const std::string & json_args,GURL & cookie_domain)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 
ParseUrl(const DictionaryValue * details,GURL * url,bool check_host_permissions)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 
ParseStoreContext(const DictionaryValue * details,net::URLRequestContextGetter ** context,std::string * store_id)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 
GetCookieFunction()176 GetCookieFunction::GetCookieFunction() {}
177 
~GetCookieFunction()178 GetCookieFunction::~GetCookieFunction() {}
179 
RunImpl()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 
GetCookieOnIOThread()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 
RespondOnUIThread()237 void GetCookieFunction::RespondOnUIThread() {
238   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
239   SendResponse(true);
240 }
241 
GetAllCookiesFunction()242 GetAllCookiesFunction::GetAllCookiesFunction() : details_(NULL) {}
243 
~GetAllCookiesFunction()244 GetAllCookiesFunction::~GetAllCookiesFunction() {}
245 
RunImpl()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 
GetAllCookiesOnIOThread()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 
RespondOnUIThread()291 void GetAllCookiesFunction::RespondOnUIThread() {
292   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
293   SendResponse(true);
294 }
295 
SetCookieFunction()296 SetCookieFunction::SetCookieFunction()
297     : secure_(false),
298       http_only_(false),
299       success_(false) {
300 }
301 
~SetCookieFunction()302 SetCookieFunction::~SetCookieFunction() {
303 }
304 
RunImpl()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 
SetCookieOnIOThread()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 
RespondOnUIThread()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 
RemoveCookieFunction()406 RemoveCookieFunction::RemoveCookieFunction() : success_(false) {
407 }
408 
~RemoveCookieFunction()409 RemoveCookieFunction::~RemoveCookieFunction() {
410 }
411 
RunImpl()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 
RemoveCookieOnIOThread()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 
RespondOnUIThread()463 void RemoveCookieFunction::RespondOnUIThread() {
464   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
465   SendResponse(true);
466 }
467 
RunImpl()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 
Run()512 void GetAllCookieStoresFunction::Run() {
513   SendResponse(RunImpl());
514 }
515