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 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->GetExtension()->permissions_data()->HasHostPermission(*url)) { 66 function->SetError(ErrorUtils::FormatErrorMessage( 67 keys::kNoHostPermissionsError, url->spec())); 68 return false; 69 } 70 return true; 71 } 72 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 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 116 CookiesEventRouter::~CookiesEventRouter() { 117 } 118 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 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 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 202 CookiesGetFunction::CookiesGetFunction() { 203 } 204 205 CookiesGetFunction::~CookiesGetFunction() { 206 } 207 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 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 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 270 void CookiesGetFunction::RespondOnUIThread() { 271 DCHECK_CURRENTLY_ON(BrowserThread::UI); 272 SendResponse(true); 273 } 274 275 CookiesGetAllFunction::CookiesGetAllFunction() { 276 } 277 278 CookiesGetAllFunction::~CookiesGetAllFunction() { 279 } 280 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 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 318 void CookiesGetAllFunction::GetAllCookiesCallback( 319 const net::CookieList& cookie_list) { 320 const extensions::Extension* extension = GetExtension(); 321 if (extension) { 322 std::vector<linked_ptr<Cookie> > match_vector; 323 cookies_helpers::AppendMatchingCookiesToVector( 324 cookie_list, url_, &parsed_args_->details, 325 GetExtension(), &match_vector); 326 327 results_ = GetAll::Results::Create(match_vector); 328 } 329 bool rv = BrowserThread::PostTask( 330 BrowserThread::UI, FROM_HERE, 331 base::Bind(&CookiesGetAllFunction::RespondOnUIThread, this)); 332 DCHECK(rv); 333 } 334 335 void CookiesGetAllFunction::RespondOnUIThread() { 336 DCHECK_CURRENTLY_ON(BrowserThread::UI); 337 SendResponse(true); 338 } 339 340 CookiesSetFunction::CookiesSetFunction() : success_(false) { 341 } 342 343 CookiesSetFunction::~CookiesSetFunction() { 344 } 345 346 bool CookiesSetFunction::RunAsync() { 347 parsed_args_ = Set::Params::Create(*args_); 348 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get()); 349 350 // Read/validate input parameters. 351 if (!ParseUrl(this, parsed_args_->details.url, &url_, true)) 352 return false; 353 354 std::string store_id = 355 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id 356 : std::string(); 357 net::URLRequestContextGetter* store_context = NULL; 358 if (!ParseStoreContext(this, &store_id, &store_context)) 359 return false; 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)); 363 364 bool rv = BrowserThread::PostTask( 365 BrowserThread::IO, FROM_HERE, 366 base::Bind(&CookiesSetFunction::SetCookieOnIOThread, this)); 367 DCHECK(rv); 368 369 // Will finish asynchronously. 370 return true; 371 } 372 373 void CookiesSetFunction::SetCookieOnIOThread() { 374 DCHECK_CURRENTLY_ON(BrowserThread::IO); 375 net::CookieMonster* cookie_monster = 376 store_browser_context_->GetURLRequestContext() 377 ->cookie_store() 378 ->GetCookieMonster(); 379 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); 387 } 388 389 cookie_monster->SetCookieWithDetailsAsync( 390 url_, 391 parsed_args_->details.name.get() ? *parsed_args_->details.name 392 : std::string(), 393 parsed_args_->details.value.get() ? *parsed_args_->details.value 394 : std::string(), 395 parsed_args_->details.domain.get() ? *parsed_args_->details.domain 396 : std::string(), 397 parsed_args_->details.path.get() ? *parsed_args_->details.path 398 : std::string(), 399 expiration_time, 400 parsed_args_->details.secure.get() ? *parsed_args_->details.secure.get() 401 : false, 402 parsed_args_->details.http_only.get() ? *parsed_args_->details.http_only 403 : false, 404 net::COOKIE_PRIORITY_DEFAULT, 405 base::Bind(&CookiesSetFunction::PullCookie, this)); 406 } 407 408 void CookiesSetFunction::PullCookie(bool set_cookie_result) { 409 // Pull the newly set cookie. 410 net::CookieMonster* cookie_monster = 411 store_browser_context_->GetURLRequestContext() 412 ->cookie_store() 413 ->GetCookieMonster(); 414 success_ = set_cookie_result; 415 cookies_helpers::GetCookieListFromStore( 416 cookie_monster, url_, 417 base::Bind(&CookiesSetFunction::PullCookieCallback, this)); 418 } 419 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). 427 std::string name = 428 parsed_args_->details.name.get() ? *parsed_args_->details.name 429 : std::string(); 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); 434 break; 435 } 436 } 437 438 bool rv = BrowserThread::PostTask( 439 BrowserThread::UI, FROM_HERE, 440 base::Bind(&CookiesSetFunction::RespondOnUIThread, this)); 441 DCHECK(rv); 442 } 443 444 void CookiesSetFunction::RespondOnUIThread() { 445 DCHECK_CURRENTLY_ON(BrowserThread::UI); 446 if (!success_) { 447 std::string name = 448 parsed_args_->details.name.get() ? *parsed_args_->details.name 449 : std::string(); 450 error_ = ErrorUtils::FormatErrorMessage(keys::kCookieSetFailedError, name); 451 } 452 SendResponse(success_); 453 } 454 455 CookiesRemoveFunction::CookiesRemoveFunction() { 456 } 457 458 CookiesRemoveFunction::~CookiesRemoveFunction() { 459 } 460 461 bool CookiesRemoveFunction::RunAsync() { 462 parsed_args_ = Remove::Params::Create(*args_); 463 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get()); 464 465 // Read/validate input parameters. 466 if (!ParseUrl(this, parsed_args_->details.url, &url_, true)) 467 return false; 468 469 std::string store_id = 470 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id 471 : std::string(); 472 net::URLRequestContextGetter* store_context = NULL; 473 if (!ParseStoreContext(this, &store_id, &store_context)) 474 return false; 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)); 478 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)); 483 DCHECK(rv); 484 485 // Will return asynchronously. 486 return true; 487 } 488 489 void CookiesRemoveFunction::RemoveCookieOnIOThread() { 490 DCHECK_CURRENTLY_ON(BrowserThread::IO); 491 492 // Remove the cookie 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)); 498 } 499 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); 507 508 // Return to UI thread 509 bool rv = BrowserThread::PostTask( 510 BrowserThread::UI, FROM_HERE, 511 base::Bind(&CookiesRemoveFunction::RespondOnUIThread, this)); 512 DCHECK(rv); 513 } 514 515 void CookiesRemoveFunction::RespondOnUIThread() { 516 DCHECK_CURRENTLY_ON(BrowserThread::UI); 517 SendResponse(true); 518 } 519 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()); 530 } 531 DCHECK(original_profile != incognito_profile); 532 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()); 543 } 544 } 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())); 551 } 552 if (incognito_tab_ids.get() && incognito_tab_ids->GetSize() > 0 && 553 incognito_profile) { 554 cookie_stores.push_back(make_linked_ptr( 555 cookies_helpers::CreateCookieStore( 556 incognito_profile, incognito_tab_ids.release()).release())); 557 } 558 results_ = GetAllCookieStores::Results::Create(cookie_stores); 559 return true; 560 } 561 562 CookiesAPI::CookiesAPI(content::BrowserContext* context) 563 : browser_context_(context) { 564 EventRouter::Get(browser_context_) 565 ->RegisterObserver(this, cookies::OnChanged::kEventName); 566 } 567 568 CookiesAPI::~CookiesAPI() { 569 } 570 571 void CookiesAPI::Shutdown() { 572 EventRouter::Get(browser_context_)->UnregisterObserver(this); 573 } 574 575 static base::LazyInstance<BrowserContextKeyedAPIFactory<CookiesAPI> > 576 g_factory = LAZY_INSTANCE_INITIALIZER; 577 578 // static 579 BrowserContextKeyedAPIFactory<CookiesAPI>* CookiesAPI::GetFactoryInstance() { 580 return g_factory.Pointer(); 581 } 582 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); 587 } 588 589 } // namespace extensions 590