• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ParseUrl(ChromeAsyncExtensionFunction * function,const std::string & url_string,GURL * url,bool check_host_permissions)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->extension()->permissions_data()->HasHostPermission(*url)) {
66     function->SetError(ErrorUtils::FormatErrorMessage(
67         keys::kNoHostPermissionsError, url->spec()));
68     return false;
69   }
70   return true;
71 }
72 
ParseStoreContext(ChromeAsyncExtensionFunction * function,std::string * store_id,net::URLRequestContextGetter ** context)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 
CookiesEventRouter(content::BrowserContext * context)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 
~CookiesEventRouter()116 CookiesEventRouter::~CookiesEventRouter() {
117 }
118 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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 
CookieChanged(Profile * profile,ChromeCookieDetails * details)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 
DispatchEvent(content::BrowserContext * context,const std::string & event_name,scoped_ptr<base::ListValue> event_args,GURL & cookie_domain)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 
CookiesGetFunction()202 CookiesGetFunction::CookiesGetFunction() {
203 }
204 
~CookiesGetFunction()205 CookiesGetFunction::~CookiesGetFunction() {
206 }
207 
RunAsync()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 
GetCookieOnIOThread()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 
GetCookieCallback(const net::CookieList & cookie_list)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 
RespondOnUIThread()270 void CookiesGetFunction::RespondOnUIThread() {
271   DCHECK_CURRENTLY_ON(BrowserThread::UI);
272   SendResponse(true);
273 }
274 
CookiesGetAllFunction()275 CookiesGetAllFunction::CookiesGetAllFunction() {
276 }
277 
~CookiesGetAllFunction()278 CookiesGetAllFunction::~CookiesGetAllFunction() {
279 }
280 
RunAsync()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 
GetAllCookiesOnIOThread()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 
GetAllCookiesCallback(const net::CookieList & cookie_list)318 void CookiesGetAllFunction::GetAllCookiesCallback(
319     const net::CookieList& cookie_list) {
320   if (extension()) {
321     std::vector<linked_ptr<Cookie> > match_vector;
322     cookies_helpers::AppendMatchingCookiesToVector(
323         cookie_list, url_, &parsed_args_->details, extension(), &match_vector);
324 
325     results_ = GetAll::Results::Create(match_vector);
326   }
327   bool rv = BrowserThread::PostTask(
328       BrowserThread::UI, FROM_HERE,
329       base::Bind(&CookiesGetAllFunction::RespondOnUIThread, this));
330   DCHECK(rv);
331 }
332 
RespondOnUIThread()333 void CookiesGetAllFunction::RespondOnUIThread() {
334   DCHECK_CURRENTLY_ON(BrowserThread::UI);
335   SendResponse(true);
336 }
337 
CookiesSetFunction()338 CookiesSetFunction::CookiesSetFunction() : success_(false) {
339 }
340 
~CookiesSetFunction()341 CookiesSetFunction::~CookiesSetFunction() {
342 }
343 
RunAsync()344 bool CookiesSetFunction::RunAsync() {
345   parsed_args_ = Set::Params::Create(*args_);
346   EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
347 
348   // Read/validate input parameters.
349   if (!ParseUrl(this, parsed_args_->details.url, &url_, true))
350       return false;
351 
352   std::string store_id =
353       parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
354                                            : std::string();
355   net::URLRequestContextGetter* store_context = NULL;
356   if (!ParseStoreContext(this, &store_id, &store_context))
357     return false;
358   store_browser_context_ = store_context;
359   if (!parsed_args_->details.store_id.get())
360     parsed_args_->details.store_id.reset(new std::string(store_id));
361 
362   bool rv = BrowserThread::PostTask(
363       BrowserThread::IO, FROM_HERE,
364       base::Bind(&CookiesSetFunction::SetCookieOnIOThread, this));
365   DCHECK(rv);
366 
367   // Will finish asynchronously.
368   return true;
369 }
370 
SetCookieOnIOThread()371 void CookiesSetFunction::SetCookieOnIOThread() {
372   DCHECK_CURRENTLY_ON(BrowserThread::IO);
373   net::CookieMonster* cookie_monster =
374       store_browser_context_->GetURLRequestContext()
375           ->cookie_store()
376           ->GetCookieMonster();
377 
378   base::Time expiration_time;
379   if (parsed_args_->details.expiration_date.get()) {
380     // Time::FromDoubleT converts double time 0 to empty Time object. So we need
381     // to do special handling here.
382     expiration_time = (*parsed_args_->details.expiration_date == 0) ?
383         base::Time::UnixEpoch() :
384         base::Time::FromDoubleT(*parsed_args_->details.expiration_date);
385   }
386 
387   cookie_monster->SetCookieWithDetailsAsync(
388       url_,
389       parsed_args_->details.name.get() ? *parsed_args_->details.name
390                                        : std::string(),
391       parsed_args_->details.value.get() ? *parsed_args_->details.value
392                                         : std::string(),
393       parsed_args_->details.domain.get() ? *parsed_args_->details.domain
394                                          : std::string(),
395       parsed_args_->details.path.get() ? *parsed_args_->details.path
396                                        : std::string(),
397       expiration_time,
398       parsed_args_->details.secure.get() ? *parsed_args_->details.secure.get()
399                                          : false,
400       parsed_args_->details.http_only.get() ? *parsed_args_->details.http_only
401                                             : false,
402       net::COOKIE_PRIORITY_DEFAULT,
403       base::Bind(&CookiesSetFunction::PullCookie, this));
404 }
405 
PullCookie(bool set_cookie_result)406 void CookiesSetFunction::PullCookie(bool set_cookie_result) {
407   // Pull the newly set cookie.
408   net::CookieMonster* cookie_monster =
409       store_browser_context_->GetURLRequestContext()
410           ->cookie_store()
411           ->GetCookieMonster();
412   success_ = set_cookie_result;
413   cookies_helpers::GetCookieListFromStore(
414       cookie_monster, url_,
415       base::Bind(&CookiesSetFunction::PullCookieCallback, this));
416 }
417 
PullCookieCallback(const net::CookieList & cookie_list)418 void CookiesSetFunction::PullCookieCallback(
419     const net::CookieList& cookie_list) {
420   net::CookieList::const_iterator it;
421   for (it = cookie_list.begin(); it != cookie_list.end(); ++it) {
422     // Return the first matching cookie. Relies on the fact that the
423     // CookieMonster returns them in canonical order (longest path, then
424     // earliest creation time).
425     std::string name =
426         parsed_args_->details.name.get() ? *parsed_args_->details.name
427                                          : std::string();
428     if (it->Name() == name) {
429       scoped_ptr<Cookie> cookie(
430           cookies_helpers::CreateCookie(*it, *parsed_args_->details.store_id));
431       results_ = Set::Results::Create(*cookie);
432       break;
433     }
434   }
435 
436   bool rv = BrowserThread::PostTask(
437       BrowserThread::UI, FROM_HERE,
438       base::Bind(&CookiesSetFunction::RespondOnUIThread, this));
439   DCHECK(rv);
440 }
441 
RespondOnUIThread()442 void CookiesSetFunction::RespondOnUIThread() {
443   DCHECK_CURRENTLY_ON(BrowserThread::UI);
444   if (!success_) {
445     std::string name =
446         parsed_args_->details.name.get() ? *parsed_args_->details.name
447                                          : std::string();
448     error_ = ErrorUtils::FormatErrorMessage(keys::kCookieSetFailedError, name);
449   }
450   SendResponse(success_);
451 }
452 
CookiesRemoveFunction()453 CookiesRemoveFunction::CookiesRemoveFunction() {
454 }
455 
~CookiesRemoveFunction()456 CookiesRemoveFunction::~CookiesRemoveFunction() {
457 }
458 
RunAsync()459 bool CookiesRemoveFunction::RunAsync() {
460   parsed_args_ = Remove::Params::Create(*args_);
461   EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
462 
463   // Read/validate input parameters.
464   if (!ParseUrl(this, parsed_args_->details.url, &url_, true))
465     return false;
466 
467   std::string store_id =
468       parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
469                                            : std::string();
470   net::URLRequestContextGetter* store_context = NULL;
471   if (!ParseStoreContext(this, &store_id, &store_context))
472     return false;
473   store_browser_context_ = store_context;
474   if (!parsed_args_->details.store_id.get())
475     parsed_args_->details.store_id.reset(new std::string(store_id));
476 
477   // Pass the work off to the IO thread.
478   bool rv = BrowserThread::PostTask(
479       BrowserThread::IO, FROM_HERE,
480       base::Bind(&CookiesRemoveFunction::RemoveCookieOnIOThread, this));
481   DCHECK(rv);
482 
483   // Will return asynchronously.
484   return true;
485 }
486 
RemoveCookieOnIOThread()487 void CookiesRemoveFunction::RemoveCookieOnIOThread() {
488   DCHECK_CURRENTLY_ON(BrowserThread::IO);
489 
490   // Remove the cookie
491   net::CookieStore* cookie_store =
492       store_browser_context_->GetURLRequestContext()->cookie_store();
493   cookie_store->DeleteCookieAsync(
494       url_, parsed_args_->details.name,
495       base::Bind(&CookiesRemoveFunction::RemoveCookieCallback, this));
496 }
497 
RemoveCookieCallback()498 void CookiesRemoveFunction::RemoveCookieCallback() {
499   // Build the callback result
500   Remove::Results::Details details;
501   details.name = parsed_args_->details.name;
502   details.url = url_.spec();
503   details.store_id = *parsed_args_->details.store_id;
504   results_ = Remove::Results::Create(details);
505 
506   // Return to UI thread
507   bool rv = BrowserThread::PostTask(
508       BrowserThread::UI, FROM_HERE,
509       base::Bind(&CookiesRemoveFunction::RespondOnUIThread, this));
510   DCHECK(rv);
511 }
512 
RespondOnUIThread()513 void CookiesRemoveFunction::RespondOnUIThread() {
514   DCHECK_CURRENTLY_ON(BrowserThread::UI);
515   SendResponse(true);
516 }
517 
RunSync()518 bool CookiesGetAllCookieStoresFunction::RunSync() {
519   Profile* original_profile = GetProfile();
520   DCHECK(original_profile);
521   scoped_ptr<base::ListValue> original_tab_ids(new base::ListValue());
522   Profile* incognito_profile = NULL;
523   scoped_ptr<base::ListValue> incognito_tab_ids;
524   if (include_incognito() && GetProfile()->HasOffTheRecordProfile()) {
525     incognito_profile = GetProfile()->GetOffTheRecordProfile();
526     if (incognito_profile)
527       incognito_tab_ids.reset(new base::ListValue());
528   }
529   DCHECK(original_profile != incognito_profile);
530 
531   // Iterate through all browser instances, and for each browser,
532   // add its tab IDs to either the regular or incognito tab ID list depending
533   // whether the browser is regular or incognito.
534   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
535     Browser* browser = *it;
536     if (browser->profile() == original_profile) {
537       cookies_helpers::AppendToTabIdList(browser, original_tab_ids.get());
538     } else if (incognito_tab_ids.get() &&
539                browser->profile() == incognito_profile) {
540       cookies_helpers::AppendToTabIdList(browser, incognito_tab_ids.get());
541     }
542   }
543   // Return a list of all cookie stores with at least one open tab.
544   std::vector<linked_ptr<CookieStore> > cookie_stores;
545   if (original_tab_ids->GetSize() > 0) {
546     cookie_stores.push_back(make_linked_ptr(
547         cookies_helpers::CreateCookieStore(
548             original_profile, original_tab_ids.release()).release()));
549   }
550   if (incognito_tab_ids.get() && incognito_tab_ids->GetSize() > 0 &&
551       incognito_profile) {
552     cookie_stores.push_back(make_linked_ptr(
553         cookies_helpers::CreateCookieStore(
554             incognito_profile, incognito_tab_ids.release()).release()));
555   }
556   results_ = GetAllCookieStores::Results::Create(cookie_stores);
557   return true;
558 }
559 
CookiesAPI(content::BrowserContext * context)560 CookiesAPI::CookiesAPI(content::BrowserContext* context)
561     : browser_context_(context) {
562   EventRouter::Get(browser_context_)
563       ->RegisterObserver(this, cookies::OnChanged::kEventName);
564 }
565 
~CookiesAPI()566 CookiesAPI::~CookiesAPI() {
567 }
568 
Shutdown()569 void CookiesAPI::Shutdown() {
570   EventRouter::Get(browser_context_)->UnregisterObserver(this);
571 }
572 
573 static base::LazyInstance<BrowserContextKeyedAPIFactory<CookiesAPI> >
574     g_factory = LAZY_INSTANCE_INITIALIZER;
575 
576 // static
GetFactoryInstance()577 BrowserContextKeyedAPIFactory<CookiesAPI>* CookiesAPI::GetFactoryInstance() {
578   return g_factory.Pointer();
579 }
580 
OnListenerAdded(const extensions::EventListenerInfo & details)581 void CookiesAPI::OnListenerAdded(
582     const extensions::EventListenerInfo& details) {
583   cookies_event_router_.reset(new CookiesEventRouter(browser_context_));
584   EventRouter::Get(browser_context_)->UnregisterObserver(this);
585 }
586 
587 }  // namespace extensions
588