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->extension()->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 if (extension()) { 321 std::vector<linked_ptr<Cookie> > match_vector; 322 cookies_helpers::AppendMatchingCookiesToVector( 323 cookie_list, url_, &parsed_args_->details, extension(), &match_vector); 324 325 results_ = GetAll::Results::Create(match_vector); 326 } 327 bool rv = BrowserThread::PostTask( 328 BrowserThread::UI, FROM_HERE, 329 base::Bind(&CookiesGetAllFunction::RespondOnUIThread, this)); 330 DCHECK(rv); 331 } 332 333 void CookiesGetAllFunction::RespondOnUIThread() { 334 DCHECK_CURRENTLY_ON(BrowserThread::UI); 335 SendResponse(true); 336 } 337 338 CookiesSetFunction::CookiesSetFunction() : success_(false) { 339 } 340 341 CookiesSetFunction::~CookiesSetFunction() { 342 } 343 344 bool CookiesSetFunction::RunAsync() { 345 parsed_args_ = Set::Params::Create(*args_); 346 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get()); 347 348 // Read/validate input parameters. 349 if (!ParseUrl(this, parsed_args_->details.url, &url_, true)) 350 return false; 351 352 std::string store_id = 353 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id 354 : std::string(); 355 net::URLRequestContextGetter* store_context = NULL; 356 if (!ParseStoreContext(this, &store_id, &store_context)) 357 return false; 358 store_browser_context_ = store_context; 359 if (!parsed_args_->details.store_id.get()) 360 parsed_args_->details.store_id.reset(new std::string(store_id)); 361 362 bool rv = BrowserThread::PostTask( 363 BrowserThread::IO, FROM_HERE, 364 base::Bind(&CookiesSetFunction::SetCookieOnIOThread, this)); 365 DCHECK(rv); 366 367 // Will finish asynchronously. 368 return true; 369 } 370 371 void CookiesSetFunction::SetCookieOnIOThread() { 372 DCHECK_CURRENTLY_ON(BrowserThread::IO); 373 net::CookieMonster* cookie_monster = 374 store_browser_context_->GetURLRequestContext() 375 ->cookie_store() 376 ->GetCookieMonster(); 377 378 base::Time expiration_time; 379 if (parsed_args_->details.expiration_date.get()) { 380 // Time::FromDoubleT converts double time 0 to empty Time object. So we need 381 // to do special handling here. 382 expiration_time = (*parsed_args_->details.expiration_date == 0) ? 383 base::Time::UnixEpoch() : 384 base::Time::FromDoubleT(*parsed_args_->details.expiration_date); 385 } 386 387 cookie_monster->SetCookieWithDetailsAsync( 388 url_, 389 parsed_args_->details.name.get() ? *parsed_args_->details.name 390 : std::string(), 391 parsed_args_->details.value.get() ? *parsed_args_->details.value 392 : std::string(), 393 parsed_args_->details.domain.get() ? *parsed_args_->details.domain 394 : std::string(), 395 parsed_args_->details.path.get() ? *parsed_args_->details.path 396 : std::string(), 397 expiration_time, 398 parsed_args_->details.secure.get() ? *parsed_args_->details.secure.get() 399 : false, 400 parsed_args_->details.http_only.get() ? *parsed_args_->details.http_only 401 : false, 402 net::COOKIE_PRIORITY_DEFAULT, 403 base::Bind(&CookiesSetFunction::PullCookie, this)); 404 } 405 406 void CookiesSetFunction::PullCookie(bool set_cookie_result) { 407 // Pull the newly set cookie. 408 net::CookieMonster* cookie_monster = 409 store_browser_context_->GetURLRequestContext() 410 ->cookie_store() 411 ->GetCookieMonster(); 412 success_ = set_cookie_result; 413 cookies_helpers::GetCookieListFromStore( 414 cookie_monster, url_, 415 base::Bind(&CookiesSetFunction::PullCookieCallback, this)); 416 } 417 418 void CookiesSetFunction::PullCookieCallback( 419 const net::CookieList& cookie_list) { 420 net::CookieList::const_iterator it; 421 for (it = cookie_list.begin(); it != cookie_list.end(); ++it) { 422 // Return the first matching cookie. Relies on the fact that the 423 // CookieMonster returns them in canonical order (longest path, then 424 // earliest creation time). 425 std::string name = 426 parsed_args_->details.name.get() ? *parsed_args_->details.name 427 : std::string(); 428 if (it->Name() == name) { 429 scoped_ptr<Cookie> cookie( 430 cookies_helpers::CreateCookie(*it, *parsed_args_->details.store_id)); 431 results_ = Set::Results::Create(*cookie); 432 break; 433 } 434 } 435 436 bool rv = BrowserThread::PostTask( 437 BrowserThread::UI, FROM_HERE, 438 base::Bind(&CookiesSetFunction::RespondOnUIThread, this)); 439 DCHECK(rv); 440 } 441 442 void CookiesSetFunction::RespondOnUIThread() { 443 DCHECK_CURRENTLY_ON(BrowserThread::UI); 444 if (!success_) { 445 std::string name = 446 parsed_args_->details.name.get() ? *parsed_args_->details.name 447 : std::string(); 448 error_ = ErrorUtils::FormatErrorMessage(keys::kCookieSetFailedError, name); 449 } 450 SendResponse(success_); 451 } 452 453 CookiesRemoveFunction::CookiesRemoveFunction() { 454 } 455 456 CookiesRemoveFunction::~CookiesRemoveFunction() { 457 } 458 459 bool CookiesRemoveFunction::RunAsync() { 460 parsed_args_ = Remove::Params::Create(*args_); 461 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get()); 462 463 // Read/validate input parameters. 464 if (!ParseUrl(this, parsed_args_->details.url, &url_, true)) 465 return false; 466 467 std::string store_id = 468 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id 469 : std::string(); 470 net::URLRequestContextGetter* store_context = NULL; 471 if (!ParseStoreContext(this, &store_id, &store_context)) 472 return false; 473 store_browser_context_ = store_context; 474 if (!parsed_args_->details.store_id.get()) 475 parsed_args_->details.store_id.reset(new std::string(store_id)); 476 477 // Pass the work off to the IO thread. 478 bool rv = BrowserThread::PostTask( 479 BrowserThread::IO, FROM_HERE, 480 base::Bind(&CookiesRemoveFunction::RemoveCookieOnIOThread, this)); 481 DCHECK(rv); 482 483 // Will return asynchronously. 484 return true; 485 } 486 487 void CookiesRemoveFunction::RemoveCookieOnIOThread() { 488 DCHECK_CURRENTLY_ON(BrowserThread::IO); 489 490 // Remove the cookie 491 net::CookieStore* cookie_store = 492 store_browser_context_->GetURLRequestContext()->cookie_store(); 493 cookie_store->DeleteCookieAsync( 494 url_, parsed_args_->details.name, 495 base::Bind(&CookiesRemoveFunction::RemoveCookieCallback, this)); 496 } 497 498 void CookiesRemoveFunction::RemoveCookieCallback() { 499 // Build the callback result 500 Remove::Results::Details details; 501 details.name = parsed_args_->details.name; 502 details.url = url_.spec(); 503 details.store_id = *parsed_args_->details.store_id; 504 results_ = Remove::Results::Create(details); 505 506 // Return to UI thread 507 bool rv = BrowserThread::PostTask( 508 BrowserThread::UI, FROM_HERE, 509 base::Bind(&CookiesRemoveFunction::RespondOnUIThread, this)); 510 DCHECK(rv); 511 } 512 513 void CookiesRemoveFunction::RespondOnUIThread() { 514 DCHECK_CURRENTLY_ON(BrowserThread::UI); 515 SendResponse(true); 516 } 517 518 bool CookiesGetAllCookieStoresFunction::RunSync() { 519 Profile* original_profile = GetProfile(); 520 DCHECK(original_profile); 521 scoped_ptr<base::ListValue> original_tab_ids(new base::ListValue()); 522 Profile* incognito_profile = NULL; 523 scoped_ptr<base::ListValue> incognito_tab_ids; 524 if (include_incognito() && GetProfile()->HasOffTheRecordProfile()) { 525 incognito_profile = GetProfile()->GetOffTheRecordProfile(); 526 if (incognito_profile) 527 incognito_tab_ids.reset(new base::ListValue()); 528 } 529 DCHECK(original_profile != incognito_profile); 530 531 // Iterate through all browser instances, and for each browser, 532 // add its tab IDs to either the regular or incognito tab ID list depending 533 // whether the browser is regular or incognito. 534 for (chrome::BrowserIterator it; !it.done(); it.Next()) { 535 Browser* browser = *it; 536 if (browser->profile() == original_profile) { 537 cookies_helpers::AppendToTabIdList(browser, original_tab_ids.get()); 538 } else if (incognito_tab_ids.get() && 539 browser->profile() == incognito_profile) { 540 cookies_helpers::AppendToTabIdList(browser, incognito_tab_ids.get()); 541 } 542 } 543 // Return a list of all cookie stores with at least one open tab. 544 std::vector<linked_ptr<CookieStore> > cookie_stores; 545 if (original_tab_ids->GetSize() > 0) { 546 cookie_stores.push_back(make_linked_ptr( 547 cookies_helpers::CreateCookieStore( 548 original_profile, original_tab_ids.release()).release())); 549 } 550 if (incognito_tab_ids.get() && incognito_tab_ids->GetSize() > 0 && 551 incognito_profile) { 552 cookie_stores.push_back(make_linked_ptr( 553 cookies_helpers::CreateCookieStore( 554 incognito_profile, incognito_tab_ids.release()).release())); 555 } 556 results_ = GetAllCookieStores::Results::Create(cookie_stores); 557 return true; 558 } 559 560 CookiesAPI::CookiesAPI(content::BrowserContext* context) 561 : browser_context_(context) { 562 EventRouter::Get(browser_context_) 563 ->RegisterObserver(this, cookies::OnChanged::kEventName); 564 } 565 566 CookiesAPI::~CookiesAPI() { 567 } 568 569 void CookiesAPI::Shutdown() { 570 EventRouter::Get(browser_context_)->UnregisterObserver(this); 571 } 572 573 static base::LazyInstance<BrowserContextKeyedAPIFactory<CookiesAPI> > 574 g_factory = LAZY_INSTANCE_INITIALIZER; 575 576 // static 577 BrowserContextKeyedAPIFactory<CookiesAPI>* CookiesAPI::GetFactoryInstance() { 578 return g_factory.Pointer(); 579 } 580 581 void CookiesAPI::OnListenerAdded( 582 const extensions::EventListenerInfo& details) { 583 cookies_event_router_.reset(new CookiesEventRouter(browser_context_)); 584 EventRouter::Get(browser_context_)->UnregisterObserver(this); 585 } 586 587 } // namespace extensions 588