OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "components/update_client/background_downloader_win.h" | 5 #include "components/update_client/background_downloader_win.h" |
6 | 6 |
7 #include <atlbase.h> | 7 #include <atlbase.h> |
8 #include <atlcom.h> | 8 #include <atlcom.h> |
9 #include <stddef.h> | 9 #include <stddef.h> |
10 | 10 |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
141 DWORD cookie, | 141 DWORD cookie, |
142 ScopedComPtr<T>* p) { | 142 ScopedComPtr<T>* p) { |
143 return git->GetInterfaceFromGlobal(cookie, __uuidof(T), p->ReceiveVoid()); | 143 return git->GetInterfaceFromGlobal(cookie, __uuidof(T), p->ReceiveVoid()); |
144 } | 144 } |
145 | 145 |
146 // Registers an interface pointer in GIT and returns its corresponding |cookie|. | 146 // Registers an interface pointer in GIT and returns its corresponding |cookie|. |
147 template <typename T> | 147 template <typename T> |
148 HRESULT RegisterInterfaceInGit(const ScopedComPtr<IGlobalInterfaceTable>& git, | 148 HRESULT RegisterInterfaceInGit(const ScopedComPtr<IGlobalInterfaceTable>& git, |
149 const ScopedComPtr<T>& p, | 149 const ScopedComPtr<T>& p, |
150 DWORD* cookie) { | 150 DWORD* cookie) { |
151 return git->RegisterInterfaceInGlobal(p.get(), __uuidof(T), cookie); | 151 return git->RegisterInterfaceInGlobal(p.Get(), __uuidof(T), cookie); |
152 } | 152 } |
153 | 153 |
154 // Returns the status code from a given BITS error. | 154 // Returns the status code from a given BITS error. |
155 int GetHttpStatusFromBitsError(HRESULT error) { | 155 int GetHttpStatusFromBitsError(HRESULT error) { |
156 // BITS errors are defined in bitsmsg.h. Although not documented, it is | 156 // BITS errors are defined in bitsmsg.h. Although not documented, it is |
157 // clear that all errors corresponding to http status code have the high | 157 // clear that all errors corresponding to http status code have the high |
158 // word equal to 0x8019 and the low word equal to the http status code. | 158 // word equal to 0x8019 and the low word equal to the http status code. |
159 const int kHttpStatusFirst = 100; // Continue. | 159 const int kHttpStatusFirst = 100; // Continue. |
160 const int kHttpStatusLast = 505; // Version not supported. | 160 const int kHttpStatusLast = 505; // Version not supported. |
161 bool is_valid = HIWORD(error) == 0x8019 && | 161 bool is_valid = HIWORD(error) == 0x8019 && |
(...skipping 10 matching lines...) Expand all Loading... |
172 if (FAILED(hr)) | 172 if (FAILED(hr)) |
173 return hr; | 173 return hr; |
174 | 174 |
175 ULONG num_files = 0; | 175 ULONG num_files = 0; |
176 hr = enum_files->GetCount(&num_files); | 176 hr = enum_files->GetCount(&num_files); |
177 if (FAILED(hr)) | 177 if (FAILED(hr)) |
178 return hr; | 178 return hr; |
179 | 179 |
180 for (ULONG i = 0; i != num_files; ++i) { | 180 for (ULONG i = 0; i != num_files; ++i) { |
181 ScopedComPtr<IBackgroundCopyFile> file; | 181 ScopedComPtr<IBackgroundCopyFile> file; |
182 if (enum_files->Next(1, file.Receive(), NULL) == S_OK && file.get()) | 182 if (enum_files->Next(1, file.Receive(), NULL) == S_OK && file.Get()) |
183 files->push_back(file); | 183 files->push_back(file); |
184 } | 184 } |
185 | 185 |
186 return S_OK; | 186 return S_OK; |
187 } | 187 } |
188 | 188 |
189 // Returns the file name, the url, and some per-file progress information. | 189 // Returns the file name, the url, and some per-file progress information. |
190 // The function out parameters can be NULL if that data is not requested. | 190 // The function out parameters can be NULL if that data is not requested. |
191 HRESULT GetJobFileProperties(IBackgroundCopyFile* file, | 191 HRESULT GetJobFileProperties(IBackgroundCopyFile* file, |
192 base::string16* local_name, | 192 base::string16* local_name, |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
293 ULONG job_count = 0; | 293 ULONG job_count = 0; |
294 hr = enum_jobs->GetCount(&job_count); | 294 hr = enum_jobs->GetCount(&job_count); |
295 if (FAILED(hr)) | 295 if (FAILED(hr)) |
296 return hr; | 296 return hr; |
297 | 297 |
298 // Iterate over jobs, run the predicate, and select the job only if | 298 // Iterate over jobs, run the predicate, and select the job only if |
299 // the job description matches the component updater jobs. | 299 // the job description matches the component updater jobs. |
300 for (ULONG i = 0; i != job_count; ++i) { | 300 for (ULONG i = 0; i != job_count; ++i) { |
301 ScopedComPtr<IBackgroundCopyJob> current_job; | 301 ScopedComPtr<IBackgroundCopyJob> current_job; |
302 if (enum_jobs->Next(1, current_job.Receive(), NULL) == S_OK && | 302 if (enum_jobs->Next(1, current_job.Receive(), NULL) == S_OK && |
303 pred(current_job.get())) { | 303 pred(current_job.Get())) { |
304 base::string16 job_description; | 304 base::string16 job_description; |
305 hr = GetJobDescription(current_job.get(), &job_description); | 305 hr = GetJobDescription(current_job.Get(), &job_description); |
306 if (job_description.compare(kJobDescription) == 0) | 306 if (job_description.compare(kJobDescription) == 0) |
307 jobs->push_back(current_job); | 307 jobs->push_back(current_job); |
308 } | 308 } |
309 } | 309 } |
310 | 310 |
311 return jobs->empty() ? S_FALSE : S_OK; | 311 return jobs->empty() ? S_FALSE : S_OK; |
312 } | 312 } |
313 | 313 |
314 // Compares the job creation time and returns true if the job creation time | 314 // Compares the job creation time and returns true if the job creation time |
315 // is older than |num_days|. | 315 // is older than |num_days|. |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
372 *bits_manager = object.Detach(); | 372 *bits_manager = object.Detach(); |
373 return S_OK; | 373 return S_OK; |
374 } | 374 } |
375 | 375 |
376 void CleanupJobFiles(IBackgroundCopyJob* job) { | 376 void CleanupJobFiles(IBackgroundCopyJob* job) { |
377 std::vector<ScopedComPtr<IBackgroundCopyFile>> files; | 377 std::vector<ScopedComPtr<IBackgroundCopyFile>> files; |
378 if (FAILED(GetFilesInJob(job, &files))) | 378 if (FAILED(GetFilesInJob(job, &files))) |
379 return; | 379 return; |
380 for (size_t i = 0; i != files.size(); ++i) { | 380 for (size_t i = 0; i != files.size(); ++i) { |
381 base::string16 local_name; | 381 base::string16 local_name; |
382 HRESULT hr(GetJobFileProperties(files[i].get(), &local_name, NULL, NULL)); | 382 HRESULT hr(GetJobFileProperties(files[i].Get(), &local_name, NULL, NULL)); |
383 if (SUCCEEDED(hr)) | 383 if (SUCCEEDED(hr)) |
384 DeleteFileAndEmptyParentDirectory(base::FilePath(local_name)); | 384 DeleteFileAndEmptyParentDirectory(base::FilePath(local_name)); |
385 } | 385 } |
386 } | 386 } |
387 | 387 |
388 // Cleans up incompleted jobs that are too old. | 388 // Cleans up incompleted jobs that are too old. |
389 HRESULT CleanupStaleJobs( | 389 HRESULT CleanupStaleJobs( |
390 const ScopedComPtr<IBackgroundCopyManager>& bits_manager) { | 390 const ScopedComPtr<IBackgroundCopyManager>& bits_manager) { |
391 if (!bits_manager.get()) | 391 if (!bits_manager.Get()) |
392 return E_FAIL; | 392 return E_FAIL; |
393 | 393 |
394 static base::Time last_sweep; | 394 static base::Time last_sweep; |
395 | 395 |
396 const base::TimeDelta time_delta( | 396 const base::TimeDelta time_delta( |
397 base::TimeDelta::FromDays(kPurgeStaleJobsIntervalBetweenChecksDays)); | 397 base::TimeDelta::FromDays(kPurgeStaleJobsIntervalBetweenChecksDays)); |
398 const base::Time current_time(base::Time::Now()); | 398 const base::Time current_time(base::Time::Now()); |
399 if (last_sweep + time_delta > current_time) | 399 if (last_sweep + time_delta > current_time) |
400 return S_OK; | 400 return S_OK; |
401 | 401 |
402 last_sweep = current_time; | 402 last_sweep = current_time; |
403 | 403 |
404 std::vector<ScopedComPtr<IBackgroundCopyJob>> jobs; | 404 std::vector<ScopedComPtr<IBackgroundCopyJob>> jobs; |
405 HRESULT hr = FindBitsJobIf( | 405 HRESULT hr = FindBitsJobIf( |
406 JobCreationOlderThanDays(kPurgeStaleJobsAfterDays), | 406 JobCreationOlderThanDays(kPurgeStaleJobsAfterDays), |
407 bits_manager.get(), &jobs); | 407 bits_manager.Get(), &jobs); |
408 if (FAILED(hr)) | 408 if (FAILED(hr)) |
409 return hr; | 409 return hr; |
410 | 410 |
411 for (size_t i = 0; i != jobs.size(); ++i) { | 411 for (size_t i = 0; i != jobs.size(); ++i) { |
412 jobs[i]->Cancel(); | 412 jobs[i]->Cancel(); |
413 CleanupJobFiles(jobs[i].get()); | 413 CleanupJobFiles(jobs[i].Get()); |
414 } | 414 } |
415 | 415 |
416 return S_OK; | 416 return S_OK; |
417 } | 417 } |
418 | 418 |
419 } // namespace | 419 } // namespace |
420 | 420 |
421 BackgroundDownloader::BackgroundDownloader( | 421 BackgroundDownloader::BackgroundDownloader( |
422 std::unique_ptr<CrxDownloader> successor, | 422 std::unique_ptr<CrxDownloader> successor, |
423 net::URLRequestContextGetter* context_getter, | 423 net::URLRequestContextGetter* context_getter, |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
574 DCHECK(!TimerIsRunning()); | 574 DCHECK(!TimerIsRunning()); |
575 | 575 |
576 const base::TimeTicks download_end_time(base::TimeTicks::Now()); | 576 const base::TimeTicks download_end_time(base::TimeTicks::Now()); |
577 const base::TimeDelta download_time = | 577 const base::TimeDelta download_time = |
578 download_end_time >= download_start_time_ | 578 download_end_time >= download_start_time_ |
579 ? download_end_time - download_start_time_ | 579 ? download_end_time - download_start_time_ |
580 : base::TimeDelta(); | 580 : base::TimeDelta(); |
581 | 581 |
582 int64_t downloaded_bytes = -1; | 582 int64_t downloaded_bytes = -1; |
583 int64_t total_bytes = -1; | 583 int64_t total_bytes = -1; |
584 GetJobByteCount(job_.get(), &downloaded_bytes, &total_bytes); | 584 GetJobByteCount(job_.Get(), &downloaded_bytes, &total_bytes); |
585 | 585 |
586 if (FAILED(error) && job_.get()) { | 586 if (FAILED(error) && job_.Get()) { |
587 job_->Cancel(); | 587 job_->Cancel(); |
588 CleanupJobFiles(job_.get()); | 588 CleanupJobFiles(job_.Get()); |
589 } | 589 } |
590 | 590 |
591 CleanupStaleJobs(bits_manager_); | 591 CleanupStaleJobs(bits_manager_); |
592 | 592 |
593 ClearGit(); | 593 ClearGit(); |
594 | 594 |
595 // Consider the url handled if it has been successfully downloaded or a | 595 // Consider the url handled if it has been successfully downloaded or a |
596 // 5xx has been received. | 596 // 5xx has been received. |
597 const bool is_handled = | 597 const bool is_handled = |
598 SUCCEEDED(error) || IsHttpServerError(GetHttpStatusFromBitsError(error)); | 598 SUCCEEDED(error) || IsHttpServerError(GetHttpStatusFromBitsError(error)); |
(...skipping 29 matching lines...) Expand all Loading... |
628 // available to the caller. | 628 // available to the caller. |
629 bool BackgroundDownloader::OnStateTransferred() { | 629 bool BackgroundDownloader::OnStateTransferred() { |
630 EndDownload(CompleteJob()); | 630 EndDownload(CompleteJob()); |
631 return true; | 631 return true; |
632 } | 632 } |
633 | 633 |
634 // Called when the job has encountered an error and no further progress can | 634 // Called when the job has encountered an error and no further progress can |
635 // be made. Cancels this job and removes it from the BITS queue. | 635 // be made. Cancels this job and removes it from the BITS queue. |
636 bool BackgroundDownloader::OnStateError() { | 636 bool BackgroundDownloader::OnStateError() { |
637 HRESULT error_code = S_OK; | 637 HRESULT error_code = S_OK; |
638 HRESULT hr = GetJobError(job_.get(), &error_code); | 638 HRESULT hr = GetJobError(job_.Get(), &error_code); |
639 if (FAILED(hr)) | 639 if (FAILED(hr)) |
640 error_code = hr; | 640 error_code = hr; |
641 | 641 |
642 DCHECK(FAILED(error_code)); | 642 DCHECK(FAILED(error_code)); |
643 EndDownload(error_code); | 643 EndDownload(error_code); |
644 return true; | 644 return true; |
645 } | 645 } |
646 | 646 |
647 // Called when the download was completed. This notification is not seen | 647 // Called when the download was completed. This notification is not seen |
648 // in the current implementation but provided here as a defensive programming | 648 // in the current implementation but provided here as a defensive programming |
(...skipping 14 matching lines...) Expand all Loading... |
663 bool BackgroundDownloader::OnStateTransientError() { | 663 bool BackgroundDownloader::OnStateTransientError() { |
664 // If the job appears to be stuck, handle the transient error as if | 664 // If the job appears to be stuck, handle the transient error as if |
665 // it were a final error. This causes the job to be cancelled and a specific | 665 // it were a final error. This causes the job to be cancelled and a specific |
666 // error be returned, if the error was available. | 666 // error be returned, if the error was available. |
667 if (IsStuck()) { | 667 if (IsStuck()) { |
668 return OnStateError(); | 668 return OnStateError(); |
669 } | 669 } |
670 | 670 |
671 // Don't retry at all if the transient error was a 5xx. | 671 // Don't retry at all if the transient error was a 5xx. |
672 HRESULT error_code = S_OK; | 672 HRESULT error_code = S_OK; |
673 HRESULT hr = GetJobError(job_.get(), &error_code); | 673 HRESULT hr = GetJobError(job_.Get(), &error_code); |
674 if (SUCCEEDED(hr) && | 674 if (SUCCEEDED(hr) && |
675 IsHttpServerError(GetHttpStatusFromBitsError(error_code))) { | 675 IsHttpServerError(GetHttpStatusFromBitsError(error_code))) { |
676 return OnStateError(); | 676 return OnStateError(); |
677 } | 677 } |
678 | 678 |
679 return false; | 679 return false; |
680 } | 680 } |
681 | 681 |
682 bool BackgroundDownloader::OnStateQueued() { | 682 bool BackgroundDownloader::OnStateQueued() { |
683 if (!IsStuck()) | 683 if (!IsStuck()) |
684 return false; | 684 return false; |
685 | 685 |
686 // Terminate the download if the job has not made progress in a while. | 686 // Terminate the download if the job has not made progress in a while. |
687 EndDownload(E_ABORT); | 687 EndDownload(E_ABORT); |
688 return true; | 688 return true; |
689 } | 689 } |
690 | 690 |
691 bool BackgroundDownloader::OnStateTransferring() { | 691 bool BackgroundDownloader::OnStateTransferring() { |
692 // Resets the baseline for detecting a stuck job since the job is transferring | 692 // Resets the baseline for detecting a stuck job since the job is transferring |
693 // data and it is making progress. | 693 // data and it is making progress. |
694 job_stuck_begin_time_ = base::TimeTicks::Now(); | 694 job_stuck_begin_time_ = base::TimeTicks::Now(); |
695 | 695 |
696 int64_t downloaded_bytes = -1; | 696 int64_t downloaded_bytes = -1; |
697 int64_t total_bytes = -1; | 697 int64_t total_bytes = -1; |
698 HRESULT hr = GetJobByteCount(job_.get(), &downloaded_bytes, &total_bytes); | 698 HRESULT hr = GetJobByteCount(job_.Get(), &downloaded_bytes, &total_bytes); |
699 if (FAILED(hr)) | 699 if (FAILED(hr)) |
700 return false; | 700 return false; |
701 | 701 |
702 Result result; | 702 Result result; |
703 result.downloaded_bytes = downloaded_bytes; | 703 result.downloaded_bytes = downloaded_bytes; |
704 result.total_bytes = total_bytes; | 704 result.total_bytes = total_bytes; |
705 | 705 |
706 main_task_runner()->PostTask( | 706 main_task_runner()->PostTask( |
707 FROM_HERE, base::Bind(&BackgroundDownloader::OnDownloadProgress, | 707 FROM_HERE, base::Bind(&BackgroundDownloader::OnDownloadProgress, |
708 base::Unretained(this), result)); | 708 base::Unretained(this), result)); |
(...skipping 25 matching lines...) Expand all Loading... |
734 *job = p.Detach(); | 734 *job = p.Detach(); |
735 | 735 |
736 return S_OK; | 736 return S_OK; |
737 } | 737 } |
738 | 738 |
739 HRESULT BackgroundDownloader::CreateOrOpenJob(const GURL& url, | 739 HRESULT BackgroundDownloader::CreateOrOpenJob(const GURL& url, |
740 IBackgroundCopyJob** job) { | 740 IBackgroundCopyJob** job) { |
741 std::vector<ScopedComPtr<IBackgroundCopyJob>> jobs; | 741 std::vector<ScopedComPtr<IBackgroundCopyJob>> jobs; |
742 HRESULT hr = FindBitsJobIf( | 742 HRESULT hr = FindBitsJobIf( |
743 JobFileUrlEqual(base::SysUTF8ToWide(url.spec())), | 743 JobFileUrlEqual(base::SysUTF8ToWide(url.spec())), |
744 bits_manager_.get(), &jobs); | 744 bits_manager_.Get(), &jobs); |
745 if (SUCCEEDED(hr) && !jobs.empty()) { | 745 if (SUCCEEDED(hr) && !jobs.empty()) { |
746 *job = jobs.front().Detach(); | 746 *job = jobs.front().Detach(); |
747 return S_FALSE; | 747 return S_FALSE; |
748 } | 748 } |
749 | 749 |
750 // Use kJobDescription as a temporary job display name until the proper | 750 // Use kJobDescription as a temporary job display name until the proper |
751 // display name is initialized later on. | 751 // display name is initialized later on. |
752 GUID guid = {0}; | 752 GUID guid = {0}; |
753 hr = bits_manager_->CreateJob(kJobDescription, BG_JOB_TYPE_DOWNLOAD, &guid, | 753 hr = bits_manager_->CreateJob(kJobDescription, BG_JOB_TYPE_DOWNLOAD, &guid, |
754 job); | 754 job); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
802 base::TimeDelta::FromMinutes(kJobStuckTimeoutMin)); | 802 base::TimeDelta::FromMinutes(kJobStuckTimeoutMin)); |
803 return job_stuck_begin_time_ + job_stuck_timeout < base::TimeTicks::Now(); | 803 return job_stuck_begin_time_ + job_stuck_timeout < base::TimeTicks::Now(); |
804 } | 804 } |
805 | 805 |
806 HRESULT BackgroundDownloader::CompleteJob() { | 806 HRESULT BackgroundDownloader::CompleteJob() { |
807 HRESULT hr = job_->Complete(); | 807 HRESULT hr = job_->Complete(); |
808 if (FAILED(hr) && hr != BG_S_UNABLE_TO_DELETE_FILES) | 808 if (FAILED(hr) && hr != BG_S_UNABLE_TO_DELETE_FILES) |
809 return hr; | 809 return hr; |
810 | 810 |
811 std::vector<ScopedComPtr<IBackgroundCopyFile>> files; | 811 std::vector<ScopedComPtr<IBackgroundCopyFile>> files; |
812 hr = GetFilesInJob(job_.get(), &files); | 812 hr = GetFilesInJob(job_.Get(), &files); |
813 if (FAILED(hr)) | 813 if (FAILED(hr)) |
814 return hr; | 814 return hr; |
815 | 815 |
816 if (files.empty()) | 816 if (files.empty()) |
817 return E_UNEXPECTED; | 817 return E_UNEXPECTED; |
818 | 818 |
819 base::string16 local_name; | 819 base::string16 local_name; |
820 BG_FILE_PROGRESS progress = {0}; | 820 BG_FILE_PROGRESS progress = {0}; |
821 hr = GetJobFileProperties(files.front().get(), &local_name, NULL, &progress); | 821 hr = GetJobFileProperties(files.front().Get(), &local_name, NULL, &progress); |
822 if (FAILED(hr)) | 822 if (FAILED(hr)) |
823 return hr; | 823 return hr; |
824 | 824 |
825 // Sanity check the post-conditions of a successful download, including | 825 // Sanity check the post-conditions of a successful download, including |
826 // the file and job invariants. The byte counts for a job and its file | 826 // the file and job invariants. The byte counts for a job and its file |
827 // must match as a job only contains one file. | 827 // must match as a job only contains one file. |
828 DCHECK(progress.Completed); | 828 DCHECK(progress.Completed); |
829 DCHECK_EQ(progress.BytesTotal, progress.BytesTransferred); | 829 DCHECK_EQ(progress.BytesTotal, progress.BytesTransferred); |
830 | 830 |
831 response_ = base::FilePath(local_name); | 831 response_ = base::FilePath(local_name); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
875 | 875 |
876 for (auto cookie : cookies) { | 876 for (auto cookie : cookies) { |
877 // TODO(sorin): check the result of the call, see crbug.com/644857. | 877 // TODO(sorin): check the result of the call, see crbug.com/644857. |
878 git->RevokeInterfaceFromGlobal(cookie); | 878 git->RevokeInterfaceFromGlobal(cookie); |
879 } | 879 } |
880 | 880 |
881 return S_OK; | 881 return S_OK; |
882 } | 882 } |
883 | 883 |
884 } // namespace update_client | 884 } // namespace update_client |
OLD | NEW |