- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / cookies / cookies_api.cc
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/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"
37
38 using content::BrowserThread;
39 using extensions::api::cookies::Cookie;
40 using extensions::api::cookies::CookieStore;
41
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;
47
48 namespace extensions {
49 namespace cookies = api::cookies;
50 namespace keys = cookies_api_constants;
51
52 CookiesEventRouter::CookiesEventRouter(Profile* profile)
53     : profile_(profile) {
54   CHECK(registrar_.IsEmpty());
55   registrar_.Add(this,
56                  chrome::NOTIFICATION_COOKIE_CHANGED,
57                  content::NotificationService::AllBrowserContextsAndSources());
58 }
59
60 CookiesEventRouter::~CookiesEventRouter() {
61 }
62
63 void CookiesEventRouter::Observe(
64     int type,
65     const content::NotificationSource& source,
66     const content::NotificationDetails& details) {
67   Profile* profile = content::Source<Profile>(source).ptr();
68   if (!profile_->IsSameProfile(profile))
69     return;
70
71   switch (type) {
72     case chrome::NOTIFICATION_COOKIE_CHANGED:
73       CookieChanged(
74           profile,
75           content::Details<ChromeCookieDetails>(details).ptr());
76       break;
77
78     default:
79       NOTREACHED();
80   }
81 }
82
83 void CookiesEventRouter::CookieChanged(
84     Profile* profile,
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);
89
90   scoped_ptr<Cookie> cookie(
91       cookies_helpers::CreateCookie(*details->cookie,
92           cookies_helpers::GetStoreIdFromProfile(profile_)));
93   dict->Set(keys::kCookieKey, cookie->ToValue().release());
94
95   // Map the internal cause to an external string.
96   std::string cause;
97   switch (details->cause) {
98     case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT:
99       cause = keys::kExplicitChangeCause;
100       break;
101
102     case net::CookieMonster::Delegate::CHANGE_COOKIE_OVERWRITE:
103       cause = keys::kOverwriteChangeCause;
104       break;
105
106     case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED:
107       cause = keys::kExpiredChangeCause;
108       break;
109
110     case net::CookieMonster::Delegate::CHANGE_COOKIE_EVICTED:
111       cause = keys::kEvictedChangeCause;
112       break;
113
114     case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED_OVERWRITE:
115       cause = keys::kExpiredOverwriteChangeCause;
116       break;
117
118     default:
119       NOTREACHED();
120   }
121   dict->SetString(keys::kCauseKey, cause);
122
123   args->Append(dict);
124
125   GURL cookie_domain =
126       cookies_helpers::GetURLFromCanonicalCookie(*details->cookie);
127   DispatchEvent(profile,
128                 cookies::OnChanged::kEventName,
129                 args.Pass(),
130                 cookie_domain);
131 }
132
133 void CookiesEventRouter::DispatchEvent(
134     Profile* profile,
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;
140   if (!router)
141     return;
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());
146 }
147
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);
154     return false;
155   }
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());
161     return false;
162   }
163   return true;
164 }
165
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);
177       return false;
178     }
179   } else {
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;
186       return false;
187     }
188     store_profile = current_browser->profile();
189     *store_id = cookies_helpers::GetStoreIdFromProfile(store_profile);
190   }
191
192   if (context)
193     *context = store_profile->GetRequestContext();
194   DCHECK(context);
195
196   return true;
197 }
198
199 CookiesGetFunction::CookiesGetFunction() {
200 }
201
202 CookiesGetFunction::~CookiesGetFunction() {
203 }
204
205 bool CookiesGetFunction::RunImpl() {
206   parsed_args_ = Get::Params::Create(*args_);
207   EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
208
209   // Read/validate input parameters.
210   if (!ParseUrl(parsed_args_->details.url, &url_, true))
211     return false;
212
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(&store_id, &store_context))
218     return false;
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));
222
223   store_context_ = store_context;
224
225   bool rv = BrowserThread::PostTask(
226       BrowserThread::IO, FROM_HERE,
227       base::Bind(&CookiesGetFunction::GetCookieOnIOThread, this));
228   DCHECK(rv);
229
230   // Will finish asynchronously.
231   return true;
232 }
233
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(
239       cookie_store, url_,
240       base::Bind(&CookiesGetFunction::GetCookieCallback, this));
241 }
242
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);
253       break;
254     }
255   }
256
257   // The cookie doesn't exist; return null.
258   if (it == cookie_list.end())
259     SetResult(Value::CreateNullValue());
260
261   bool rv = BrowserThread::PostTask(
262       BrowserThread::UI, FROM_HERE,
263       base::Bind(&CookiesGetFunction::RespondOnUIThread, this));
264   DCHECK(rv);
265 }
266
267 void CookiesGetFunction::RespondOnUIThread() {
268   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
269   SendResponse(true);
270 }
271
272 CookiesGetAllFunction::CookiesGetAllFunction() {
273 }
274
275 CookiesGetAllFunction::~CookiesGetAllFunction() {
276 }
277
278 bool CookiesGetAllFunction::RunImpl() {
279   parsed_args_ = GetAll::Params::Create(*args_);
280   EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
281
282   if (parsed_args_->details.url.get() &&
283       !ParseUrl(*parsed_args_->details.url, &url_, false)) {
284     return false;
285   }
286
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(&store_id, &store_context))
292     return false;
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));
296
297   bool rv = BrowserThread::PostTask(
298       BrowserThread::IO, FROM_HERE,
299       base::Bind(&CookiesGetAllFunction::GetAllCookiesOnIOThread, this));
300   DCHECK(rv);
301
302   // Will finish asynchronously.
303   return true;
304 }
305
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(
311       cookie_store, url_,
312       base::Bind(&CookiesGetAllFunction::GetAllCookiesCallback, this));
313 }
314
315 void CookiesGetAllFunction::GetAllCookiesCallback(
316     const net::CookieList& cookie_list) {
317   const extensions::Extension* extension = GetExtension();
318   if (extension) {
319     std::vector<linked_ptr<Cookie> > match_vector;
320     cookies_helpers::AppendMatchingCookiesToVector(
321         cookie_list, url_, &parsed_args_->details,
322         GetExtension(), &match_vector);
323
324     results_ = GetAll::Results::Create(match_vector);
325   }
326   bool rv = BrowserThread::PostTask(
327       BrowserThread::UI, FROM_HERE,
328       base::Bind(&CookiesGetAllFunction::RespondOnUIThread, this));
329   DCHECK(rv);
330 }
331
332 void CookiesGetAllFunction::RespondOnUIThread() {
333   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
334   SendResponse(true);
335 }
336
337 CookiesSetFunction::CookiesSetFunction() : success_(false) {
338 }
339
340 CookiesSetFunction::~CookiesSetFunction() {
341 }
342
343 bool CookiesSetFunction::RunImpl() {
344   parsed_args_ = Set::Params::Create(*args_);
345   EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
346
347   // Read/validate input parameters.
348   if (!ParseUrl(parsed_args_->details.url, &url_, true))
349       return false;
350
351   std::string store_id =
352       parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
353                                            : std::string();
354   net::URLRequestContextGetter* store_context = NULL;
355   if (!ParseStoreContext(&store_id, &store_context))
356     return false;
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));
360
361   bool rv = BrowserThread::PostTask(
362       BrowserThread::IO, FROM_HERE,
363       base::Bind(&CookiesSetFunction::SetCookieOnIOThread, this));
364   DCHECK(rv);
365
366   // Will finish asynchronously.
367   return true;
368 }
369
370 void CookiesSetFunction::SetCookieOnIOThread() {
371   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
372   net::CookieMonster* cookie_monster =
373       store_context_->GetURLRequestContext()->cookie_store()->
374       GetCookieMonster();
375
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);
383   }
384
385   cookie_monster->SetCookieWithDetailsAsync(
386       url_,
387       parsed_args_->details.name.get() ? *parsed_args_->details.name
388                                        : std::string(),
389       parsed_args_->details.value.get() ? *parsed_args_->details.value
390                                         : std::string(),
391       parsed_args_->details.domain.get() ? *parsed_args_->details.domain
392                                          : std::string(),
393       parsed_args_->details.path.get() ? *parsed_args_->details.path
394                                        : std::string(),
395       expiration_time,
396       parsed_args_->details.secure.get() ? *parsed_args_->details.secure.get()
397                                          : false,
398       parsed_args_->details.http_only.get() ? *parsed_args_->details.http_only
399                                             : false,
400       net::COOKIE_PRIORITY_DEFAULT,
401       base::Bind(&CookiesSetFunction::PullCookie, this));
402 }
403
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()->
408       GetCookieMonster();
409   success_ = set_cookie_result;
410   cookies_helpers::GetCookieListFromStore(
411       cookie_monster, url_,
412       base::Bind(&CookiesSetFunction::PullCookieCallback, this));
413 }
414
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).
422     std::string name =
423         parsed_args_->details.name.get() ? *parsed_args_->details.name
424                                          : std::string();
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);
429       break;
430     }
431   }
432
433   bool rv = BrowserThread::PostTask(
434       BrowserThread::UI, FROM_HERE,
435       base::Bind(&CookiesSetFunction::RespondOnUIThread, this));
436   DCHECK(rv);
437 }
438
439 void CookiesSetFunction::RespondOnUIThread() {
440   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
441   if (!success_) {
442     std::string name =
443         parsed_args_->details.name.get() ? *parsed_args_->details.name
444                                          : std::string();
445     error_ = ErrorUtils::FormatErrorMessage(keys::kCookieSetFailedError, name);
446   }
447   SendResponse(success_);
448 }
449
450 CookiesRemoveFunction::CookiesRemoveFunction() {
451 }
452
453 CookiesRemoveFunction::~CookiesRemoveFunction() {
454 }
455
456 bool CookiesRemoveFunction::RunImpl() {
457   parsed_args_ = Remove::Params::Create(*args_);
458   EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
459
460   // Read/validate input parameters.
461   if (!ParseUrl(parsed_args_->details.url, &url_, true))
462     return false;
463
464   std::string store_id =
465       parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id
466                                            : std::string();
467   net::URLRequestContextGetter* store_context = NULL;
468   if (!ParseStoreContext(&store_id, &store_context))
469     return false;
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));
473
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));
478   DCHECK(rv);
479
480   // Will return asynchronously.
481   return true;
482 }
483
484 void CookiesRemoveFunction::RemoveCookieOnIOThread() {
485   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
486
487   // Remove the cookie
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));
493 }
494
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);
502
503   // Return to UI thread
504   bool rv = BrowserThread::PostTask(
505       BrowserThread::UI, FROM_HERE,
506       base::Bind(&CookiesRemoveFunction::RespondOnUIThread, this));
507   DCHECK(rv);
508 }
509
510 void CookiesRemoveFunction::RespondOnUIThread() {
511   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
512   SendResponse(true);
513 }
514
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());
525   }
526   DCHECK(original_profile != incognito_profile);
527
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());
538     }
539   }
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()));
546   }
547   if (incognito_tab_ids.get() && incognito_tab_ids->GetSize() > 0 &&
548       incognito_profile) {
549     cookie_stores.push_back(make_linked_ptr(
550         cookies_helpers::CreateCookieStore(
551             incognito_profile, incognito_tab_ids.release()).release()));
552   }
553   results_ = GetAllCookieStores::Results::Create(cookie_stores);
554   return true;
555 }
556
557 void CookiesGetAllCookieStoresFunction::Run() {
558   SendResponse(RunImpl());
559 }
560
561 CookiesAPI::CookiesAPI(Profile* profile)
562     : profile_(profile) {
563   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
564       this, cookies::OnChanged::kEventName);
565 }
566
567 CookiesAPI::~CookiesAPI() {
568 }
569
570 void CookiesAPI::Shutdown() {
571   ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
572 }
573
574 static base::LazyInstance<ProfileKeyedAPIFactory<CookiesAPI> >
575 g_factory = LAZY_INSTANCE_INITIALIZER;
576
577 // static
578 ProfileKeyedAPIFactory<CookiesAPI>* CookiesAPI::GetFactoryInstance() {
579   return &g_factory.Get();
580 }
581
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);
586 }
587
588 }  // namespace extensions