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" |
| 16 #include "base/single_thread_task_runner.h" |
15 #include "base/strings/sys_string_conversions.h" | 17 #include "base/strings/sys_string_conversions.h" |
16 #include "base/win/scoped_co_mem.h" | 18 #include "base/win/scoped_co_mem.h" |
17 #include "chrome/browser/component_updater/component_updater_utils.h" | 19 #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" | 20 #include "ui/base/win/atl_module.h" |
20 #include "url/gurl.h" | 21 #include "url/gurl.h" |
21 | 22 |
22 using base::win::ScopedCoMem; | 23 using base::win::ScopedCoMem; |
23 using base::win::ScopedComPtr; | 24 using base::win::ScopedComPtr; |
24 using content::BrowserThread; | |
25 | 25 |
26 // The class BackgroundDownloader in this module is an adapter between | 26 // The class BackgroundDownloader in this module is an adapter between |
27 // the CrxDownloader interface and the BITS service interfaces. | 27 // the CrxDownloader interface and the BITS service interfaces. |
28 // The interface exposed on the CrxDownloader code runs on the UI thread, while | 28 // 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 | 29 // while the BITS specific code runs on a separate thread passed in by the |
30 // thread. | 30 // client. For every url to download, a BITS job is created, unless there is |
31 // For every url to download, a BITS job is created, unless there is already | 31 // already an existing job for that url, in which case, the downloader |
32 // an existing job for that url, in which case, the downloader connects to it. | 32 // connects to it. Once a job is associated with the url, the code looks for |
33 // Once a job is associated with the url, the code looks for changes in the | 33 // changes in the BITS job state. The checks are triggered by a timer. |
34 // 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 | 34 // 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 | 35 // 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 | 36 // 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 | 37 // 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. | 38 // file is still needed, otherwise it will be cleaned up on a periodic basis. |
40 // | 39 // |
41 // To list the BITS jobs for a user, use the |bitsadmin| tool. The command line | 40 // To list the BITS jobs for a user, use the |bitsadmin| tool. The command line |
42 // to do that is: "bitsadmin /list /verbose". Another useful command is | 41 // to do that is: "bitsadmin /list /verbose". Another useful command is |
43 // "bitsadmin /info" and provide the job id returned by the previous /list | 42 // "bitsadmin /info" and provide the job id returned by the previous /list |
44 // command. | 43 // command. |
(...skipping 335 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
380 } | 379 } |
381 | 380 |
382 return S_OK; | 381 return S_OK; |
383 } | 382 } |
384 | 383 |
385 } // namespace | 384 } // namespace |
386 | 385 |
387 BackgroundDownloader::BackgroundDownloader( | 386 BackgroundDownloader::BackgroundDownloader( |
388 scoped_ptr<CrxDownloader> successor, | 387 scoped_ptr<CrxDownloader> successor, |
389 net::URLRequestContextGetter* context_getter, | 388 net::URLRequestContextGetter* context_getter, |
390 scoped_refptr<base::SequencedTaskRunner> task_runner) | 389 scoped_refptr<base::SingleThreadTaskRunner> task_runner) |
391 : CrxDownloader(successor.Pass()), | 390 : CrxDownloader(successor.Pass()), |
| 391 main_task_runner_(base::MessageLoopProxy::current()), |
392 context_getter_(context_getter), | 392 context_getter_(context_getter), |
393 task_runner_(task_runner), | 393 task_runner_(task_runner), |
394 is_completed_(false) { | 394 is_completed_(false) { |
395 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
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_task_runner_->PostTask( |
556 FROM_HERE, | 554 FROM_HERE, |
557 base::Bind(&BackgroundDownloader::OnDownloadComplete, | 555 base::Bind(&BackgroundDownloader::OnDownloadComplete, |
558 base::Unretained(this), | 556 base::Unretained(this), |
559 is_handled, | 557 is_handled, |
560 result, | 558 result, |
561 download_metrics)); | 559 download_metrics)); |
562 | 560 |
563 // Once the task is posted to the the UI thread, this object may be deleted | 561 // 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 | 562 // 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 | 563 // task runner from this point on. The timer is stopped and all BITS |
566 // interface pointers have been released. | 564 // interface pointers have been released. |
567 } | 565 } |
568 | 566 |
569 // Called when the BITS job has been transferred successfully. Completes the | 567 // 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 | 568 // BITS job by removing it from the BITS queue and making the download |
571 // available to the caller. | 569 // available to the caller. |
572 void BackgroundDownloader::OnStateTransferred() { | 570 void BackgroundDownloader::OnStateTransferred() { |
573 EndDownload(CompleteJob()); | 571 EndDownload(CompleteJob()); |
574 } | 572 } |
575 | 573 |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
618 int64 downloaded_bytes = -1; | 616 int64 downloaded_bytes = -1; |
619 int64 total_bytes = -1; | 617 int64 total_bytes = -1; |
620 HRESULT hr = GetJobByteCount(job_, &downloaded_bytes, &total_bytes); | 618 HRESULT hr = GetJobByteCount(job_, &downloaded_bytes, &total_bytes); |
621 if (FAILED(hr)) | 619 if (FAILED(hr)) |
622 return; | 620 return; |
623 | 621 |
624 Result result; | 622 Result result; |
625 result.downloaded_bytes = downloaded_bytes; | 623 result.downloaded_bytes = downloaded_bytes; |
626 result.total_bytes = total_bytes; | 624 result.total_bytes = total_bytes; |
627 | 625 |
628 BrowserThread::PostTask(BrowserThread::UI, | 626 main_task_runner_->PostTask( |
629 FROM_HERE, | 627 FROM_HERE, |
630 base::Bind(&BackgroundDownloader::OnDownloadProgress, | 628 base::Bind(&BackgroundDownloader::OnDownloadProgress, |
631 base::Unretained(this), | 629 base::Unretained(this), |
632 result)); | 630 result)); |
633 } | 631 } |
634 | 632 |
635 // Called when the download was cancelled. Since the observer should have | 633 // Called when the download was cancelled. Since the observer should have |
636 // been disconnected by now, this notification must not be seen. | 634 // been disconnected by now, this notification must not be seen. |
637 void BackgroundDownloader::OnStateCancelled() { | 635 void BackgroundDownloader::OnStateCancelled() { |
638 EndDownload(E_UNEXPECTED); | 636 EndDownload(E_UNEXPECTED); |
639 } | 637 } |
640 | 638 |
641 // Called when the download was completed. Same as above. | 639 // Called when the download was completed. Same as above. |
642 void BackgroundDownloader::OnStateAcknowledged() { | 640 void BackgroundDownloader::OnStateAcknowledged() { |
643 EndDownload(E_UNEXPECTED); | 641 EndDownload(E_UNEXPECTED); |
644 } | 642 } |
645 | 643 |
646 // Creates or opens a job for the given url and queues it up. Tries to | 644 // 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. | 645 // install a job observer but continues on if an observer can't be set up. |
648 HRESULT BackgroundDownloader::QueueBitsJob(const GURL& url) { | 646 HRESULT BackgroundDownloader::QueueBitsJob(const GURL& url) { |
649 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 647 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
650 | 648 |
651 HRESULT hr = S_OK; | 649 HRESULT hr = S_OK; |
652 if (bits_manager_ == NULL) { | 650 if (bits_manager_ == NULL) { |
653 hr = GetBitsManager(bits_manager_.Receive()); | 651 hr = GetBitsManager(bits_manager_.Receive()); |
654 if (FAILED(hr)) | 652 if (FAILED(hr)) |
655 return hr; | 653 return hr; |
656 } | 654 } |
657 | 655 |
658 hr = CreateOrOpenJob(url); | 656 hr = CreateOrOpenJob(url); |
659 if (FAILED(hr)) | 657 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. | 757 // must match as a job only contains one file. |
760 DCHECK(progress.Completed); | 758 DCHECK(progress.Completed); |
761 DCHECK_EQ(progress.BytesTotal, progress.BytesTransferred); | 759 DCHECK_EQ(progress.BytesTotal, progress.BytesTransferred); |
762 | 760 |
763 response_ = base::FilePath(local_name); | 761 response_ = base::FilePath(local_name); |
764 | 762 |
765 return S_OK; | 763 return S_OK; |
766 } | 764 } |
767 | 765 |
768 } // namespace component_updater | 766 } // namespace component_updater |
OLD | NEW |