Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / api / cookies / cookies_api.cc
blobe9a502525025a131fc37f082b5ac48536965ef2a
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"
9 #include <vector>
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;
39 namespace extensions {
41 namespace cookies = api::cookies;
42 namespace keys = cookies_api_constants;
43 namespace Get = cookies::Get;
44 namespace GetAll = cookies::GetAll;
45 namespace GetAllCookieStores = cookies::GetAllCookieStores;
46 namespace Remove = cookies::Remove;
47 namespace Set = cookies::Set;
49 namespace {
51 bool ParseUrl(ChromeAsyncExtensionFunction* function,
52 const std::string& url_string,
53 GURL* url,
54 bool check_host_permissions) {
55 *url = GURL(url_string);
56 if (!url->is_valid()) {
57 function->SetError(
58 ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, url_string));
59 return false;
61 // Check against host permissions if needed.
62 if (check_host_permissions &&
63 !function->extension()->permissions_data()->HasHostPermission(*url)) {
64 function->SetError(ErrorUtils::FormatErrorMessage(
65 keys::kNoHostPermissionsError, url->spec()));
66 return false;
68 return true;
71 bool ParseStoreContext(ChromeAsyncExtensionFunction* function,
72 std::string* store_id,
73 net::URLRequestContextGetter** context) {
74 DCHECK((context || store_id->empty()));
75 Profile* store_profile = NULL;
76 if (!store_id->empty()) {
77 store_profile = cookies_helpers::ChooseProfileFromStoreId(
78 *store_id, function->GetProfile(), function->include_incognito());
79 if (!store_profile) {
80 function->SetError(ErrorUtils::FormatErrorMessage(
81 keys::kInvalidStoreIdError, *store_id));
82 return false;
84 } else {
85 // The store ID was not specified; use the current execution context's
86 // cookie store by default.
87 // GetCurrentBrowser() already takes into account incognito settings.
88 Browser* current_browser = function->GetCurrentBrowser();
89 if (!current_browser) {
90 function->SetError(keys::kNoCookieStoreFoundError);
91 return false;
93 store_profile = current_browser->profile();
94 *store_id = cookies_helpers::GetStoreIdFromProfile(store_profile);
97 if (context)
98 *context = store_profile->GetRequestContext();
99 DCHECK(context);
101 return true;
104 } // namespace
106 CookiesEventRouter::CookiesEventRouter(content::BrowserContext* context)
107 : profile_(Profile::FromBrowserContext(context)) {
108 CHECK(registrar_.IsEmpty());
109 registrar_.Add(this,
110 chrome::NOTIFICATION_COOKIE_CHANGED_FOR_EXTENSIONS,
111 content::NotificationService::AllBrowserContextsAndSources());
114 CookiesEventRouter::~CookiesEventRouter() {
117 void CookiesEventRouter::Observe(
118 int type,
119 const content::NotificationSource& source,
120 const content::NotificationDetails& details) {
121 Profile* profile = content::Source<Profile>(source).ptr();
122 if (!profile_->IsSameProfile(profile))
123 return;
125 switch (type) {
126 case chrome::NOTIFICATION_COOKIE_CHANGED_FOR_EXTENSIONS:
127 CookieChanged(
128 profile,
129 content::Details<ChromeCookieDetails>(details).ptr());
130 break;
132 default:
133 NOTREACHED();
137 void CookiesEventRouter::CookieChanged(
138 Profile* profile,
139 ChromeCookieDetails* details) {
140 scoped_ptr<base::ListValue> args(new base::ListValue());
141 base::DictionaryValue* dict = new base::DictionaryValue();
142 dict->SetBoolean(keys::kRemovedKey, details->removed);
144 scoped_ptr<cookies::Cookie> cookie(cookies_helpers::CreateCookie(
145 *details->cookie, cookies_helpers::GetStoreIdFromProfile(profile)));
146 dict->Set(keys::kCookieKey, cookie->ToValue().release());
148 // Map the internal cause to an external string.
149 std::string cause;
150 switch (details->cause) {
151 case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT:
152 cause = keys::kExplicitChangeCause;
153 break;
155 case net::CookieMonster::Delegate::CHANGE_COOKIE_OVERWRITE:
156 cause = keys::kOverwriteChangeCause;
157 break;
159 case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED:
160 cause = keys::kExpiredChangeCause;
161 break;
163 case net::CookieMonster::Delegate::CHANGE_COOKIE_EVICTED:
164 cause = keys::kEvictedChangeCause;
165 break;
167 case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED_OVERWRITE:
168 cause = keys::kExpiredOverwriteChangeCause;
169 break;
171 default:
172 NOTREACHED();
174 dict->SetString(keys::kCauseKey, cause);
176 args->Append(dict);
178 GURL cookie_domain =
179 cookies_helpers::GetURLFromCanonicalCookie(*details->cookie);
180 DispatchEvent(profile, events::COOKIES_ON_CHANGED,
181 cookies::OnChanged::kEventName, args.Pass(), cookie_domain);
184 void CookiesEventRouter::DispatchEvent(content::BrowserContext* context,
185 events::HistogramValue histogram_value,
186 const std::string& event_name,
187 scoped_ptr<base::ListValue> event_args,
188 GURL& cookie_domain) {
189 EventRouter* router = context ? EventRouter::Get(context) : NULL;
190 if (!router)
191 return;
192 scoped_ptr<Event> event(
193 new Event(histogram_value, event_name, event_args.Pass()));
194 event->restrict_to_browser_context = context;
195 event->event_url = cookie_domain;
196 router->BroadcastEvent(event.Pass());
199 CookiesGetFunction::CookiesGetFunction() {
202 CookiesGetFunction::~CookiesGetFunction() {
205 bool CookiesGetFunction::RunAsync() {
206 parsed_args_ = Get::Params::Create(*args_);
207 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
209 // Read/validate input parameters.
210 if (!ParseUrl(this, parsed_args_->details.url, &url_, true))
211 return false;
213 std::string store_id =
214 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
215 : std::string();
216 net::URLRequestContextGetter* store_context = NULL;
217 if (!ParseStoreContext(this, &store_id, &store_context))
218 return false;
219 store_browser_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_browser_context_ = store_context;
225 bool rv = BrowserThread::PostTask(
226 BrowserThread::IO, FROM_HERE,
227 base::Bind(&CookiesGetFunction::GetCookieOnIOThread, this));
228 DCHECK(rv);
230 // Will finish asynchronously.
231 return true;
234 void CookiesGetFunction::GetCookieOnIOThread() {
235 DCHECK_CURRENTLY_ON(BrowserThread::IO);
236 net::CookieStore* cookie_store =
237 store_browser_context_->GetURLRequestContext()->cookie_store();
238 cookies_helpers::GetCookieListFromStore(
239 cookie_store, url_,
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<cookies::Cookie> cookie(
251 cookies_helpers::CreateCookie(*it, *parsed_args_->details.store_id));
252 results_ = Get::Results::Create(*cookie);
253 break;
257 // The cookie doesn't exist; return null.
258 if (it == cookie_list.end())
259 SetResult(base::Value::CreateNullValue());
261 bool rv = BrowserThread::PostTask(
262 BrowserThread::UI, FROM_HERE,
263 base::Bind(&CookiesGetFunction::RespondOnUIThread, this));
264 DCHECK(rv);
267 void CookiesGetFunction::RespondOnUIThread() {
268 DCHECK_CURRENTLY_ON(BrowserThread::UI);
269 SendResponse(true);
272 CookiesGetAllFunction::CookiesGetAllFunction() {
275 CookiesGetAllFunction::~CookiesGetAllFunction() {
278 bool CookiesGetAllFunction::RunAsync() {
279 parsed_args_ = GetAll::Params::Create(*args_);
280 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
282 if (parsed_args_->details.url.get() &&
283 !ParseUrl(this, *parsed_args_->details.url, &url_, false)) {
284 return false;
287 std::string store_id =
288 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
289 : std::string();
290 net::URLRequestContextGetter* store_context = NULL;
291 if (!ParseStoreContext(this, &store_id, &store_context))
292 return false;
293 store_browser_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));
300 DCHECK(rv);
302 // Will finish asynchronously.
303 return true;
306 void CookiesGetAllFunction::GetAllCookiesOnIOThread() {
307 DCHECK_CURRENTLY_ON(BrowserThread::IO);
308 net::CookieStore* cookie_store =
309 store_browser_context_->GetURLRequestContext()->cookie_store();
310 cookies_helpers::GetCookieListFromStore(
311 cookie_store, url_,
312 base::Bind(&CookiesGetAllFunction::GetAllCookiesCallback, this));
315 void CookiesGetAllFunction::GetAllCookiesCallback(
316 const net::CookieList& cookie_list) {
317 if (extension()) {
318 std::vector<linked_ptr<cookies::Cookie>> match_vector;
319 cookies_helpers::AppendMatchingCookiesToVector(
320 cookie_list, url_, &parsed_args_->details, extension(), &match_vector);
322 results_ = GetAll::Results::Create(match_vector);
324 bool rv = BrowserThread::PostTask(
325 BrowserThread::UI, FROM_HERE,
326 base::Bind(&CookiesGetAllFunction::RespondOnUIThread, this));
327 DCHECK(rv);
330 void CookiesGetAllFunction::RespondOnUIThread() {
331 DCHECK_CURRENTLY_ON(BrowserThread::UI);
332 SendResponse(true);
335 CookiesSetFunction::CookiesSetFunction() : success_(false) {
338 CookiesSetFunction::~CookiesSetFunction() {
341 bool CookiesSetFunction::RunAsync() {
342 parsed_args_ = Set::Params::Create(*args_);
343 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
345 // Read/validate input parameters.
346 if (!ParseUrl(this, parsed_args_->details.url, &url_, true))
347 return false;
349 std::string store_id =
350 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
351 : std::string();
352 net::URLRequestContextGetter* store_context = NULL;
353 if (!ParseStoreContext(this, &store_id, &store_context))
354 return false;
355 store_browser_context_ = store_context;
356 if (!parsed_args_->details.store_id.get())
357 parsed_args_->details.store_id.reset(new std::string(store_id));
359 bool rv = BrowserThread::PostTask(
360 BrowserThread::IO, FROM_HERE,
361 base::Bind(&CookiesSetFunction::SetCookieOnIOThread, this));
362 DCHECK(rv);
364 // Will finish asynchronously.
365 return true;
368 void CookiesSetFunction::SetCookieOnIOThread() {
369 DCHECK_CURRENTLY_ON(BrowserThread::IO);
370 net::CookieMonster* cookie_monster =
371 store_browser_context_->GetURLRequestContext()
372 ->cookie_store()
373 ->GetCookieMonster();
375 base::Time expiration_time;
376 if (parsed_args_->details.expiration_date.get()) {
377 // Time::FromDoubleT converts double time 0 to empty Time object. So we need
378 // to do special handling here.
379 expiration_time = (*parsed_args_->details.expiration_date == 0) ?
380 base::Time::UnixEpoch() :
381 base::Time::FromDoubleT(*parsed_args_->details.expiration_date);
384 cookie_monster->SetCookieWithDetailsAsync(
385 url_,
386 parsed_args_->details.name.get() ? *parsed_args_->details.name
387 : std::string(),
388 parsed_args_->details.value.get() ? *parsed_args_->details.value
389 : std::string(),
390 parsed_args_->details.domain.get() ? *parsed_args_->details.domain
391 : std::string(),
392 parsed_args_->details.path.get() ? *parsed_args_->details.path
393 : std::string(),
394 expiration_time,
395 parsed_args_->details.secure.get() ? *parsed_args_->details.secure.get()
396 : false,
397 parsed_args_->details.http_only.get() ? *parsed_args_->details.http_only
398 : false,
399 // TODO(mkwst): If we decide to ship First-party-only cookies, we'll need
400 // to extend the extension API to support them. For the moment, we'll set
401 // all cookies as non-First-party-only.
402 false,
403 net::COOKIE_PRIORITY_DEFAULT,
404 base::Bind(&CookiesSetFunction::PullCookie, this));
407 void CookiesSetFunction::PullCookie(bool set_cookie_result) {
408 // Pull the newly set cookie.
409 net::CookieMonster* cookie_monster =
410 store_browser_context_->GetURLRequestContext()
411 ->cookie_store()
412 ->GetCookieMonster();
413 success_ = set_cookie_result;
414 cookies_helpers::GetCookieListFromStore(
415 cookie_monster, url_,
416 base::Bind(&CookiesSetFunction::PullCookieCallback, this));
419 void CookiesSetFunction::PullCookieCallback(
420 const net::CookieList& cookie_list) {
421 net::CookieList::const_iterator it;
422 for (it = cookie_list.begin(); it != cookie_list.end(); ++it) {
423 // Return the first matching cookie. Relies on the fact that the
424 // CookieMonster returns them in canonical order (longest path, then
425 // earliest creation time).
426 std::string name =
427 parsed_args_->details.name.get() ? *parsed_args_->details.name
428 : std::string();
429 if (it->Name() == name) {
430 scoped_ptr<cookies::Cookie> cookie(
431 cookies_helpers::CreateCookie(*it, *parsed_args_->details.store_id));
432 results_ = Set::Results::Create(*cookie);
433 break;
437 bool rv = BrowserThread::PostTask(
438 BrowserThread::UI, FROM_HERE,
439 base::Bind(&CookiesSetFunction::RespondOnUIThread, this));
440 DCHECK(rv);
443 void CookiesSetFunction::RespondOnUIThread() {
444 DCHECK_CURRENTLY_ON(BrowserThread::UI);
445 if (!success_) {
446 std::string name =
447 parsed_args_->details.name.get() ? *parsed_args_->details.name
448 : std::string();
449 error_ = ErrorUtils::FormatErrorMessage(keys::kCookieSetFailedError, name);
451 SendResponse(success_);
454 CookiesRemoveFunction::CookiesRemoveFunction() {
457 CookiesRemoveFunction::~CookiesRemoveFunction() {
460 bool CookiesRemoveFunction::RunAsync() {
461 parsed_args_ = Remove::Params::Create(*args_);
462 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
464 // Read/validate input parameters.
465 if (!ParseUrl(this, parsed_args_->details.url, &url_, true))
466 return false;
468 std::string store_id =
469 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
470 : std::string();
471 net::URLRequestContextGetter* store_context = NULL;
472 if (!ParseStoreContext(this, &store_id, &store_context))
473 return false;
474 store_browser_context_ = store_context;
475 if (!parsed_args_->details.store_id.get())
476 parsed_args_->details.store_id.reset(new std::string(store_id));
478 // Pass the work off to the IO thread.
479 bool rv = BrowserThread::PostTask(
480 BrowserThread::IO, FROM_HERE,
481 base::Bind(&CookiesRemoveFunction::RemoveCookieOnIOThread, this));
482 DCHECK(rv);
484 // Will return asynchronously.
485 return true;
488 void CookiesRemoveFunction::RemoveCookieOnIOThread() {
489 DCHECK_CURRENTLY_ON(BrowserThread::IO);
491 // Remove the cookie
492 net::CookieStore* cookie_store =
493 store_browser_context_->GetURLRequestContext()->cookie_store();
494 cookie_store->DeleteCookieAsync(
495 url_, parsed_args_->details.name,
496 base::Bind(&CookiesRemoveFunction::RemoveCookieCallback, this));
499 void CookiesRemoveFunction::RemoveCookieCallback() {
500 // Build the callback result
501 Remove::Results::Details details;
502 details.name = parsed_args_->details.name;
503 details.url = url_.spec();
504 details.store_id = *parsed_args_->details.store_id;
505 results_ = Remove::Results::Create(details);
507 // Return to UI thread
508 bool rv = BrowserThread::PostTask(
509 BrowserThread::UI, FROM_HERE,
510 base::Bind(&CookiesRemoveFunction::RespondOnUIThread, this));
511 DCHECK(rv);
514 void CookiesRemoveFunction::RespondOnUIThread() {
515 DCHECK_CURRENTLY_ON(BrowserThread::UI);
516 SendResponse(true);
519 bool CookiesGetAllCookieStoresFunction::RunSync() {
520 Profile* original_profile = GetProfile();
521 DCHECK(original_profile);
522 scoped_ptr<base::ListValue> original_tab_ids(new base::ListValue());
523 Profile* incognito_profile = NULL;
524 scoped_ptr<base::ListValue> incognito_tab_ids;
525 if (include_incognito() && GetProfile()->HasOffTheRecordProfile()) {
526 incognito_profile = GetProfile()->GetOffTheRecordProfile();
527 if (incognito_profile)
528 incognito_tab_ids.reset(new base::ListValue());
530 DCHECK(original_profile != incognito_profile);
532 // Iterate through all browser instances, and for each browser,
533 // add its tab IDs to either the regular or incognito tab ID list depending
534 // whether the browser is regular or incognito.
535 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
536 Browser* browser = *it;
537 if (browser->profile() == original_profile) {
538 cookies_helpers::AppendToTabIdList(browser, original_tab_ids.get());
539 } else if (incognito_tab_ids.get() &&
540 browser->profile() == incognito_profile) {
541 cookies_helpers::AppendToTabIdList(browser, incognito_tab_ids.get());
544 // Return a list of all cookie stores with at least one open tab.
545 std::vector<linked_ptr<cookies::CookieStore>> cookie_stores;
546 if (original_tab_ids->GetSize() > 0) {
547 cookie_stores.push_back(make_linked_ptr(
548 cookies_helpers::CreateCookieStore(
549 original_profile, original_tab_ids.release()).release()));
551 if (incognito_tab_ids.get() && incognito_tab_ids->GetSize() > 0 &&
552 incognito_profile) {
553 cookie_stores.push_back(make_linked_ptr(
554 cookies_helpers::CreateCookieStore(
555 incognito_profile, incognito_tab_ids.release()).release()));
557 results_ = GetAllCookieStores::Results::Create(cookie_stores);
558 return true;
561 CookiesAPI::CookiesAPI(content::BrowserContext* context)
562 : browser_context_(context) {
563 EventRouter::Get(browser_context_)
564 ->RegisterObserver(this, cookies::OnChanged::kEventName);
567 CookiesAPI::~CookiesAPI() {
570 void CookiesAPI::Shutdown() {
571 EventRouter::Get(browser_context_)->UnregisterObserver(this);
574 static base::LazyInstance<BrowserContextKeyedAPIFactory<CookiesAPI> >
575 g_factory = LAZY_INSTANCE_INITIALIZER;
577 // static
578 BrowserContextKeyedAPIFactory<CookiesAPI>* CookiesAPI::GetFactoryInstance() {
579 return g_factory.Pointer();
582 void CookiesAPI::OnListenerAdded(const EventListenerInfo& details) {
583 cookies_event_router_.reset(new CookiesEventRouter(browser_context_));
584 EventRouter::Get(browser_context_)->UnregisterObserver(this);
587 } // namespace extensions