Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/net/sqlite_persistent_cookie_store.h" | 5 #include "chrome/browser/net/sqlite_persistent_cookie_store.h" |
| 6 | 6 |
| 7 #include <list> | 7 #include <list> |
| 8 #include <map> | 8 #include <map> |
| 9 #include <set> | 9 #include <set> |
| 10 #include <utility> | 10 #include <utility> |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 58 class SQLitePersistentCookieStore::Backend | 58 class SQLitePersistentCookieStore::Backend |
| 59 : public base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend> { | 59 : public base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend> { |
| 60 public: | 60 public: |
| 61 Backend(const FilePath& path, bool restore_old_session_cookies) | 61 Backend(const FilePath& path, bool restore_old_session_cookies) |
| 62 : path_(path), | 62 : path_(path), |
| 63 db_(NULL), | 63 db_(NULL), |
| 64 num_pending_(0), | 64 num_pending_(0), |
| 65 clear_local_state_on_exit_(false), | 65 clear_local_state_on_exit_(false), |
| 66 initialized_(false), | 66 initialized_(false), |
| 67 restore_old_session_cookies_(restore_old_session_cookies), | 67 restore_old_session_cookies_(restore_old_session_cookies), |
| 68 num_cookies_read_(0), | |
| 68 num_priority_waiting_(0), | 69 num_priority_waiting_(0), |
| 69 total_priority_requests_(0) { | 70 total_priority_requests_(0) { |
| 70 } | 71 } |
| 71 | 72 |
| 72 // Creates or loads the SQLite database. | 73 // Creates or loads the SQLite database. |
| 73 void Load(const LoadedCallback& loaded_callback); | 74 void Load(const LoadedCallback& loaded_callback); |
| 74 | 75 |
| 75 // Loads cookies for the domain key (eTLD+1). | 76 // Loads cookies for the domain key (eTLD+1). |
| 76 void LoadCookiesForKey(const std::string& domain, | 77 void LoadCookiesForKey(const std::string& domain, |
| 77 const LoadedCallback& loaded_callback); | 78 const LoadedCallback& loaded_callback); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 121 OperationType op() const { return op_; } | 122 OperationType op() const { return op_; } |
| 122 const net::CookieMonster::CanonicalCookie& cc() const { return cc_; } | 123 const net::CookieMonster::CanonicalCookie& cc() const { return cc_; } |
| 123 | 124 |
| 124 private: | 125 private: |
| 125 OperationType op_; | 126 OperationType op_; |
| 126 net::CookieMonster::CanonicalCookie cc_; | 127 net::CookieMonster::CanonicalCookie cc_; |
| 127 }; | 128 }; |
| 128 | 129 |
| 129 private: | 130 private: |
| 130 // Creates or loads the SQLite database on DB thread. | 131 // Creates or loads the SQLite database on DB thread. |
| 131 void LoadAndNotifyOnDBThread(const LoadedCallback& loaded_callback); | 132 void LoadAndNotifyOnDBThread(const LoadedCallback& loaded_callback, |
| 133 const base::Time& posted_at); | |
| 132 | 134 |
| 133 // Loads cookies for the domain key (eTLD+1) on DB thread. | 135 // Loads cookies for the domain key (eTLD+1) on DB thread. |
| 134 void LoadKeyAndNotifyOnDBThread(const std::string& domains, | 136 void LoadKeyAndNotifyOnDBThread(const std::string& domains, |
| 135 const LoadedCallback& loaded_callback); | 137 const LoadedCallback& loaded_callback, |
| 138 const base::Time& posted_at); | |
| 136 | 139 |
| 137 // Notifies the CookieMonster when loading completes for a specific domain key | 140 // Notifies the CookieMonster when loading completes for a specific domain key |
| 138 // or for all domain keys. Triggers the callback and passes it all cookies | 141 // or for all domain keys. Triggers the callback and passes it all cookies |
| 139 // that have been loaded from DB since last IO notification. | 142 // that have been loaded from DB since last IO notification. |
| 140 void Notify(const LoadedCallback& loaded_callback, bool load_success); | 143 void Notify(const LoadedCallback& loaded_callback, bool load_success); |
| 141 | 144 |
| 142 // Sends notification when the entire store is loaded, and reports metrics | 145 // Sends notification when the entire store is loaded, and reports metrics |
| 143 // for the total time to load and aggregated results from any priority loads | 146 // for the total time to load and aggregated results from any priority loads |
| 144 // that occurred. | 147 // that occurred. |
| 145 void CompleteLoadOnIOThread(const LoadedCallback& loaded_callback, | 148 void CompleteLoadOnIOThread(const LoadedCallback& loaded_callback, |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 202 // Indicates if DB has been initialized. | 205 // Indicates if DB has been initialized. |
| 203 bool initialized_; | 206 bool initialized_; |
| 204 | 207 |
| 205 // If false, we should filter out session cookies when reading the DB. | 208 // If false, we should filter out session cookies when reading the DB. |
| 206 bool restore_old_session_cookies_; | 209 bool restore_old_session_cookies_; |
| 207 | 210 |
| 208 // The cumulative time spent loading the cookies on the DB thread. Incremented | 211 // The cumulative time spent loading the cookies on the DB thread. Incremented |
| 209 // and reported from the DB thread. | 212 // and reported from the DB thread. |
| 210 base::TimeDelta cookie_load_duration_; | 213 base::TimeDelta cookie_load_duration_; |
| 211 | 214 |
| 215 // The total number of cookies read. Incremented and reported on the DB | |
| 216 // thread. | |
| 217 int num_cookies_read_; | |
| 218 | |
| 212 // Guards the following metrics-related properties (only accessed when | 219 // Guards the following metrics-related properties (only accessed when |
| 213 // starting/completing priority loads or completing the total load). | 220 // starting/completing priority loads or completing the total load). |
| 214 base::Lock metrics_lock_; | 221 base::Lock metrics_lock_; |
| 215 int num_priority_waiting_; | 222 int num_priority_waiting_; |
| 216 // The total number of priority requests. | 223 // The total number of priority requests. |
| 217 int total_priority_requests_; | 224 int total_priority_requests_; |
| 218 // The time when |num_priority_waiting_| incremented to 1. | 225 // The time when |num_priority_waiting_| incremented to 1. |
| 219 base::Time current_priority_wait_start_; | 226 base::Time current_priority_wait_start_; |
| 220 // The cumulative duration of time when |num_priority_waiting_| was greater | 227 // The cumulative duration of time when |num_priority_waiting_| was greater |
| 221 // than 1. | 228 // than 1. |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 296 } | 303 } |
| 297 | 304 |
| 298 } // namespace | 305 } // namespace |
| 299 | 306 |
| 300 void SQLitePersistentCookieStore::Backend::Load( | 307 void SQLitePersistentCookieStore::Backend::Load( |
| 301 const LoadedCallback& loaded_callback) { | 308 const LoadedCallback& loaded_callback) { |
| 302 // This function should be called only once per instance. | 309 // This function should be called only once per instance. |
| 303 DCHECK(!db_.get()); | 310 DCHECK(!db_.get()); |
| 304 BrowserThread::PostTask( | 311 BrowserThread::PostTask( |
| 305 BrowserThread::DB, FROM_HERE, | 312 BrowserThread::DB, FROM_HERE, |
| 306 base::Bind(&Backend::LoadAndNotifyOnDBThread, this, loaded_callback)); | 313 base::Bind(&Backend::LoadAndNotifyOnDBThread, this, loaded_callback, |
| 314 base::Time::Now())); | |
| 307 } | 315 } |
| 308 | 316 |
| 309 void SQLitePersistentCookieStore::Backend::LoadCookiesForKey( | 317 void SQLitePersistentCookieStore::Backend::LoadCookiesForKey( |
| 310 const std::string& key, | 318 const std::string& key, |
| 311 const LoadedCallback& loaded_callback) { | 319 const LoadedCallback& loaded_callback) { |
| 312 { | 320 { |
| 313 base::AutoLock locked(metrics_lock_); | 321 base::AutoLock locked(metrics_lock_); |
| 314 if (num_priority_waiting_ == 0) | 322 if (num_priority_waiting_ == 0) |
| 315 current_priority_wait_start_ = base::Time::Now(); | 323 current_priority_wait_start_ = base::Time::Now(); |
| 316 num_priority_waiting_++; | 324 num_priority_waiting_++; |
| 317 total_priority_requests_++; | 325 total_priority_requests_++; |
| 318 } | 326 } |
| 319 | 327 |
| 320 BrowserThread::PostTask( | 328 BrowserThread::PostTask( |
| 321 BrowserThread::DB, FROM_HERE, | 329 BrowserThread::DB, FROM_HERE, |
| 322 base::Bind(&Backend::LoadKeyAndNotifyOnDBThread, this, | 330 base::Bind(&Backend::LoadKeyAndNotifyOnDBThread, this, |
| 323 key, | 331 key, |
| 324 loaded_callback)); | 332 loaded_callback, |
| 333 base::Time::Now())); | |
| 325 } | 334 } |
| 326 | 335 |
| 327 void SQLitePersistentCookieStore::Backend::LoadAndNotifyOnDBThread( | 336 void SQLitePersistentCookieStore::Backend::LoadAndNotifyOnDBThread( |
| 328 const LoadedCallback& loaded_callback) { | 337 const LoadedCallback& loaded_callback, const base::Time& posted_at) { |
| 329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 330 IncrementTimeDelta increment(&cookie_load_duration_); | 339 IncrementTimeDelta increment(&cookie_load_duration_); |
| 331 | 340 |
| 341 UMA_HISTOGRAM_CUSTOM_TIMES( | |
| 342 "Cookie.TimeLoadDBQueueWait", | |
| 343 base::Time::Now() - posted_at, | |
| 344 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), | |
| 345 50); | |
|
Randy Smith (Not in Mondays)
2011/12/05 22:18:08
Do the queue load histograms give us a lot more in
erikwright (departed)
2011/12/06 00:07:09
AFAIK, task profiler data is not yet available fro
| |
| 346 | |
| 332 if (!InitializeDatabase()) { | 347 if (!InitializeDatabase()) { |
| 333 BrowserThread::PostTask( | 348 BrowserThread::PostTask( |
| 334 BrowserThread::IO, FROM_HERE, | 349 BrowserThread::IO, FROM_HERE, |
| 335 base::Bind(&SQLitePersistentCookieStore::Backend::CompleteLoadOnIOThread, | 350 base::Bind(&SQLitePersistentCookieStore::Backend::CompleteLoadOnIOThread, |
| 336 this, loaded_callback, false)); | 351 this, loaded_callback, false)); |
| 337 } else { | 352 } else { |
| 338 ChainLoadCookies(loaded_callback); | 353 ChainLoadCookies(loaded_callback); |
| 339 } | 354 } |
| 340 } | 355 } |
| 341 | 356 |
| 342 void SQLitePersistentCookieStore::Backend::LoadKeyAndNotifyOnDBThread( | 357 void SQLitePersistentCookieStore::Backend::LoadKeyAndNotifyOnDBThread( |
| 343 const std::string& key, | 358 const std::string& key, |
| 344 const LoadedCallback& loaded_callback) { | 359 const LoadedCallback& loaded_callback, |
| 360 const base::Time& posted_at) { | |
| 345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 361 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 346 IncrementTimeDelta increment(&cookie_load_duration_); | 362 IncrementTimeDelta increment(&cookie_load_duration_); |
| 347 | 363 |
| 364 UMA_HISTOGRAM_CUSTOM_TIMES( | |
| 365 "Cookie.TimeKeyLoadDBQueueWait", | |
| 366 base::Time::Now() - posted_at, | |
| 367 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), | |
| 368 50); | |
| 369 | |
| 348 bool success = false; | 370 bool success = false; |
| 349 if (InitializeDatabase()) { | 371 if (InitializeDatabase()) { |
| 350 std::map<std::string, std::set<std::string> >::iterator | 372 std::map<std::string, std::set<std::string> >::iterator |
| 351 it = keys_to_load_.find(key); | 373 it = keys_to_load_.find(key); |
| 352 if (it != keys_to_load_.end()) { | 374 if (it != keys_to_load_.end()) { |
| 353 success = LoadCookiesForDomains(it->second); | 375 success = LoadCookiesForDomains(it->second); |
| 354 keys_to_load_.erase(it); | 376 keys_to_load_.erase(it); |
| 355 } else { | 377 } else { |
| 356 success = true; | 378 success = true; |
| 357 } | 379 } |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 396 base::AutoLock locked(metrics_lock_); | 418 base::AutoLock locked(metrics_lock_); |
| 397 UMA_HISTOGRAM_CUSTOM_TIMES( | 419 UMA_HISTOGRAM_CUSTOM_TIMES( |
| 398 "Cookie.PriorityBlockingTime", | 420 "Cookie.PriorityBlockingTime", |
| 399 priority_wait_duration_, | 421 priority_wait_duration_, |
| 400 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), | 422 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), |
| 401 50); | 423 50); |
| 402 | 424 |
| 403 UMA_HISTOGRAM_COUNTS_100( | 425 UMA_HISTOGRAM_COUNTS_100( |
| 404 "Cookie.PriorityLoadCount", | 426 "Cookie.PriorityLoadCount", |
| 405 total_priority_requests_); | 427 total_priority_requests_); |
| 428 | |
| 429 UMA_HISTOGRAM_COUNTS_10000( | |
| 430 "Cookie.NumberOfLoadedCookies", | |
| 431 num_cookies_read_); | |
| 406 } | 432 } |
| 407 } | 433 } |
| 408 | 434 |
| 409 void SQLitePersistentCookieStore::Backend::CompleteLoadOnIOThread( | 435 void SQLitePersistentCookieStore::Backend::CompleteLoadOnIOThread( |
| 410 const LoadedCallback& loaded_callback, bool load_success) { | 436 const LoadedCallback& loaded_callback, bool load_success) { |
| 411 Notify(loaded_callback, load_success); | 437 Notify(loaded_callback, load_success); |
| 412 | 438 |
| 413 if (load_success) | 439 if (load_success) |
| 414 ReportMetrics(); | 440 ReportMetrics(); |
| 415 } | 441 } |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 428 loaded_callback.Run(cookies); | 454 loaded_callback.Run(cookies); |
| 429 } | 455 } |
| 430 | 456 |
| 431 bool SQLitePersistentCookieStore::Backend::InitializeDatabase() { | 457 bool SQLitePersistentCookieStore::Backend::InitializeDatabase() { |
| 432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 458 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 433 | 459 |
| 434 if (initialized_) { | 460 if (initialized_) { |
| 435 return true; | 461 return true; |
| 436 } | 462 } |
| 437 | 463 |
| 464 base::Time start = base::Time::Now(); | |
| 465 | |
| 438 const FilePath dir = path_.DirName(); | 466 const FilePath dir = path_.DirName(); |
| 439 if (!file_util::PathExists(dir) && !file_util::CreateDirectory(dir)) { | 467 if (!file_util::PathExists(dir) && !file_util::CreateDirectory(dir)) { |
| 440 return false; | 468 return false; |
| 441 } | 469 } |
| 442 | 470 |
| 471 int64 db_size = 0; | |
| 472 if (file_util::GetFileSize(path_, &db_size)) { | |
| 473 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
| 474 UMA_HISTOGRAM_COUNTS("Cookie.DBSizeInKB", db_size / 1024 ); | |
| 475 } | |
| 476 | |
| 443 db_.reset(new sql::Connection); | 477 db_.reset(new sql::Connection); |
| 444 if (!db_->Open(path_)) { | 478 if (!db_->Open(path_)) { |
| 445 NOTREACHED() << "Unable to open cookie DB."; | 479 NOTREACHED() << "Unable to open cookie DB."; |
| 446 db_.reset(); | 480 db_.reset(); |
| 447 return false; | 481 return false; |
| 448 } | 482 } |
| 449 | 483 |
| 450 db_->set_error_delegate(GetErrorHandlerForCookieDb()); | 484 db_->set_error_delegate(GetErrorHandlerForCookieDb()); |
| 451 | 485 |
| 452 if (!EnsureDatabaseVersion() || !InitTable(db_.get())) { | 486 if (!EnsureDatabaseVersion() || !InitTable(db_.get())) { |
| 453 NOTREACHED() << "Unable to open cookie DB."; | 487 NOTREACHED() << "Unable to open cookie DB."; |
| 454 db_.reset(); | 488 db_.reset(); |
| 455 return false; | 489 return false; |
| 456 } | 490 } |
| 457 | 491 |
| 458 db_->Preload(); | 492 db_->Preload(); |
|
Randy Smith (Not in Mondays)
2011/12/05 22:18:08
Didn't we nuke this? Is there context I'm missing
erikwright (departed)
2011/12/06 00:07:09
After uploading that CL I decided I would prefer t
| |
| 459 | 493 |
| 494 UMA_HISTOGRAM_CUSTOM_TIMES( | |
| 495 "Cookie.TimeInitializeDB", | |
| 496 base::Time::Now() - start, | |
| 497 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), | |
| 498 50); | |
| 499 | |
| 500 start = base::Time::Now(); | |
| 501 | |
| 460 // Retrieve all the domains | 502 // Retrieve all the domains |
| 461 sql::Statement smt(db_->GetUniqueStatement( | 503 sql::Statement smt(db_->GetUniqueStatement( |
| 462 "SELECT DISTINCT host_key FROM cookies")); | 504 "SELECT DISTINCT host_key FROM cookies")); |
| 463 | 505 |
| 464 if (!smt) { | 506 if (!smt) { |
| 465 NOTREACHED() << "select statement prep failed"; | 507 NOTREACHED() << "select statement prep failed"; |
| 466 db_.reset(); | 508 db_.reset(); |
| 467 return false; | 509 return false; |
| 468 } | 510 } |
| 469 | 511 |
| 470 // Build a map of domain keys (always eTLD+1) to domains. | 512 // Build a map of domain keys (always eTLD+1) to domains. |
| 471 while (smt.Step()) { | 513 while (smt.Step()) { |
| 472 std::string domain = smt.ColumnString(0); | 514 std::string domain = smt.ColumnString(0); |
| 473 std::string key = | 515 std::string key = |
| 474 net::RegistryControlledDomainService::GetDomainAndRegistry(domain); | 516 net::RegistryControlledDomainService::GetDomainAndRegistry(domain); |
| 475 | 517 |
| 476 std::map<std::string, std::set<std::string> >::iterator it = | 518 std::map<std::string, std::set<std::string> >::iterator it = |
| 477 keys_to_load_.find(key); | 519 keys_to_load_.find(key); |
| 478 if (it == keys_to_load_.end()) | 520 if (it == keys_to_load_.end()) |
| 479 it = keys_to_load_.insert(std::make_pair | 521 it = keys_to_load_.insert(std::make_pair |
| 480 (key, std::set<std::string>())).first; | 522 (key, std::set<std::string>())).first; |
| 481 it->second.insert(domain); | 523 it->second.insert(domain); |
| 482 } | 524 } |
| 483 | 525 |
| 526 UMA_HISTOGRAM_CUSTOM_TIMES( | |
| 527 "Cookie.TimeInitializeDomainMap", | |
| 528 base::Time::Now() - start, | |
| 529 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), | |
| 530 50); | |
| 531 | |
| 484 initialized_ = true; | 532 initialized_ = true; |
| 485 return true; | 533 return true; |
| 486 } | 534 } |
| 487 | 535 |
| 488 void SQLitePersistentCookieStore::Backend::ChainLoadCookies( | 536 void SQLitePersistentCookieStore::Backend::ChainLoadCookies( |
| 489 const LoadedCallback& loaded_callback) { | 537 const LoadedCallback& loaded_callback) { |
| 490 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 538 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 491 IncrementTimeDelta increment(&cookie_load_duration_); | 539 IncrementTimeDelta increment(&cookie_load_duration_); |
| 492 | 540 |
| 493 bool load_success = true; | 541 bool load_success = true; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 558 Time::FromInternalValue(smt.ColumnInt64(0)), // creation_utc | 606 Time::FromInternalValue(smt.ColumnInt64(0)), // creation_utc |
| 559 Time::FromInternalValue(smt.ColumnInt64(5)), // expires_utc | 607 Time::FromInternalValue(smt.ColumnInt64(5)), // expires_utc |
| 560 Time::FromInternalValue(smt.ColumnInt64(8)), // last_access_utc | 608 Time::FromInternalValue(smt.ColumnInt64(8)), // last_access_utc |
| 561 smt.ColumnInt(6) != 0, // secure | 609 smt.ColumnInt(6) != 0, // secure |
| 562 smt.ColumnInt(7) != 0, // httponly | 610 smt.ColumnInt(7) != 0, // httponly |
| 563 smt.ColumnInt(9) != 0, // has_expires | 611 smt.ColumnInt(9) != 0, // has_expires |
| 564 smt.ColumnInt(10) != 0)); // is_persistent | 612 smt.ColumnInt(10) != 0)); // is_persistent |
| 565 DLOG_IF(WARNING, | 613 DLOG_IF(WARNING, |
| 566 cc->CreationDate() > Time::Now()) << L"CreationDate too recent"; | 614 cc->CreationDate() > Time::Now()) << L"CreationDate too recent"; |
| 567 cookies.push_back(cc.release()); | 615 cookies.push_back(cc.release()); |
| 616 ++num_cookies_read_; | |
| 568 } | 617 } |
| 569 smt.Reset(); | 618 smt.Reset(); |
| 570 } | 619 } |
| 571 { | 620 { |
| 572 base::AutoLock locked(lock_); | 621 base::AutoLock locked(lock_); |
| 573 cookies_.insert(cookies_.end(), cookies.begin(), cookies.end()); | 622 cookies_.insert(cookies_.end(), cookies.begin(), cookies.end()); |
| 574 } | 623 } |
| 575 return true; | 624 return true; |
| 576 } | 625 } |
| 577 | 626 |
| (...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 906 if (backend_.get()) | 955 if (backend_.get()) |
| 907 backend_->SetClearLocalStateOnExit(clear_local_state); | 956 backend_->SetClearLocalStateOnExit(clear_local_state); |
| 908 } | 957 } |
| 909 | 958 |
| 910 void SQLitePersistentCookieStore::Flush(Task* completion_task) { | 959 void SQLitePersistentCookieStore::Flush(Task* completion_task) { |
| 911 if (backend_.get()) | 960 if (backend_.get()) |
| 912 backend_->Flush(completion_task); | 961 backend_->Flush(completion_task); |
| 913 else if (completion_task) | 962 else if (completion_task) |
| 914 MessageLoop::current()->PostTask(FROM_HERE, completion_task); | 963 MessageLoop::current()->PostTask(FROM_HERE, completion_task); |
| 915 } | 964 } |
| OLD | NEW |