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/extensions/event_router.h"
22 #include "chrome/browser/extensions/extension_system.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_iterator.h"
26 #include "chrome/common/extensions/api/cookies.h"
27 #include "chrome/common/extensions/extension.h"
28 #include "chrome/common/extensions/permissions/permissions_data.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/notification_service.h"
31 #include "extensions/common/error_utils.h"
32 #include "net/cookies/canonical_cookie.h"
33 #include "net/cookies/cookie_constants.h"
34 #include "net/cookies/cookie_monster.h"
35 #include "net/url_request/url_request_context.h"
36 #include "net/url_request/url_request_context_getter.h"
38 using content::BrowserThread;
39 using extensions::api::cookies::Cookie;
40 using extensions::api::cookies::CookieStore;
42 namespace Get = extensions::api::cookies::Get;
43 namespace GetAll = extensions::api::cookies::GetAll;
44 namespace GetAllCookieStores = extensions::api::cookies::GetAllCookieStores;
45 namespace Remove = extensions::api::cookies::Remove;
46 namespace Set = extensions::api::cookies::Set;
48 namespace extensions {
49 namespace cookies = api::cookies;
50 namespace keys = cookies_api_constants;
52 CookiesEventRouter::CookiesEventRouter(Profile* profile)
54 CHECK(registrar_.IsEmpty());
56 chrome::NOTIFICATION_COOKIE_CHANGED,
57 content::NotificationService::AllBrowserContextsAndSources());
60 CookiesEventRouter::~CookiesEventRouter() {
63 void CookiesEventRouter::Observe(
65 const content::NotificationSource& source,
66 const content::NotificationDetails& details) {
67 Profile* profile = content::Source<Profile>(source).ptr();
68 if (!profile_->IsSameProfile(profile))
72 case chrome::NOTIFICATION_COOKIE_CHANGED:
75 content::Details<ChromeCookieDetails>(details).ptr());
83 void CookiesEventRouter::CookieChanged(
85 ChromeCookieDetails* details) {
86 scoped_ptr<base::ListValue> args(new base::ListValue());
87 base::DictionaryValue* dict = new base::DictionaryValue();
88 dict->SetBoolean(keys::kRemovedKey, details->removed);
90 scoped_ptr<Cookie> cookie(
91 cookies_helpers::CreateCookie(*details->cookie,
92 cookies_helpers::GetStoreIdFromProfile(profile_)));
93 dict->Set(keys::kCookieKey, cookie->ToValue().release());
95 // Map the internal cause to an external string.
97 switch (details->cause) {
98 case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT:
99 cause = keys::kExplicitChangeCause;
102 case net::CookieMonster::Delegate::CHANGE_COOKIE_OVERWRITE:
103 cause = keys::kOverwriteChangeCause;
106 case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED:
107 cause = keys::kExpiredChangeCause;
110 case net::CookieMonster::Delegate::CHANGE_COOKIE_EVICTED:
111 cause = keys::kEvictedChangeCause;
114 case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED_OVERWRITE:
115 cause = keys::kExpiredOverwriteChangeCause;
121 dict->SetString(keys::kCauseKey, cause);
126 cookies_helpers::GetURLFromCanonicalCookie(*details->cookie);
127 DispatchEvent(profile,
128 cookies::OnChanged::kEventName,
133 void CookiesEventRouter::DispatchEvent(
135 const std::string& event_name,
136 scoped_ptr<base::ListValue> event_args,
137 GURL& cookie_domain) {
138 EventRouter* router = profile ?
139 extensions::ExtensionSystem::Get(profile)->event_router() : NULL;
142 scoped_ptr<Event> event(new Event(event_name, event_args.Pass()));
143 event->restrict_to_profile = profile;
144 event->event_url = cookie_domain;
145 router->BroadcastEvent(event.Pass());
148 bool CookiesFunction::ParseUrl(const std::string& url_string, GURL* url,
149 bool check_host_permissions) {
150 *url = GURL(url_string);
151 if (!url->is_valid()) {
152 error_ = ErrorUtils::FormatErrorMessage(
153 keys::kInvalidUrlError, url_string);
156 // Check against host permissions if needed.
157 if (check_host_permissions &&
158 !PermissionsData::HasHostPermission(GetExtension(), *url)) {
159 error_ = ErrorUtils::FormatErrorMessage(
160 keys::kNoHostPermissionsError, url->spec());
166 bool CookiesFunction::ParseStoreContext(
167 std::string* store_id,
168 net::URLRequestContextGetter** context) {
169 DCHECK((context || store_id->empty()));
170 Profile* store_profile = NULL;
171 if (!store_id->empty()) {
172 store_profile = cookies_helpers::ChooseProfileFromStoreId(
173 *store_id, GetProfile(), include_incognito());
174 if (!store_profile) {
175 error_ = ErrorUtils::FormatErrorMessage(
176 keys::kInvalidStoreIdError, *store_id);
180 // The store ID was not specified; use the current execution context's
181 // cookie store by default.
182 // GetCurrentBrowser() already takes into account incognito settings.
183 Browser* current_browser = GetCurrentBrowser();
184 if (!current_browser) {
185 error_ = keys::kNoCookieStoreFoundError;
188 store_profile = current_browser->profile();
189 *store_id = cookies_helpers::GetStoreIdFromProfile(store_profile);
193 *context = store_profile->GetRequestContext();
199 CookiesGetFunction::CookiesGetFunction() {
202 CookiesGetFunction::~CookiesGetFunction() {
205 bool CookiesGetFunction::RunImpl() {
206 parsed_args_ = Get::Params::Create(*args_);
207 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
209 // Read/validate input parameters.
210 if (!ParseUrl(parsed_args_->details.url, &url_, true))
213 std::string store_id =
214 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
216 net::URLRequestContextGetter* store_context = NULL;
217 if (!ParseStoreContext(&store_id, &store_context))
219 store_context_ = store_context;
220 if (!parsed_args_->details.store_id.get())
221 parsed_args_->details.store_id.reset(new std::string(store_id));
223 store_context_ = store_context;
225 bool rv = BrowserThread::PostTask(
226 BrowserThread::IO, FROM_HERE,
227 base::Bind(&CookiesGetFunction::GetCookieOnIOThread, this));
230 // Will finish asynchronously.
234 void CookiesGetFunction::GetCookieOnIOThread() {
235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
236 net::CookieStore* cookie_store =
237 store_context_->GetURLRequestContext()->cookie_store();
238 cookies_helpers::GetCookieListFromStore(
240 base::Bind(&CookiesGetFunction::GetCookieCallback, this));
243 void CookiesGetFunction::GetCookieCallback(const net::CookieList& cookie_list) {
244 net::CookieList::const_iterator it;
245 for (it = cookie_list.begin(); it != cookie_list.end(); ++it) {
246 // Return the first matching cookie. Relies on the fact that the
247 // CookieMonster returns them in canonical order (longest path, then
248 // earliest creation time).
249 if (it->Name() == parsed_args_->details.name) {
250 scoped_ptr<Cookie> cookie(
251 cookies_helpers::CreateCookie(*it, *parsed_args_->details.store_id));
252 results_ = Get::Results::Create(*cookie);
257 // The cookie doesn't exist; return null.
258 if (it == cookie_list.end())
259 SetResult(Value::CreateNullValue());
261 bool rv = BrowserThread::PostTask(
262 BrowserThread::UI, FROM_HERE,
263 base::Bind(&CookiesGetFunction::RespondOnUIThread, this));
267 void CookiesGetFunction::RespondOnUIThread() {
268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
272 CookiesGetAllFunction::CookiesGetAllFunction() {
275 CookiesGetAllFunction::~CookiesGetAllFunction() {
278 bool CookiesGetAllFunction::RunImpl() {
279 parsed_args_ = GetAll::Params::Create(*args_);
280 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
282 if (parsed_args_->details.url.get() &&
283 !ParseUrl(*parsed_args_->details.url, &url_, false)) {
287 std::string store_id =
288 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
290 net::URLRequestContextGetter* store_context = NULL;
291 if (!ParseStoreContext(&store_id, &store_context))
293 store_context_ = store_context;
294 if (!parsed_args_->details.store_id.get())
295 parsed_args_->details.store_id.reset(new std::string(store_id));
297 bool rv = BrowserThread::PostTask(
298 BrowserThread::IO, FROM_HERE,
299 base::Bind(&CookiesGetAllFunction::GetAllCookiesOnIOThread, this));
302 // Will finish asynchronously.
306 void CookiesGetAllFunction::GetAllCookiesOnIOThread() {
307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
308 net::CookieStore* cookie_store =
309 store_context_->GetURLRequestContext()->cookie_store();
310 cookies_helpers::GetCookieListFromStore(
312 base::Bind(&CookiesGetAllFunction::GetAllCookiesCallback, this));
315 void CookiesGetAllFunction::GetAllCookiesCallback(
316 const net::CookieList& cookie_list) {
317 const extensions::Extension* extension = GetExtension();
319 std::vector<linked_ptr<Cookie> > match_vector;
320 cookies_helpers::AppendMatchingCookiesToVector(
321 cookie_list, url_, &parsed_args_->details,
322 GetExtension(), &match_vector);
324 results_ = GetAll::Results::Create(match_vector);
326 bool rv = BrowserThread::PostTask(
327 BrowserThread::UI, FROM_HERE,
328 base::Bind(&CookiesGetAllFunction::RespondOnUIThread, this));
332 void CookiesGetAllFunction::RespondOnUIThread() {
333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
337 CookiesSetFunction::CookiesSetFunction() : success_(false) {
340 CookiesSetFunction::~CookiesSetFunction() {
343 bool CookiesSetFunction::RunImpl() {
344 parsed_args_ = Set::Params::Create(*args_);
345 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
347 // Read/validate input parameters.
348 if (!ParseUrl(parsed_args_->details.url, &url_, true))
351 std::string store_id =
352 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
354 net::URLRequestContextGetter* store_context = NULL;
355 if (!ParseStoreContext(&store_id, &store_context))
357 store_context_ = store_context;
358 if (!parsed_args_->details.store_id.get())
359 parsed_args_->details.store_id.reset(new std::string(store_id));
361 bool rv = BrowserThread::PostTask(
362 BrowserThread::IO, FROM_HERE,
363 base::Bind(&CookiesSetFunction::SetCookieOnIOThread, this));
366 // Will finish asynchronously.
370 void CookiesSetFunction::SetCookieOnIOThread() {
371 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
372 net::CookieMonster* cookie_monster =
373 store_context_->GetURLRequestContext()->cookie_store()->
376 base::Time expiration_time;
377 if (parsed_args_->details.expiration_date.get()) {
378 // Time::FromDoubleT converts double time 0 to empty Time object. So we need
379 // to do special handling here.
380 expiration_time = (*parsed_args_->details.expiration_date == 0) ?
381 base::Time::UnixEpoch() :
382 base::Time::FromDoubleT(*parsed_args_->details.expiration_date);
385 cookie_monster->SetCookieWithDetailsAsync(
387 parsed_args_->details.name.get() ? *parsed_args_->details.name
389 parsed_args_->details.value.get() ? *parsed_args_->details.value
391 parsed_args_->details.domain.get() ? *parsed_args_->details.domain
393 parsed_args_->details.path.get() ? *parsed_args_->details.path
396 parsed_args_->details.secure.get() ? *parsed_args_->details.secure.get()
398 parsed_args_->details.http_only.get() ? *parsed_args_->details.http_only
400 net::COOKIE_PRIORITY_DEFAULT,
401 base::Bind(&CookiesSetFunction::PullCookie, this));
404 void CookiesSetFunction::PullCookie(bool set_cookie_result) {
405 // Pull the newly set cookie.
406 net::CookieMonster* cookie_monster =
407 store_context_->GetURLRequestContext()->cookie_store()->
409 success_ = set_cookie_result;
410 cookies_helpers::GetCookieListFromStore(
411 cookie_monster, url_,
412 base::Bind(&CookiesSetFunction::PullCookieCallback, this));
415 void CookiesSetFunction::PullCookieCallback(
416 const net::CookieList& cookie_list) {
417 net::CookieList::const_iterator it;
418 for (it = cookie_list.begin(); it != cookie_list.end(); ++it) {
419 // Return the first matching cookie. Relies on the fact that the
420 // CookieMonster returns them in canonical order (longest path, then
421 // earliest creation time).
423 parsed_args_->details.name.get() ? *parsed_args_->details.name
425 if (it->Name() == name) {
426 scoped_ptr<Cookie> cookie(
427 cookies_helpers::CreateCookie(*it, *parsed_args_->details.store_id));
428 results_ = Set::Results::Create(*cookie);
433 bool rv = BrowserThread::PostTask(
434 BrowserThread::UI, FROM_HERE,
435 base::Bind(&CookiesSetFunction::RespondOnUIThread, this));
439 void CookiesSetFunction::RespondOnUIThread() {
440 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
443 parsed_args_->details.name.get() ? *parsed_args_->details.name
445 error_ = ErrorUtils::FormatErrorMessage(keys::kCookieSetFailedError, name);
447 SendResponse(success_);
450 CookiesRemoveFunction::CookiesRemoveFunction() {
453 CookiesRemoveFunction::~CookiesRemoveFunction() {
456 bool CookiesRemoveFunction::RunImpl() {
457 parsed_args_ = Remove::Params::Create(*args_);
458 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
460 // Read/validate input parameters.
461 if (!ParseUrl(parsed_args_->details.url, &url_, true))
464 std::string store_id =
465 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
467 net::URLRequestContextGetter* store_context = NULL;
468 if (!ParseStoreContext(&store_id, &store_context))
470 store_context_ = store_context;
471 if (!parsed_args_->details.store_id.get())
472 parsed_args_->details.store_id.reset(new std::string(store_id));
474 // Pass the work off to the IO thread.
475 bool rv = BrowserThread::PostTask(
476 BrowserThread::IO, FROM_HERE,
477 base::Bind(&CookiesRemoveFunction::RemoveCookieOnIOThread, this));
480 // Will return asynchronously.
484 void CookiesRemoveFunction::RemoveCookieOnIOThread() {
485 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
488 net::CookieStore* cookie_store =
489 store_context_->GetURLRequestContext()->cookie_store();
490 cookie_store->DeleteCookieAsync(
491 url_, parsed_args_->details.name,
492 base::Bind(&CookiesRemoveFunction::RemoveCookieCallback, this));
495 void CookiesRemoveFunction::RemoveCookieCallback() {
496 // Build the callback result
497 Remove::Results::Details details;
498 details.name = parsed_args_->details.name;
499 details.url = url_.spec();
500 details.store_id = *parsed_args_->details.store_id;
501 results_ = Remove::Results::Create(details);
503 // Return to UI thread
504 bool rv = BrowserThread::PostTask(
505 BrowserThread::UI, FROM_HERE,
506 base::Bind(&CookiesRemoveFunction::RespondOnUIThread, this));
510 void CookiesRemoveFunction::RespondOnUIThread() {
511 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
515 bool CookiesGetAllCookieStoresFunction::RunImpl() {
516 Profile* original_profile = GetProfile();
517 DCHECK(original_profile);
518 scoped_ptr<base::ListValue> original_tab_ids(new base::ListValue());
519 Profile* incognito_profile = NULL;
520 scoped_ptr<base::ListValue> incognito_tab_ids;
521 if (include_incognito() && GetProfile()->HasOffTheRecordProfile()) {
522 incognito_profile = GetProfile()->GetOffTheRecordProfile();
523 if (incognito_profile)
524 incognito_tab_ids.reset(new base::ListValue());
526 DCHECK(original_profile != incognito_profile);
528 // Iterate through all browser instances, and for each browser,
529 // add its tab IDs to either the regular or incognito tab ID list depending
530 // whether the browser is regular or incognito.
531 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
532 Browser* browser = *it;
533 if (browser->profile() == original_profile) {
534 cookies_helpers::AppendToTabIdList(browser, original_tab_ids.get());
535 } else if (incognito_tab_ids.get() &&
536 browser->profile() == incognito_profile) {
537 cookies_helpers::AppendToTabIdList(browser, incognito_tab_ids.get());
540 // Return a list of all cookie stores with at least one open tab.
541 std::vector<linked_ptr<CookieStore> > cookie_stores;
542 if (original_tab_ids->GetSize() > 0) {
543 cookie_stores.push_back(make_linked_ptr(
544 cookies_helpers::CreateCookieStore(
545 original_profile, original_tab_ids.release()).release()));
547 if (incognito_tab_ids.get() && incognito_tab_ids->GetSize() > 0 &&
549 cookie_stores.push_back(make_linked_ptr(
550 cookies_helpers::CreateCookieStore(
551 incognito_profile, incognito_tab_ids.release()).release()));
553 results_ = GetAllCookieStores::Results::Create(cookie_stores);
557 void CookiesGetAllCookieStoresFunction::Run() {
558 SendResponse(RunImpl());
561 CookiesAPI::CookiesAPI(Profile* profile)
562 : profile_(profile) {
563 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
564 this, cookies::OnChanged::kEventName);
567 CookiesAPI::~CookiesAPI() {
570 void CookiesAPI::Shutdown() {
571 ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
574 static base::LazyInstance<ProfileKeyedAPIFactory<CookiesAPI> >
575 g_factory = LAZY_INSTANCE_INITIALIZER;
578 ProfileKeyedAPIFactory<CookiesAPI>* CookiesAPI::GetFactoryInstance() {
579 return &g_factory.Get();
582 void CookiesAPI::OnListenerAdded(
583 const extensions::EventListenerInfo& details) {
584 cookies_event_router_.reset(new CookiesEventRouter(profile_));
585 ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
588 } // namespace extensions