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.
5 // Implements the Chrome Extensions Cookies API.
7 #include "chrome/browser/extensions/api/cookies/cookies_api.h"
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"
37 using content::BrowserThread
;
38 using extensions::api::cookies::Cookie
;
39 using extensions::api::cookies::CookieStore
;
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
;
47 namespace extensions
{
48 namespace cookies
= api::cookies
;
49 namespace keys
= cookies_api_constants
;
53 bool ParseUrl(ChromeAsyncExtensionFunction
* function
,
54 const std::string
& url_string
,
56 bool check_host_permissions
) {
57 *url
= GURL(url_string
);
58 if (!url
->is_valid()) {
60 ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError
, url_string
));
63 // Check against host permissions if needed.
64 if (check_host_permissions
&&
65 !PermissionsData::HasHostPermission(function
->GetExtension(), *url
)) {
66 function
->SetError(ErrorUtils::FormatErrorMessage(
67 keys::kNoHostPermissionsError
, url
->spec()));
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());
82 function
->SetError(ErrorUtils::FormatErrorMessage(
83 keys::kInvalidStoreIdError
, *store_id
));
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
);
95 store_profile
= current_browser
->profile();
96 *store_id
= cookies_helpers::GetStoreIdFromProfile(store_profile
);
100 *context
= store_profile
->GetRequestContext();
108 CookiesEventRouter::CookiesEventRouter(content::BrowserContext
* context
)
109 : profile_(Profile::FromBrowserContext(context
)) {
110 CHECK(registrar_
.IsEmpty());
112 chrome::NOTIFICATION_COOKIE_CHANGED
,
113 content::NotificationService::AllBrowserContextsAndSources());
116 CookiesEventRouter::~CookiesEventRouter() {
119 void CookiesEventRouter::Observe(
121 const content::NotificationSource
& source
,
122 const content::NotificationDetails
& details
) {
123 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
124 if (!profile_
->IsSameProfile(profile
))
128 case chrome::NOTIFICATION_COOKIE_CHANGED
:
131 content::Details
<ChromeCookieDetails
>(details
).ptr());
139 void CookiesEventRouter::CookieChanged(
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
);
146 scoped_ptr
<Cookie
> cookie(
147 cookies_helpers::CreateCookie(*details
->cookie
,
148 cookies_helpers::GetStoreIdFromProfile(profile
)));
149 dict
->Set(keys::kCookieKey
, cookie
->ToValue().release());
151 // Map the internal cause to an external string.
153 switch (details
->cause
) {
154 case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT
:
155 cause
= keys::kExplicitChangeCause
;
158 case net::CookieMonster::Delegate::CHANGE_COOKIE_OVERWRITE
:
159 cause
= keys::kOverwriteChangeCause
;
162 case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED
:
163 cause
= keys::kExpiredChangeCause
;
166 case net::CookieMonster::Delegate::CHANGE_COOKIE_EVICTED
:
167 cause
= keys::kEvictedChangeCause
;
170 case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED_OVERWRITE
:
171 cause
= keys::kExpiredOverwriteChangeCause
;
177 dict
->SetString(keys::kCauseKey
, cause
);
182 cookies_helpers::GetURLFromCanonicalCookie(*details
->cookie
);
183 DispatchEvent(profile
,
184 cookies::OnChanged::kEventName
,
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
;
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());
202 CookiesGetFunction::CookiesGetFunction() {
205 CookiesGetFunction::~CookiesGetFunction() {
208 bool CookiesGetFunction::RunImpl() {
209 parsed_args_
= Get::Params::Create(*args_
);
210 EXTENSION_FUNCTION_VALIDATE(parsed_args_
.get());
212 // Read/validate input parameters.
213 if (!ParseUrl(this, parsed_args_
->details
.url
, &url_
, true))
216 std::string store_id
=
217 parsed_args_
->details
.store_id
.get() ? *parsed_args_
->details
.store_id
219 net::URLRequestContextGetter
* store_context
= NULL
;
220 if (!ParseStoreContext(this, &store_id
, &store_context
))
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
));
226 store_browser_context_
= store_context
;
228 bool rv
= BrowserThread::PostTask(
229 BrowserThread::IO
, FROM_HERE
,
230 base::Bind(&CookiesGetFunction::GetCookieOnIOThread
, this));
233 // Will finish asynchronously.
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(
243 base::Bind(&CookiesGetFunction::GetCookieCallback
, this));
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
);
260 // The cookie doesn't exist; return null.
261 if (it
== cookie_list
.end())
262 SetResult(base::Value::CreateNullValue());
264 bool rv
= BrowserThread::PostTask(
265 BrowserThread::UI
, FROM_HERE
,
266 base::Bind(&CookiesGetFunction::RespondOnUIThread
, this));
270 void CookiesGetFunction::RespondOnUIThread() {
271 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
275 CookiesGetAllFunction::CookiesGetAllFunction() {
278 CookiesGetAllFunction::~CookiesGetAllFunction() {
281 bool CookiesGetAllFunction::RunImpl() {
282 parsed_args_
= GetAll::Params::Create(*args_
);
283 EXTENSION_FUNCTION_VALIDATE(parsed_args_
.get());
285 if (parsed_args_
->details
.url
.get() &&
286 !ParseUrl(this, *parsed_args_
->details
.url
, &url_
, false)) {
290 std::string store_id
=
291 parsed_args_
->details
.store_id
.get() ? *parsed_args_
->details
.store_id
293 net::URLRequestContextGetter
* store_context
= NULL
;
294 if (!ParseStoreContext(this, &store_id
, &store_context
))
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
));
300 bool rv
= BrowserThread::PostTask(
301 BrowserThread::IO
, FROM_HERE
,
302 base::Bind(&CookiesGetAllFunction::GetAllCookiesOnIOThread
, this));
305 // Will finish asynchronously.
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(
315 base::Bind(&CookiesGetAllFunction::GetAllCookiesCallback
, this));
318 void CookiesGetAllFunction::GetAllCookiesCallback(
319 const net::CookieList
& cookie_list
) {
320 const extensions::Extension
* extension
= GetExtension();
322 std::vector
<linked_ptr
<Cookie
> > match_vector
;
323 cookies_helpers::AppendMatchingCookiesToVector(
324 cookie_list
, url_
, &parsed_args_
->details
,
325 GetExtension(), &match_vector
);
327 results_
= GetAll::Results::Create(match_vector
);
329 bool rv
= BrowserThread::PostTask(
330 BrowserThread::UI
, FROM_HERE
,
331 base::Bind(&CookiesGetAllFunction::RespondOnUIThread
, this));
335 void CookiesGetAllFunction::RespondOnUIThread() {
336 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
340 CookiesSetFunction::CookiesSetFunction() : success_(false) {
343 CookiesSetFunction::~CookiesSetFunction() {
346 bool CookiesSetFunction::RunImpl() {
347 parsed_args_
= Set::Params::Create(*args_
);
348 EXTENSION_FUNCTION_VALIDATE(parsed_args_
.get());
350 // Read/validate input parameters.
351 if (!ParseUrl(this, parsed_args_
->details
.url
, &url_
, true))
354 std::string store_id
=
355 parsed_args_
->details
.store_id
.get() ? *parsed_args_
->details
.store_id
357 net::URLRequestContextGetter
* store_context
= NULL
;
358 if (!ParseStoreContext(this, &store_id
, &store_context
))
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
));
364 bool rv
= BrowserThread::PostTask(
365 BrowserThread::IO
, FROM_HERE
,
366 base::Bind(&CookiesSetFunction::SetCookieOnIOThread
, this));
369 // Will finish asynchronously.
373 void CookiesSetFunction::SetCookieOnIOThread() {
374 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
375 net::CookieMonster
* cookie_monster
=
376 store_browser_context_
->GetURLRequestContext()
378 ->GetCookieMonster();
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
);
389 cookie_monster
->SetCookieWithDetailsAsync(
391 parsed_args_
->details
.name
.get() ? *parsed_args_
->details
.name
393 parsed_args_
->details
.value
.get() ? *parsed_args_
->details
.value
395 parsed_args_
->details
.domain
.get() ? *parsed_args_
->details
.domain
397 parsed_args_
->details
.path
.get() ? *parsed_args_
->details
.path
400 parsed_args_
->details
.secure
.get() ? *parsed_args_
->details
.secure
.get()
402 parsed_args_
->details
.http_only
.get() ? *parsed_args_
->details
.http_only
404 net::COOKIE_PRIORITY_DEFAULT
,
405 base::Bind(&CookiesSetFunction::PullCookie
, this));
408 void CookiesSetFunction::PullCookie(bool set_cookie_result
) {
409 // Pull the newly set cookie.
410 net::CookieMonster
* cookie_monster
=
411 store_browser_context_
->GetURLRequestContext()
413 ->GetCookieMonster();
414 success_
= set_cookie_result
;
415 cookies_helpers::GetCookieListFromStore(
416 cookie_monster
, url_
,
417 base::Bind(&CookiesSetFunction::PullCookieCallback
, this));
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).
428 parsed_args_
->details
.name
.get() ? *parsed_args_
->details
.name
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
);
438 bool rv
= BrowserThread::PostTask(
439 BrowserThread::UI
, FROM_HERE
,
440 base::Bind(&CookiesSetFunction::RespondOnUIThread
, this));
444 void CookiesSetFunction::RespondOnUIThread() {
445 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
448 parsed_args_
->details
.name
.get() ? *parsed_args_
->details
.name
450 error_
= ErrorUtils::FormatErrorMessage(keys::kCookieSetFailedError
, name
);
452 SendResponse(success_
);
455 CookiesRemoveFunction::CookiesRemoveFunction() {
458 CookiesRemoveFunction::~CookiesRemoveFunction() {
461 bool CookiesRemoveFunction::RunImpl() {
462 parsed_args_
= Remove::Params::Create(*args_
);
463 EXTENSION_FUNCTION_VALIDATE(parsed_args_
.get());
465 // Read/validate input parameters.
466 if (!ParseUrl(this, parsed_args_
->details
.url
, &url_
, true))
469 std::string store_id
=
470 parsed_args_
->details
.store_id
.get() ? *parsed_args_
->details
.store_id
472 net::URLRequestContextGetter
* store_context
= NULL
;
473 if (!ParseStoreContext(this, &store_id
, &store_context
))
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
));
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));
485 // Will return asynchronously.
489 void CookiesRemoveFunction::RemoveCookieOnIOThread() {
490 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
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));
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
);
508 // Return to UI thread
509 bool rv
= BrowserThread::PostTask(
510 BrowserThread::UI
, FROM_HERE
,
511 base::Bind(&CookiesRemoveFunction::RespondOnUIThread
, this));
515 void CookiesRemoveFunction::RespondOnUIThread() {
516 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
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());
531 DCHECK(original_profile
!= incognito_profile
);
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());
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()));
552 if (incognito_tab_ids
.get() && incognito_tab_ids
->GetSize() > 0 &&
554 cookie_stores
.push_back(make_linked_ptr(
555 cookies_helpers::CreateCookieStore(
556 incognito_profile
, incognito_tab_ids
.release()).release()));
558 results_
= GetAllCookieStores::Results::Create(cookie_stores
);
562 CookiesAPI::CookiesAPI(content::BrowserContext
* context
)
563 : browser_context_(context
) {
564 EventRouter::Get(browser_context_
)
565 ->RegisterObserver(this, cookies::OnChanged::kEventName
);
568 CookiesAPI::~CookiesAPI() {
571 void CookiesAPI::Shutdown() {
572 EventRouter::Get(browser_context_
)->UnregisterObserver(this);
575 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<CookiesAPI
> >
576 g_factory
= LAZY_INSTANCE_INITIALIZER
;
579 BrowserContextKeyedAPIFactory
<CookiesAPI
>* CookiesAPI::GetFactoryInstance() {
580 return g_factory
.Pointer();
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);
589 } // namespace extensions