Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/component_updater/background_downloader_win.h" | 5 #include "chrome/browser/component_updater/background_downloader_win.h" |
| 6 | 6 |
| 7 #include <atlbase.h> | 7 #include <atlbase.h> |
| 8 #include <atlcom.h> | 8 #include <atlcom.h> |
| 9 | 9 |
| 10 #include <functional> | 10 #include <functional> |
| 11 #include <iomanip> | 11 #include <iomanip> |
| 12 #include <vector> | 12 #include <vector> |
| 13 | 13 |
| 14 #include "base/file_util.h" | 14 #include "base/file_util.h" |
| 15 #include "base/message_loop/message_loop_proxy.h" | |
| 15 #include "base/strings/sys_string_conversions.h" | 16 #include "base/strings/sys_string_conversions.h" |
| 16 #include "base/win/scoped_co_mem.h" | 17 #include "base/win/scoped_co_mem.h" |
| 17 #include "chrome/browser/component_updater/component_updater_utils.h" | 18 #include "chrome/browser/component_updater/component_updater_utils.h" |
| 18 #include "content/public/browser/browser_thread.h" | |
| 19 #include "ui/base/win/atl_module.h" | 19 #include "ui/base/win/atl_module.h" |
| 20 #include "url/gurl.h" | 20 #include "url/gurl.h" |
| 21 | 21 |
| 22 using base::win::ScopedCoMem; | 22 using base::win::ScopedCoMem; |
| 23 using base::win::ScopedComPtr; | 23 using base::win::ScopedComPtr; |
| 24 using content::BrowserThread; | |
| 25 | 24 |
| 26 // The class BackgroundDownloader in this module is an adapter between | 25 // The class BackgroundDownloader in this module is an adapter between |
| 27 // the CrxDownloader interface and the BITS service interfaces. | 26 // the CrxDownloader interface and the BITS service interfaces. |
| 28 // The interface exposed on the CrxDownloader code runs on the UI thread, while | 27 // The interface exposed on the CrxDownloader code runs on the main thread, |
| 29 // the BITS specific code runs in a single threaded apartment on the FILE | 28 // while the BITS specific code runs on a single thread task runner. |
|
blundell
2014/07/15 08:45:20
I would say something like "a separate thread pass
tommycli
2014/07/15 18:58:25
Done.
| |
| 30 // thread. | |
| 31 // For every url to download, a BITS job is created, unless there is already | 29 // For every url to download, a BITS job is created, unless there is already |
| 32 // an existing job for that url, in which case, the downloader connects to it. | 30 // an existing job for that url, in which case, the downloader connects to it. |
| 33 // Once a job is associated with the url, the code looks for changes in the | 31 // Once a job is associated with the url, the code looks for changes in the |
| 34 // BITS job state. The checks are triggered by a timer. | 32 // BITS job state. The checks are triggered by a timer. |
| 35 // The BITS job contains just one file to download. There could only be one | 33 // The BITS job contains just one file to download. There could only be one |
| 36 // download in progress at a time. If Chrome closes down before the download is | 34 // download in progress at a time. If Chrome closes down before the download is |
| 37 // complete, the BITS job remains active and finishes in the background, without | 35 // complete, the BITS job remains active and finishes in the background, without |
| 38 // any intervention. The job can be completed next time the code runs, if the | 36 // any intervention. The job can be completed next time the code runs, if the |
| 39 // file is still needed, otherwise it will be cleaned up on a periodic basis. | 37 // file is still needed, otherwise it will be cleaned up on a periodic basis. |
| 40 // | 38 // |
| (...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 380 } | 378 } |
| 381 | 379 |
| 382 return S_OK; | 380 return S_OK; |
| 383 } | 381 } |
| 384 | 382 |
| 385 } // namespace | 383 } // namespace |
| 386 | 384 |
| 387 BackgroundDownloader::BackgroundDownloader( | 385 BackgroundDownloader::BackgroundDownloader( |
| 388 scoped_ptr<CrxDownloader> successor, | 386 scoped_ptr<CrxDownloader> successor, |
| 389 net::URLRequestContextGetter* context_getter, | 387 net::URLRequestContextGetter* context_getter, |
| 390 scoped_refptr<base::SequencedTaskRunner> task_runner) | 388 scoped_refptr<base::SingleThreadTaskRunner> task_runner) |
| 391 : CrxDownloader(successor.Pass()), | 389 : CrxDownloader(successor.Pass()), |
| 390 main_loop_(base::MessageLoopProxy::current()), | |
| 392 context_getter_(context_getter), | 391 context_getter_(context_getter), |
| 393 task_runner_(task_runner), | 392 task_runner_(task_runner), |
| 394 is_completed_(false) { | 393 is_completed_(false) { |
| 395 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 394 DCHECK(thread_checker_.CalledOnValidThread()); |
|
blundell
2014/07/15 08:45:20
this DCHECK is meaningless I think as thread_check
tommycli
2014/07/15 18:58:25
Done.
| |
| 396 } | 395 } |
| 397 | 396 |
| 398 BackgroundDownloader::~BackgroundDownloader() { | 397 BackgroundDownloader::~BackgroundDownloader() { |
| 399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 398 DCHECK(thread_checker_.CalledOnValidThread()); |
| 400 | 399 |
| 401 // The following objects have thread affinity and can't be destroyed on the | 400 // The following objects have thread affinity and can't be destroyed on the |
| 402 // UI thread. The resources managed by these objects are acquired at the | 401 // main thread. The resources managed by these objects are acquired at the |
| 403 // beginning of a download and released at the end of the download. Most of | 402 // beginning of a download and released at the end of the download. Most of |
| 404 // the time, when this destructor is called, these resources have been already | 403 // the time, when this destructor is called, these resources have been already |
| 405 // disposed by. Releasing the ownership here is a NOP. However, if the browser | 404 // disposed by. Releasing the ownership here is a NOP. However, if the browser |
| 406 // is shutting down while a download is in progress, the timer is active and | 405 // is shutting down while a download is in progress, the timer is active and |
| 407 // the interface pointers are valid. Releasing the ownership means leaking | 406 // the interface pointers are valid. Releasing the ownership means leaking |
| 408 // these objects and their associated resources. | 407 // these objects and their associated resources. |
| 409 timer_.release(); | 408 timer_.release(); |
| 410 bits_manager_.Detach(); | 409 bits_manager_.Detach(); |
| 411 job_.Detach(); | 410 job_.Detach(); |
| 412 } | 411 } |
| 413 | 412 |
| 414 void BackgroundDownloader::DoStartDownload(const GURL& url) { | 413 void BackgroundDownloader::DoStartDownload(const GURL& url) { |
| 415 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 414 DCHECK(thread_checker_.CalledOnValidThread()); |
| 416 | 415 |
| 417 BrowserThread::PostTask( | 416 task_runner_->PostTask( |
| 418 BrowserThread::FILE, | |
| 419 FROM_HERE, | 417 FROM_HERE, |
| 420 base::Bind( | 418 base::Bind( |
| 421 &BackgroundDownloader::BeginDownload, base::Unretained(this), url)); | 419 &BackgroundDownloader::BeginDownload, base::Unretained(this), url)); |
| 422 } | 420 } |
| 423 | 421 |
| 424 // Called once when this class is asked to do a download. Creates or opens | 422 // Called once when this class is asked to do a download. Creates or opens |
| 425 // an existing bits job, hooks up the notifications, and starts the timer. | 423 // an existing bits job, hooks up the notifications, and starts the timer. |
| 426 void BackgroundDownloader::BeginDownload(const GURL& url) { | 424 void BackgroundDownloader::BeginDownload(const GURL& url) { |
| 427 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 425 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 428 | 426 |
| 429 DCHECK(!timer_); | 427 DCHECK(!timer_); |
| 430 | 428 |
| 431 is_completed_ = false; | 429 is_completed_ = false; |
| 432 download_start_time_ = base::Time::Now(); | 430 download_start_time_ = base::Time::Now(); |
| 433 job_stuck_begin_time_ = download_start_time_; | 431 job_stuck_begin_time_ = download_start_time_; |
| 434 | 432 |
| 435 HRESULT hr = QueueBitsJob(url); | 433 HRESULT hr = QueueBitsJob(url); |
| 436 if (FAILED(hr)) { | 434 if (FAILED(hr)) { |
| 437 EndDownload(hr); | 435 EndDownload(hr); |
| 438 return; | 436 return; |
| 439 } | 437 } |
| 440 | 438 |
| 441 // A repeating timer retains the user task. This timer can be stopped and | 439 // A repeating timer retains the user task. This timer can be stopped and |
| 442 // reset multiple times. | 440 // reset multiple times. |
| 443 timer_.reset(new base::RepeatingTimer<BackgroundDownloader>); | 441 timer_.reset(new base::RepeatingTimer<BackgroundDownloader>); |
| 444 timer_->Start(FROM_HERE, | 442 timer_->Start(FROM_HERE, |
| 445 base::TimeDelta::FromSeconds(kJobPollingIntervalSec), | 443 base::TimeDelta::FromSeconds(kJobPollingIntervalSec), |
| 446 this, | 444 this, |
| 447 &BackgroundDownloader::OnDownloading); | 445 &BackgroundDownloader::OnDownloading); |
| 448 } | 446 } |
| 449 | 447 |
| 450 // Called any time the timer fires. | 448 // Called any time the timer fires. |
| 451 void BackgroundDownloader::OnDownloading() { | 449 void BackgroundDownloader::OnDownloading() { |
| 452 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 450 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 453 | 451 |
| 454 DCHECK(job_); | 452 DCHECK(job_); |
| 455 | 453 |
| 456 DCHECK(!is_completed_); | 454 DCHECK(!is_completed_); |
| 457 if (is_completed_) | 455 if (is_completed_) |
| 458 return; | 456 return; |
| 459 | 457 |
| 460 BG_JOB_STATE job_state = BG_JOB_STATE_ERROR; | 458 BG_JOB_STATE job_state = BG_JOB_STATE_ERROR; |
| 461 HRESULT hr = job_->GetState(&job_state); | 459 HRESULT hr = job_->GetState(&job_state); |
| 462 if (FAILED(hr)) { | 460 if (FAILED(hr)) { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 498 break; | 496 break; |
| 499 | 497 |
| 500 default: | 498 default: |
| 501 break; | 499 break; |
| 502 } | 500 } |
| 503 } | 501 } |
| 504 | 502 |
| 505 // Completes the BITS download, picks up the file path of the response, and | 503 // Completes the BITS download, picks up the file path of the response, and |
| 506 // notifies the CrxDownloader. The function should be called only once. | 504 // notifies the CrxDownloader. The function should be called only once. |
| 507 void BackgroundDownloader::EndDownload(HRESULT error) { | 505 void BackgroundDownloader::EndDownload(HRESULT error) { |
| 508 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 506 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 509 | 507 |
| 510 DCHECK(!is_completed_); | 508 DCHECK(!is_completed_); |
| 511 is_completed_ = true; | 509 is_completed_ = true; |
| 512 | 510 |
| 513 timer_.reset(); | 511 timer_.reset(); |
| 514 | 512 |
| 515 const base::Time download_end_time(base::Time::Now()); | 513 const base::Time download_end_time(base::Time::Now()); |
| 516 const base::TimeDelta download_time = | 514 const base::TimeDelta download_time = |
| 517 download_end_time >= download_start_time_ | 515 download_end_time >= download_start_time_ |
| 518 ? download_end_time - download_start_time_ | 516 ? download_end_time - download_start_time_ |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 545 download_metrics.error = error_to_report; | 543 download_metrics.error = error_to_report; |
| 546 download_metrics.downloaded_bytes = downloaded_bytes; | 544 download_metrics.downloaded_bytes = downloaded_bytes; |
| 547 download_metrics.total_bytes = total_bytes; | 545 download_metrics.total_bytes = total_bytes; |
| 548 download_metrics.download_time_ms = download_time.InMilliseconds(); | 546 download_metrics.download_time_ms = download_time.InMilliseconds(); |
| 549 | 547 |
| 550 Result result; | 548 Result result; |
| 551 result.error = error_to_report; | 549 result.error = error_to_report; |
| 552 result.response = response_; | 550 result.response = response_; |
| 553 result.downloaded_bytes = downloaded_bytes; | 551 result.downloaded_bytes = downloaded_bytes; |
| 554 result.total_bytes = total_bytes; | 552 result.total_bytes = total_bytes; |
| 555 BrowserThread::PostTask(BrowserThread::UI, | 553 main_loop_->PostTask(FROM_HERE, |
| 556 FROM_HERE, | 554 base::Bind(&BackgroundDownloader::OnDownloadComplete, |
| 557 base::Bind(&BackgroundDownloader::OnDownloadComplete, | 555 base::Unretained(this), |
| 558 base::Unretained(this), | 556 is_handled, |
| 559 is_handled, | 557 result, |
| 560 result, | 558 download_metrics)); |
| 561 download_metrics)); | |
| 562 | 559 |
| 563 // Once the task is posted to the the UI thread, this object may be deleted | 560 // Once the task is posted to the the main thread, this object may be deleted |
| 564 // by its owner. It is not safe to access members of this object on the | 561 // by its owner. It is not safe to access members of this object on the |
| 565 // FILE thread from this point on. The timer is stopped and all BITS | 562 // task runner from this point on. The timer is stopped and all BITS |
| 566 // interface pointers have been released. | 563 // interface pointers have been released. |
| 567 } | 564 } |
| 568 | 565 |
| 569 // Called when the BITS job has been transferred successfully. Completes the | 566 // Called when the BITS job has been transferred successfully. Completes the |
| 570 // BITS job by removing it from the BITS queue and making the download | 567 // BITS job by removing it from the BITS queue and making the download |
| 571 // available to the caller. | 568 // available to the caller. |
| 572 void BackgroundDownloader::OnStateTransferred() { | 569 void BackgroundDownloader::OnStateTransferred() { |
| 573 EndDownload(CompleteJob()); | 570 EndDownload(CompleteJob()); |
| 574 } | 571 } |
| 575 | 572 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 618 int64 downloaded_bytes = -1; | 615 int64 downloaded_bytes = -1; |
| 619 int64 total_bytes = -1; | 616 int64 total_bytes = -1; |
| 620 HRESULT hr = GetJobByteCount(job_, &downloaded_bytes, &total_bytes); | 617 HRESULT hr = GetJobByteCount(job_, &downloaded_bytes, &total_bytes); |
| 621 if (FAILED(hr)) | 618 if (FAILED(hr)) |
| 622 return; | 619 return; |
| 623 | 620 |
| 624 Result result; | 621 Result result; |
| 625 result.downloaded_bytes = downloaded_bytes; | 622 result.downloaded_bytes = downloaded_bytes; |
| 626 result.total_bytes = total_bytes; | 623 result.total_bytes = total_bytes; |
| 627 | 624 |
| 628 BrowserThread::PostTask(BrowserThread::UI, | 625 main_loop_->PostTask(FROM_HERE, |
| 629 FROM_HERE, | 626 base::Bind(&BackgroundDownloader::OnDownloadProgress, |
| 630 base::Bind(&BackgroundDownloader::OnDownloadProgress, | 627 base::Unretained(this), |
| 631 base::Unretained(this), | 628 result)); |
| 632 result)); | |
| 633 } | 629 } |
| 634 | 630 |
| 635 // Called when the download was cancelled. Since the observer should have | 631 // Called when the download was cancelled. Since the observer should have |
| 636 // been disconnected by now, this notification must not be seen. | 632 // been disconnected by now, this notification must not be seen. |
| 637 void BackgroundDownloader::OnStateCancelled() { | 633 void BackgroundDownloader::OnStateCancelled() { |
| 638 EndDownload(E_UNEXPECTED); | 634 EndDownload(E_UNEXPECTED); |
| 639 } | 635 } |
| 640 | 636 |
| 641 // Called when the download was completed. Same as above. | 637 // Called when the download was completed. Same as above. |
| 642 void BackgroundDownloader::OnStateAcknowledged() { | 638 void BackgroundDownloader::OnStateAcknowledged() { |
| 643 EndDownload(E_UNEXPECTED); | 639 EndDownload(E_UNEXPECTED); |
| 644 } | 640 } |
| 645 | 641 |
| 646 // Creates or opens a job for the given url and queues it up. Tries to | 642 // Creates or opens a job for the given url and queues it up. Tries to |
| 647 // install a job observer but continues on if an observer can't be set up. | 643 // install a job observer but continues on if an observer can't be set up. |
| 648 HRESULT BackgroundDownloader::QueueBitsJob(const GURL& url) { | 644 HRESULT BackgroundDownloader::QueueBitsJob(const GURL& url) { |
| 649 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 645 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 650 | 646 |
| 651 HRESULT hr = S_OK; | 647 HRESULT hr = S_OK; |
| 652 if (bits_manager_ == NULL) { | 648 if (bits_manager_ == NULL) { |
| 653 hr = GetBitsManager(bits_manager_.Receive()); | 649 hr = GetBitsManager(bits_manager_.Receive()); |
| 654 if (FAILED(hr)) | 650 if (FAILED(hr)) |
| 655 return hr; | 651 return hr; |
| 656 } | 652 } |
| 657 | 653 |
| 658 hr = CreateOrOpenJob(url); | 654 hr = CreateOrOpenJob(url); |
| 659 if (FAILED(hr)) | 655 if (FAILED(hr)) |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 759 // must match as a job only contains one file. | 755 // must match as a job only contains one file. |
| 760 DCHECK(progress.Completed); | 756 DCHECK(progress.Completed); |
| 761 DCHECK_EQ(progress.BytesTotal, progress.BytesTransferred); | 757 DCHECK_EQ(progress.BytesTotal, progress.BytesTransferred); |
| 762 | 758 |
| 763 response_ = base::FilePath(local_name); | 759 response_ = base::FilePath(local_name); |
| 764 | 760 |
| 765 return S_OK; | 761 return S_OK; |
| 766 } | 762 } |
| 767 | 763 |
| 768 } // namespace component_updater | 764 } // namespace component_updater |
| OLD | NEW |