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 keys = cookies_api_constants; 50 51 CookiesEventRouter::CookiesEventRouter(Profile* profile) 52 : profile_(profile) { 53 CHECK(registrar_.IsEmpty()); 54 registrar_.Add(this, 55 chrome::NOTIFICATION_COOKIE_CHANGED, 56 content::NotificationService::AllBrowserContextsAndSources()); 57 } 58 59 CookiesEventRouter::~CookiesEventRouter() { 60 } 61 62 void CookiesEventRouter::Observe( 63 int type, 64 const content::NotificationSource& source, 65 const content::NotificationDetails& details) { 66 Profile* profile = content::Source<Profile>(source).ptr(); 67 if (!profile_->IsSameProfile(profile)) 68 return; 69 70 switch (type) { 71 case chrome::NOTIFICATION_COOKIE_CHANGED: 72 CookieChanged( 73 profile, 74 content::Details<ChromeCookieDetails>(details).ptr()); 75 break; 76 77 default: 78 NOTREACHED(); 79 } 80 } 81 82 void CookiesEventRouter::CookieChanged( 83 Profile* profile, 84 ChromeCookieDetails* details) { 85 scoped_ptr<base::ListValue> args(new base::ListValue()); 86 base::DictionaryValue* dict = new base::DictionaryValue(); 87 dict->SetBoolean(keys::kRemovedKey, details->removed); 88 89 scoped_ptr<Cookie> cookie( 90 cookies_helpers::CreateCookie(*details->cookie, 91 cookies_helpers::GetStoreIdFromProfile(profile_))); 92 dict->Set(keys::kCookieKey, cookie->ToValue().release()); 93 94 // Map the internal cause to an external string. 95 std::string cause; 96 switch (details->cause) { 97 case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT: 98 cause = keys::kExplicitChangeCause; 99 break; 100 101 case net::CookieMonster::Delegate::CHANGE_COOKIE_OVERWRITE: 102 cause = keys::kOverwriteChangeCause; 103 break; 104 105 case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED: 106 cause = keys::kExpiredChangeCause; 107 break; 108 109 case net::CookieMonster::Delegate::CHANGE_COOKIE_EVICTED: 110 cause = keys::kEvictedChangeCause; 111 break; 112 113 case net::CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED_OVERWRITE: 114 cause = keys::kExpiredOverwriteChangeCause; 115 break; 116 117 default: 118 NOTREACHED(); 119 } 120 dict->SetString(keys::kCauseKey, cause); 121 122 args->Append(dict); 123 124 GURL cookie_domain = 125 cookies_helpers::GetURLFromCanonicalCookie(*details->cookie); 126 DispatchEvent(profile, keys::kOnChanged, args.Pass(), cookie_domain); 127 } 128 129 void CookiesEventRouter::DispatchEvent( 130 Profile* profile, 131 const std::string& event_name, 132 scoped_ptr<base::ListValue> event_args, 133 GURL& cookie_domain) { 134 EventRouter* router = profile ? 135 extensions::ExtensionSystem::Get(profile)->event_router() : NULL; 136 if (!router) 137 return; 138 scoped_ptr<Event> event(new Event(event_name, event_args.Pass())); 139 event->restrict_to_profile = profile; 140 event->event_url = cookie_domain; 141 router->BroadcastEvent(event.Pass()); 142 } 143 144 bool CookiesFunction::ParseUrl(const std::string& url_string, GURL* url, 145 bool check_host_permissions) { 146 *url = GURL(url_string); 147 if (!url->is_valid()) { 148 error_ = ErrorUtils::FormatErrorMessage( 149 keys::kInvalidUrlError, url_string); 150 return false; 151 } 152 // Check against host permissions if needed. 153 if (check_host_permissions && 154 !PermissionsData::HasHostPermission(GetExtension(), *url)) { 155 error_ = ErrorUtils::FormatErrorMessage( 156 keys::kNoHostPermissionsError, url->spec()); 157 return false; 158 } 159 return true; 160 } 161 162 bool CookiesFunction::ParseStoreContext( 163 std::string* store_id, 164 net::URLRequestContextGetter** context) { 165 DCHECK((context || store_id->empty())); 166 Profile* store_profile = NULL; 167 if (!store_id->empty()) { 168 store_profile = cookies_helpers::ChooseProfileFromStoreId( 169 *store_id, profile(), include_incognito()); 170 if (!store_profile) { 171 error_ = ErrorUtils::FormatErrorMessage( 172 keys::kInvalidStoreIdError, *store_id); 173 return false; 174 } 175 } else { 176 // The store ID was not specified; use the current execution context's 177 // cookie store by default. 178 // GetCurrentBrowser() already takes into account incognito settings. 179 Browser* current_browser = GetCurrentBrowser(); 180 if (!current_browser) { 181 error_ = keys::kNoCookieStoreFoundError; 182 return false; 183 } 184 store_profile = current_browser->profile(); 185 *store_id = cookies_helpers::GetStoreIdFromProfile(store_profile); 186 } 187 188 if (context) 189 *context = store_profile->GetRequestContext(); 190 DCHECK(context); 191 192 return true; 193 } 194 195 CookiesGetFunction::CookiesGetFunction() { 196 } 197 198 CookiesGetFunction::~CookiesGetFunction() { 199 } 200 201 bool CookiesGetFunction::RunImpl() { 202 parsed_args_ = Get::Params::Create(*args_); 203 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get()); 204 205 // Read/validate input parameters. 206 if (!ParseUrl(parsed_args_->details.url, &url_, true)) 207 return false; 208 209 std::string store_id = 210 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id 211 : std::string(); 212 net::URLRequestContextGetter* store_context = NULL; 213 if (!ParseStoreContext(&store_id, &store_context)) 214 return false; 215 store_context_ = store_context; 216 if (!parsed_args_->details.store_id.get()) 217 parsed_args_->details.store_id.reset(new std::string(store_id)); 218 219 store_context_ = store_context; 220 221 bool rv = BrowserThread::PostTask( 222 BrowserThread::IO, FROM_HERE, 223 base::Bind(&CookiesGetFunction::GetCookieOnIOThread, this)); 224 DCHECK(rv); 225 226 // Will finish asynchronously. 227 return true; 228 } 229 230 void CookiesGetFunction::GetCookieOnIOThread() { 231 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 232 net::CookieStore* cookie_store = 233 store_context_->GetURLRequestContext()->cookie_store(); 234 cookies_helpers::GetCookieListFromStore( 235 cookie_store, url_, 236 base::Bind(&CookiesGetFunction::GetCookieCallback, this)); 237 } 238 239 void CookiesGetFunction::GetCookieCallback(const net::CookieList& cookie_list) { 240 net::CookieList::const_iterator it; 241 for (it = cookie_list.begin(); it != cookie_list.end(); ++it) { 242 // Return the first matching cookie. Relies on the fact that the 243 // CookieMonster returns them in canonical order (longest path, then 244 // earliest creation time). 245 if (it->Name() == parsed_args_->details.name) { 246 scoped_ptr<Cookie> cookie( 247 cookies_helpers::CreateCookie(*it, *parsed_args_->details.store_id)); 248 results_ = Get::Results::Create(*cookie); 249 break; 250 } 251 } 252 253 // The cookie doesn't exist; return null. 254 if (it == cookie_list.end()) 255 SetResult(Value::CreateNullValue()); 256 257 bool rv = BrowserThread::PostTask( 258 BrowserThread::UI, FROM_HERE, 259 base::Bind(&CookiesGetFunction::RespondOnUIThread, this)); 260 DCHECK(rv); 261 } 262 263 void CookiesGetFunction::RespondOnUIThread() { 264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 265 SendResponse(true); 266 } 267 268 CookiesGetAllFunction::CookiesGetAllFunction() { 269 } 270 271 CookiesGetAllFunction::~CookiesGetAllFunction() { 272 } 273 274 bool CookiesGetAllFunction::RunImpl() { 275 parsed_args_ = GetAll::Params::Create(*args_); 276 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get()); 277 278 if (parsed_args_->details.url.get() && 279 !ParseUrl(*parsed_args_->details.url, &url_, false)) { 280 return false; 281 } 282 283 std::string store_id = 284 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id 285 : std::string(); 286 net::URLRequestContextGetter* store_context = NULL; 287 if (!ParseStoreContext(&store_id, &store_context)) 288 return false; 289 store_context_ = store_context; 290 if (!parsed_args_->details.store_id.get()) 291 parsed_args_->details.store_id.reset(new std::string(store_id)); 292 293 bool rv = BrowserThread::PostTask( 294 BrowserThread::IO, FROM_HERE, 295 base::Bind(&CookiesGetAllFunction::GetAllCookiesOnIOThread, this)); 296 DCHECK(rv); 297 298 // Will finish asynchronously. 299 return true; 300 } 301 302 void CookiesGetAllFunction::GetAllCookiesOnIOThread() { 303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 304 net::CookieStore* cookie_store = 305 store_context_->GetURLRequestContext()->cookie_store(); 306 cookies_helpers::GetCookieListFromStore( 307 cookie_store, url_, 308 base::Bind(&CookiesGetAllFunction::GetAllCookiesCallback, this)); 309 } 310 311 void CookiesGetAllFunction::GetAllCookiesCallback( 312 const net::CookieList& cookie_list) { 313 const extensions::Extension* extension = GetExtension(); 314 if (extension) { 315 std::vector<linked_ptr<Cookie> > match_vector; 316 cookies_helpers::AppendMatchingCookiesToVector( 317 cookie_list, url_, &parsed_args_->details, 318 GetExtension(), &match_vector); 319 320 results_ = GetAll::Results::Create(match_vector); 321 } 322 bool rv = BrowserThread::PostTask( 323 BrowserThread::UI, FROM_HERE, 324 base::Bind(&CookiesGetAllFunction::RespondOnUIThread, this)); 325 DCHECK(rv); 326 } 327 328 void CookiesGetAllFunction::RespondOnUIThread() { 329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 330 SendResponse(true); 331 } 332 333 CookiesSetFunction::CookiesSetFunction() : success_(false) { 334 } 335 336 CookiesSetFunction::~CookiesSetFunction() { 337 } 338 339 bool CookiesSetFunction::RunImpl() { 340 parsed_args_ = Set::Params::Create(*args_); 341 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get()); 342 343 // Read/validate input parameters. 344 if (!ParseUrl(parsed_args_->details.url, &url_, true)) 345 return false; 346 347 std::string store_id = 348 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id 349 : std::string(); 350 net::URLRequestContextGetter* store_context = NULL; 351 if (!ParseStoreContext(&store_id, &store_context)) 352 return false; 353 store_context_ = store_context; 354 if (!parsed_args_->details.store_id.get()) 355 parsed_args_->details.store_id.reset(new std::string(store_id)); 356 357 bool rv = BrowserThread::PostTask( 358 BrowserThread::IO, FROM_HERE, 359 base::Bind(&CookiesSetFunction::SetCookieOnIOThread, this)); 360 DCHECK(rv); 361 362 // Will finish asynchronously. 363 return true; 364 } 365 366 void CookiesSetFunction::SetCookieOnIOThread() { 367 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 368 net::CookieMonster* cookie_monster = 369 store_context_->GetURLRequestContext()->cookie_store()-> 370 GetCookieMonster(); 371 372 base::Time expiration_time; 373 if (parsed_args_->details.expiration_date.get()) { 374 // Time::FromDoubleT converts double time 0 to empty Time object. So we need 375 // to do special handling here. 376 expiration_time = (*parsed_args_->details.expiration_date == 0) ? 377 base::Time::UnixEpoch() : 378 base::Time::FromDoubleT(*parsed_args_->details.expiration_date); 379 } 380 381 cookie_monster->SetCookieWithDetailsAsync( 382 url_, 383 parsed_args_->details.name.get() ? *parsed_args_->details.name 384 : std::string(), 385 parsed_args_->details.value.get() ? *parsed_args_->details.value 386 : std::string(), 387 parsed_args_->details.domain.get() ? *parsed_args_->details.domain 388 : std::string(), 389 parsed_args_->details.path.get() ? *parsed_args_->details.path 390 : std::string(), 391 expiration_time, 392 parsed_args_->details.secure.get() ? *parsed_args_->details.secure.get() 393 : false, 394 parsed_args_->details.http_only.get() ? *parsed_args_->details.http_only 395 : false, 396 net::COOKIE_PRIORITY_DEFAULT, 397 base::Bind(&CookiesSetFunction::PullCookie, this)); 398 } 399 400 void CookiesSetFunction::PullCookie(bool set_cookie_result) { 401 // Pull the newly set cookie. 402 net::CookieMonster* cookie_monster = 403 store_context_->GetURLRequestContext()->cookie_store()-> 404 GetCookieMonster(); 405 success_ = set_cookie_result; 406 cookies_helpers::GetCookieListFromStore( 407 cookie_monster, url_, 408 base::Bind(&CookiesSetFunction::PullCookieCallback, this)); 409 } 410 411 void CookiesSetFunction::PullCookieCallback( 412 const net::CookieList& cookie_list) { 413 net::CookieList::const_iterator it; 414 for (it = cookie_list.begin(); it != cookie_list.end(); ++it) { 415 // Return the first matching cookie. Relies on the fact that the 416 // CookieMonster returns them in canonical order (longest path, then 417 // earliest creation time). 418 std::string name = 419 parsed_args_->details.name.get() ? *parsed_args_->details.name 420 : std::string(); 421 if (it->Name() == name) { 422 scoped_ptr<Cookie> cookie( 423 cookies_helpers::CreateCookie(*it, *parsed_args_->details.store_id)); 424 results_ = Set::Results::Create(*cookie); 425 break; 426 } 427 } 428 429 bool rv = BrowserThread::PostTask( 430 BrowserThread::UI, FROM_HERE, 431 base::Bind(&CookiesSetFunction::RespondOnUIThread, this)); 432 DCHECK(rv); 433 } 434 435 void CookiesSetFunction::RespondOnUIThread() { 436 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 437 if (!success_) { 438 std::string name = 439 parsed_args_->details.name.get() ? *parsed_args_->details.name 440 : std::string(); 441 error_ = ErrorUtils::FormatErrorMessage(keys::kCookieSetFailedError, name); 442 } 443 SendResponse(success_); 444 } 445 446 CookiesRemoveFunction::CookiesRemoveFunction() { 447 } 448 449 CookiesRemoveFunction::~CookiesRemoveFunction() { 450 } 451 452 bool CookiesRemoveFunction::RunImpl() { 453 parsed_args_ = Remove::Params::Create(*args_); 454 EXTENSION_FUNCTION_VALIDATE(parsed_args_.get()); 455 456 // Read/validate input parameters. 457 if (!ParseUrl(parsed_args_->details.url, &url_, true)) 458 return false; 459 460 std::string store_id = 461 parsed_args_->details.store_id.get() ? *parsed_args_->details.store_id 462 : std::string(); 463 net::URLRequestContextGetter* store_context = NULL; 464 if (!ParseStoreContext(&store_id, &store_context)) 465 return false; 466 store_context_ = store_context; 467 if (!parsed_args_->details.store_id.get()) 468 parsed_args_->details.store_id.reset(new std::string(store_id)); 469 470 // Pass the work off to the IO thread. 471 bool rv = BrowserThread::PostTask( 472 BrowserThread::IO, FROM_HERE, 473 base::Bind(&CookiesRemoveFunction::RemoveCookieOnIOThread, this)); 474 DCHECK(rv); 475 476 // Will return asynchronously. 477 return true; 478 } 479 480 void CookiesRemoveFunction::RemoveCookieOnIOThread() { 481 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 482 483 // Remove the cookie 484 net::CookieStore* cookie_store = 485 store_context_->GetURLRequestContext()->cookie_store(); 486 cookie_store->DeleteCookieAsync( 487 url_, parsed_args_->details.name, 488 base::Bind(&CookiesRemoveFunction::RemoveCookieCallback, this)); 489 } 490 491 void CookiesRemoveFunction::RemoveCookieCallback() { 492 // Build the callback result 493 Remove::Results::Details details; 494 details.name = parsed_args_->details.name; 495 details.url = url_.spec(); 496 details.store_id = *parsed_args_->details.store_id; 497 results_ = Remove::Results::Create(details); 498 499 // Return to UI thread 500 bool rv = BrowserThread::PostTask( 501 BrowserThread::UI, FROM_HERE, 502 base::Bind(&CookiesRemoveFunction::RespondOnUIThread, this)); 503 DCHECK(rv); 504 } 505 506 void CookiesRemoveFunction::RespondOnUIThread() { 507 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 508 SendResponse(true); 509 } 510 511 bool CookiesGetAllCookieStoresFunction::RunImpl() { 512 Profile* original_profile = profile(); 513 DCHECK(original_profile); 514 scoped_ptr<base::ListValue> original_tab_ids(new base::ListValue()); 515 Profile* incognito_profile = NULL; 516 scoped_ptr<base::ListValue> incognito_tab_ids; 517 if (include_incognito() && profile()->HasOffTheRecordProfile()) { 518 incognito_profile = profile()->GetOffTheRecordProfile(); 519 if (incognito_profile) 520 incognito_tab_ids.reset(new base::ListValue()); 521 } 522 DCHECK(original_profile != incognito_profile); 523 524 // Iterate through all browser instances, and for each browser, 525 // add its tab IDs to either the regular or incognito tab ID list depending 526 // whether the browser is regular or incognito. 527 for (chrome::BrowserIterator it; !it.done(); it.Next()) { 528 Browser* browser = *it; 529 if (browser->profile() == original_profile) { 530 cookies_helpers::AppendToTabIdList(browser, original_tab_ids.get()); 531 } else if (incognito_tab_ids.get() && 532 browser->profile() == incognito_profile) { 533 cookies_helpers::AppendToTabIdList(browser, incognito_tab_ids.get()); 534 } 535 } 536 // Return a list of all cookie stores with at least one open tab. 537 std::vector<linked_ptr<CookieStore> > cookie_stores; 538 if (original_tab_ids->GetSize() > 0) { 539 cookie_stores.push_back(make_linked_ptr( 540 cookies_helpers::CreateCookieStore( 541 original_profile, original_tab_ids.release()).release())); 542 } 543 if (incognito_tab_ids.get() && incognito_tab_ids->GetSize() > 0 && 544 incognito_profile) { 545 cookie_stores.push_back(make_linked_ptr( 546 cookies_helpers::CreateCookieStore( 547 incognito_profile, incognito_tab_ids.release()).release())); 548 } 549 results_ = GetAllCookieStores::Results::Create(cookie_stores); 550 return true; 551 } 552 553 void CookiesGetAllCookieStoresFunction::Run() { 554 SendResponse(RunImpl()); 555 } 556 557 CookiesAPI::CookiesAPI(Profile* profile) 558 : profile_(profile) { 559 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( 560 this, keys::kOnChanged); 561 } 562 563 CookiesAPI::~CookiesAPI() { 564 } 565 566 void CookiesAPI::Shutdown() { 567 ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this); 568 } 569 570 static base::LazyInstance<ProfileKeyedAPIFactory<CookiesAPI> > 571 g_factory = LAZY_INSTANCE_INITIALIZER; 572 573 // static 574 ProfileKeyedAPIFactory<CookiesAPI>* CookiesAPI::GetFactoryInstance() { 575 return &g_factory.Get(); 576 } 577 578 void CookiesAPI::OnListenerAdded( 579 const extensions::EventListenerInfo& details) { 580 cookies_event_router_.reset(new CookiesEventRouter(profile_)); 581 ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this); 582 } 583 584 } // namespace extensions 585