Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(441)

Side by Side Diff: components/component_updater/background_downloader_win.cc

Issue 808773005: Move most of the component updater artifacts to update_client. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "components/component_updater/background_downloader_win.h"
6
7 #include <atlbase.h>
8 #include <atlcom.h>
9
10 #include <stdint.h>
11 #include <functional>
12 #include <iomanip>
13 #include <limits>
14 #include <vector>
15
16 #include "base/bind.h"
17 #include "base/bind_helpers.h"
18 #include "base/files/file_util.h"
19 #include "base/message_loop/message_loop_proxy.h"
20 #include "base/single_thread_task_runner.h"
21 #include "base/strings/sys_string_conversions.h"
22 #include "base/win/scoped_co_mem.h"
23 #include "components/component_updater/component_updater_utils.h"
24 #include "ui/base/win/atl_module.h"
25 #include "url/gurl.h"
26
27 using base::win::ScopedCoMem;
28 using base::win::ScopedComPtr;
29
30 // The class BackgroundDownloader in this module is an adapter between
31 // the CrxDownloader interface and the BITS service interfaces.
32 // The interface exposed on the CrxDownloader code runs on the main thread,
33 // while the BITS specific code runs on a separate thread passed in by the
34 // client. For every url to download, a BITS job is created, unless there is
35 // already an existing job for that url, in which case, the downloader
36 // connects to it. Once a job is associated with the url, the code looks for
37 // changes in the BITS job state. The checks are triggered by a timer.
38 // The BITS job contains just one file to download. There could only be one
39 // download in progress at a time. If Chrome closes down before the download is
40 // complete, the BITS job remains active and finishes in the background, without
41 // any intervention. The job can be completed next time the code runs, if the
42 // file is still needed, otherwise it will be cleaned up on a periodic basis.
43 //
44 // To list the BITS jobs for a user, use the |bitsadmin| tool. The command line
45 // to do that is: "bitsadmin /list /verbose". Another useful command is
46 // "bitsadmin /info" and provide the job id returned by the previous /list
47 // command.
48 //
49 // Ignoring the suspend/resume issues since this code is not using them, the
50 // job state machine implemented by BITS is something like this:
51 //
52 // Suspended--->Queued--->Connecting---->Transferring--->Transferred
53 // | ^ | | |
54 // | | V V | (complete)
55 // +----------|---------+-----------------+-----+ V
56 // | | | | Acknowledged
57 // | V V |
58 // | Transient Error------->Error |
59 // | | | |(cancel)
60 // | +-------+---------+--->-+
61 // | V |
62 // | (resume) | |
63 // +------<----------+ +---->Cancelled
64 //
65 // The job is created in the "suspended" state. Once |Resume| is called,
66 // BITS queues up the job, then tries to connect, begins transferring the
67 // job bytes, and moves the job to the "transferred state, after the job files
68 // have been transferred. When calling |Complete| for a job, the job files are
69 // made available to the caller, and the job is moved to the "acknowledged"
70 // state.
71 // At any point, the job can be cancelled, in which case, the job is moved
72 // to the "cancelled state" and the job object is removed from the BITS queue.
73 // Along the way, the job can encounter recoverable and non-recoverable errors.
74 // BITS moves the job to "transient error" or "error", depending on which kind
75 // of error has occured.
76 // If the job has reached the "transient error" state, BITS retries the
77 // job after a certain programmable delay. If the job can't be completed in a
78 // certain time interval, BITS stops retrying and errors the job out. This time
79 // interval is also programmable.
80 // If the job is in either of the error states, the job parameters can be
81 // adjusted to handle the error, after which the job can be resumed, and the
82 // whole cycle starts again.
83 // Jobs that are not touched in 90 days (or a value set by group policy) are
84 // automatically disposed off by BITS. This concludes the brief description of
85 // a job lifetime, according to BITS.
86 //
87 // In addition to how BITS is managing the life time of the job, there are a
88 // couple of special cases defined by the BackgroundDownloader.
89 // First, if the job encounters any of the 5xx HTTP responses, the job is
90 // not retried, in order to avoid DDOS-ing the servers.
91 // Second, there is a simple mechanism to detect stuck jobs, and allow the rest
92 // of the code to move on to trying other urls or trying other components.
93 // Last, after completing a job, irrespective of the outcome, the jobs older
94 // than a week are proactively cleaned up.
95
96 namespace component_updater {
97
98 namespace {
99
100 // All jobs created by this module have a specific description so they can
101 // be found at run-time or by using system administration tools.
102 const base::char16 kJobDescription[] = L"Chrome Component Updater";
103
104 // How often the code looks for changes in the BITS job state.
105 const int kJobPollingIntervalSec = 4;
106
107 // How long BITS waits before retrying a job after the job encountered
108 // a transient error. If this value is not set, the BITS default is 10 minutes.
109 const int kMinimumRetryDelayMin = 1;
110
111 // How long to wait for stuck jobs. Stuck jobs could be queued for too long,
112 // have trouble connecting, could be suspended for any reason, or they have
113 // encountered some transient error.
114 const int kJobStuckTimeoutMin = 15;
115
116 // How long BITS waits before giving up on a job that could not be completed
117 // since the job has encountered its first transient error. If this value is
118 // not set, the BITS default is 14 days.
119 const int kSetNoProgressTimeoutDays = 1;
120
121 // How often the jobs which were started but not completed for any reason
122 // are cleaned up. Reasons for jobs to be left behind include browser restarts,
123 // system restarts, etc. Also, the check to purge stale jobs only happens
124 // at most once a day. If the job clean up code is not running, the BITS
125 // default policy is to cancel jobs after 90 days of inactivity.
126 const int kPurgeStaleJobsAfterDays = 7;
127 const int kPurgeStaleJobsIntervalBetweenChecksDays = 1;
128
129 // Returns the status code from a given BITS error.
130 int GetHttpStatusFromBitsError(HRESULT error) {
131 // BITS errors are defined in bitsmsg.h. Although not documented, it is
132 // clear that all errors corresponding to http status code have the high
133 // word equal to 0x8019 and the low word equal to the http status code.
134 const int kHttpStatusFirst = 100; // Continue.
135 const int kHttpStatusLast = 505; // Version not supported.
136 bool is_valid = HIWORD(error) == 0x8019 &&
137 LOWORD(error) >= kHttpStatusFirst &&
138 LOWORD(error) <= kHttpStatusLast;
139 return is_valid ? LOWORD(error) : 0;
140 }
141
142 // Returns the files in a BITS job.
143 HRESULT GetFilesInJob(IBackgroundCopyJob* job,
144 std::vector<ScopedComPtr<IBackgroundCopyFile> >* files) {
145 ScopedComPtr<IEnumBackgroundCopyFiles> enum_files;
146 HRESULT hr = job->EnumFiles(enum_files.Receive());
147 if (FAILED(hr))
148 return hr;
149
150 ULONG num_files = 0;
151 hr = enum_files->GetCount(&num_files);
152 if (FAILED(hr))
153 return hr;
154
155 for (ULONG i = 0; i != num_files; ++i) {
156 ScopedComPtr<IBackgroundCopyFile> file;
157 if (enum_files->Next(1, file.Receive(), NULL) == S_OK && file.get())
158 files->push_back(file);
159 }
160
161 return S_OK;
162 }
163
164 // Returns the file name, the url, and some per-file progress information.
165 // The function out parameters can be NULL if that data is not requested.
166 HRESULT GetJobFileProperties(IBackgroundCopyFile* file,
167 base::string16* local_name,
168 base::string16* remote_name,
169 BG_FILE_PROGRESS* progress) {
170 if (!file)
171 return E_FAIL;
172
173 HRESULT hr = S_OK;
174
175 if (local_name) {
176 ScopedCoMem<base::char16> name;
177 hr = file->GetLocalName(&name);
178 if (FAILED(hr))
179 return hr;
180 local_name->assign(name);
181 }
182
183 if (remote_name) {
184 ScopedCoMem<base::char16> name;
185 hr = file->GetRemoteName(&name);
186 if (FAILED(hr))
187 return hr;
188 remote_name->assign(name);
189 }
190
191 if (progress) {
192 BG_FILE_PROGRESS bg_file_progress = {};
193 hr = file->GetProgress(&bg_file_progress);
194 if (FAILED(hr))
195 return hr;
196 *progress = bg_file_progress;
197 }
198
199 return hr;
200 }
201
202 // Returns the number of bytes downloaded and bytes to download for all files
203 // in the job. If the values are not known or if an error has occurred,
204 // a value of -1 is reported.
205 HRESULT GetJobByteCount(IBackgroundCopyJob* job,
206 int64_t* downloaded_bytes,
207 int64_t* total_bytes) {
208 *downloaded_bytes = -1;
209 *total_bytes = -1;
210
211 if (!job)
212 return E_FAIL;
213
214 BG_JOB_PROGRESS job_progress = {0};
215 HRESULT hr = job->GetProgress(&job_progress);
216 if (FAILED(hr))
217 return hr;
218
219 const uint64_t kMaxNumBytes =
220 static_cast<uint64_t>(std::numeric_limits<int64_t>::max());
221 if (job_progress.BytesTransferred <= kMaxNumBytes)
222 *downloaded_bytes = job_progress.BytesTransferred;
223
224 if (job_progress.BytesTotal <= kMaxNumBytes &&
225 job_progress.BytesTotal != BG_SIZE_UNKNOWN)
226 *total_bytes = job_progress.BytesTotal;
227
228 return S_OK;
229 }
230
231 HRESULT GetJobDescription(IBackgroundCopyJob* job, const base::string16* name) {
232 ScopedCoMem<base::char16> description;
233 return job->GetDescription(&description);
234 }
235
236 // Returns the job error code in |error_code| if the job is in the transient
237 // or the final error state. Otherwise, the job error is not available and
238 // the function fails.
239 HRESULT GetJobError(IBackgroundCopyJob* job, HRESULT* error_code_out) {
240 *error_code_out = S_OK;
241 ScopedComPtr<IBackgroundCopyError> copy_error;
242 HRESULT hr = job->GetError(copy_error.Receive());
243 if (FAILED(hr))
244 return hr;
245
246 BG_ERROR_CONTEXT error_context = BG_ERROR_CONTEXT_NONE;
247 HRESULT error_code = S_OK;
248 hr = copy_error->GetError(&error_context, &error_code);
249 if (FAILED(hr))
250 return hr;
251
252 *error_code_out = FAILED(error_code) ? error_code : E_FAIL;
253 return S_OK;
254 }
255
256 // Finds the component updater jobs matching the given predicate.
257 // Returns S_OK if the function has found at least one job, returns S_FALSE if
258 // no job was found, and it returns an error otherwise.
259 template <class Predicate>
260 HRESULT FindBitsJobIf(Predicate pred,
261 IBackgroundCopyManager* bits_manager,
262 std::vector<ScopedComPtr<IBackgroundCopyJob> >* jobs) {
263 ScopedComPtr<IEnumBackgroundCopyJobs> enum_jobs;
264 HRESULT hr = bits_manager->EnumJobs(0, enum_jobs.Receive());
265 if (FAILED(hr))
266 return hr;
267
268 ULONG job_count = 0;
269 hr = enum_jobs->GetCount(&job_count);
270 if (FAILED(hr))
271 return hr;
272
273 // Iterate over jobs, run the predicate, and select the job only if
274 // the job description matches the component updater jobs.
275 for (ULONG i = 0; i != job_count; ++i) {
276 ScopedComPtr<IBackgroundCopyJob> current_job;
277 if (enum_jobs->Next(1, current_job.Receive(), NULL) == S_OK &&
278 pred(current_job.get())) {
279 base::string16 job_description;
280 hr = GetJobDescription(current_job.get(), &job_description);
281 if (job_description.compare(kJobDescription) == 0)
282 jobs->push_back(current_job);
283 }
284 }
285
286 return jobs->empty() ? S_FALSE : S_OK;
287 }
288
289 // Compares the job creation time and returns true if the job creation time
290 // is older than |num_days|.
291 struct JobCreationOlderThanDays
292 : public std::binary_function<IBackgroundCopyJob*, int, bool> {
293 bool operator()(IBackgroundCopyJob* job, int num_days) const;
294 };
295
296 bool JobCreationOlderThanDays::operator()(IBackgroundCopyJob* job,
297 int num_days) const {
298 BG_JOB_TIMES times = {0};
299 HRESULT hr = job->GetTimes(&times);
300 if (FAILED(hr))
301 return false;
302
303 const base::TimeDelta time_delta(base::TimeDelta::FromDays(num_days));
304 const base::Time creation_time(base::Time::FromFileTime(times.CreationTime));
305
306 return creation_time + time_delta < base::Time::Now();
307 }
308
309 // Compares the url of a file in a job and returns true if the remote name
310 // of any file in a job matches the argument.
311 struct JobFileUrlEqual : public std::binary_function<IBackgroundCopyJob*,
312 const base::string16&,
313 bool> {
314 bool operator()(IBackgroundCopyJob* job,
315 const base::string16& remote_name) const;
316 };
317
318 bool JobFileUrlEqual::operator()(IBackgroundCopyJob* job,
319 const base::string16& remote_name) const {
320 std::vector<ScopedComPtr<IBackgroundCopyFile> > files;
321 HRESULT hr = GetFilesInJob(job, &files);
322 if (FAILED(hr))
323 return false;
324
325 for (size_t i = 0; i != files.size(); ++i) {
326 ScopedCoMem<base::char16> name;
327 if (SUCCEEDED(files[i]->GetRemoteName(&name)) &&
328 remote_name.compare(name) == 0)
329 return true;
330 }
331
332 return false;
333 }
334
335 // Creates an instance of the BITS manager.
336 HRESULT GetBitsManager(IBackgroundCopyManager** bits_manager) {
337 ScopedComPtr<IBackgroundCopyManager> object;
338 HRESULT hr = object.CreateInstance(__uuidof(BackgroundCopyManager));
339 if (FAILED(hr)) {
340 return hr;
341 }
342 *bits_manager = object.Detach();
343 return S_OK;
344 }
345
346 void CleanupJobFiles(IBackgroundCopyJob* job) {
347 std::vector<ScopedComPtr<IBackgroundCopyFile> > files;
348 if (FAILED(GetFilesInJob(job, &files)))
349 return;
350 for (size_t i = 0; i != files.size(); ++i) {
351 base::string16 local_name;
352 HRESULT hr(GetJobFileProperties(files[i].get(), &local_name, NULL, NULL));
353 if (SUCCEEDED(hr))
354 DeleteFileAndEmptyParentDirectory(base::FilePath(local_name));
355 }
356 }
357
358 // Cleans up incompleted jobs that are too old.
359 HRESULT CleanupStaleJobs(
360 base::win::ScopedComPtr<IBackgroundCopyManager> bits_manager) {
361 if (!bits_manager.get())
362 return E_FAIL;
363
364 static base::Time last_sweep;
365
366 const base::TimeDelta time_delta(
367 base::TimeDelta::FromDays(kPurgeStaleJobsIntervalBetweenChecksDays));
368 const base::Time current_time(base::Time::Now());
369 if (last_sweep + time_delta > current_time)
370 return S_OK;
371
372 last_sweep = current_time;
373
374 std::vector<ScopedComPtr<IBackgroundCopyJob> > jobs;
375 HRESULT hr = FindBitsJobIf(
376 std::bind2nd(JobCreationOlderThanDays(), kPurgeStaleJobsAfterDays),
377 bits_manager.get(), &jobs);
378 if (FAILED(hr))
379 return hr;
380
381 for (size_t i = 0; i != jobs.size(); ++i) {
382 jobs[i]->Cancel();
383 CleanupJobFiles(jobs[i].get());
384 }
385
386 return S_OK;
387 }
388
389 } // namespace
390
391 BackgroundDownloader::BackgroundDownloader(
392 scoped_ptr<CrxDownloader> successor,
393 net::URLRequestContextGetter* context_getter,
394 scoped_refptr<base::SingleThreadTaskRunner> task_runner)
395 : CrxDownloader(successor.Pass()),
396 main_task_runner_(base::MessageLoopProxy::current()),
397 context_getter_(context_getter),
398 task_runner_(task_runner),
399 is_completed_(false) {
400 }
401
402 BackgroundDownloader::~BackgroundDownloader() {
403 DCHECK(thread_checker_.CalledOnValidThread());
404
405 // The following objects have thread affinity and can't be destroyed on the
406 // main thread. The resources managed by these objects are acquired at the
407 // beginning of a download and released at the end of the download. Most of
408 // the time, when this destructor is called, these resources have been already
409 // disposed by. Releasing the ownership here is a NOP. However, if the browser
410 // is shutting down while a download is in progress, the timer is active and
411 // the interface pointers are valid. Releasing the ownership means leaking
412 // these objects and their associated resources.
413 timer_.release();
414 bits_manager_.Detach();
415 job_.Detach();
416 }
417
418 void BackgroundDownloader::DoStartDownload(const GURL& url) {
419 DCHECK(thread_checker_.CalledOnValidThread());
420
421 task_runner_->PostTask(
422 FROM_HERE,
423 base::Bind(
424 &BackgroundDownloader::BeginDownload, base::Unretained(this), url));
425 }
426
427 // Called once when this class is asked to do a download. Creates or opens
428 // an existing bits job, hooks up the notifications, and starts the timer.
429 void BackgroundDownloader::BeginDownload(const GURL& url) {
430 DCHECK(task_runner_->RunsTasksOnCurrentThread());
431
432 DCHECK(!timer_);
433
434 is_completed_ = false;
435 download_start_time_ = base::Time::Now();
436 job_stuck_begin_time_ = download_start_time_;
437
438 HRESULT hr = QueueBitsJob(url);
439 if (FAILED(hr)) {
440 EndDownload(hr);
441 return;
442 }
443
444 // A repeating timer retains the user task. This timer can be stopped and
445 // reset multiple times.
446 timer_.reset(new base::RepeatingTimer<BackgroundDownloader>);
447 timer_->Start(FROM_HERE,
448 base::TimeDelta::FromSeconds(kJobPollingIntervalSec),
449 this,
450 &BackgroundDownloader::OnDownloading);
451 }
452
453 // Called any time the timer fires.
454 void BackgroundDownloader::OnDownloading() {
455 DCHECK(task_runner_->RunsTasksOnCurrentThread());
456
457 DCHECK(job_.get());
458
459 DCHECK(!is_completed_);
460 if (is_completed_)
461 return;
462
463 BG_JOB_STATE job_state = BG_JOB_STATE_ERROR;
464 HRESULT hr = job_->GetState(&job_state);
465 if (FAILED(hr)) {
466 EndDownload(hr);
467 return;
468 }
469
470 switch (job_state) {
471 case BG_JOB_STATE_TRANSFERRED:
472 OnStateTransferred();
473 return;
474
475 case BG_JOB_STATE_ERROR:
476 OnStateError();
477 return;
478
479 case BG_JOB_STATE_CANCELLED:
480 OnStateCancelled();
481 return;
482
483 case BG_JOB_STATE_ACKNOWLEDGED:
484 OnStateAcknowledged();
485 return;
486
487 case BG_JOB_STATE_QUEUED:
488 // Fall through.
489 case BG_JOB_STATE_CONNECTING:
490 // Fall through.
491 case BG_JOB_STATE_SUSPENDED:
492 OnStateQueued();
493 break;
494
495 case BG_JOB_STATE_TRANSIENT_ERROR:
496 OnStateTransientError();
497 break;
498
499 case BG_JOB_STATE_TRANSFERRING:
500 OnStateTransferring();
501 break;
502
503 default:
504 break;
505 }
506 }
507
508 // Completes the BITS download, picks up the file path of the response, and
509 // notifies the CrxDownloader. The function should be called only once.
510 void BackgroundDownloader::EndDownload(HRESULT error) {
511 DCHECK(task_runner_->RunsTasksOnCurrentThread());
512
513 DCHECK(!is_completed_);
514 is_completed_ = true;
515
516 timer_.reset();
517
518 const base::Time download_end_time(base::Time::Now());
519 const base::TimeDelta download_time =
520 download_end_time >= download_start_time_
521 ? download_end_time - download_start_time_
522 : base::TimeDelta();
523
524 int64_t downloaded_bytes = -1;
525 int64_t total_bytes = -1;
526 GetJobByteCount(job_.get(), &downloaded_bytes, &total_bytes);
527
528 if (FAILED(error) && job_.get()) {
529 job_->Cancel();
530 CleanupJobFiles(job_.get());
531 }
532
533 job_ = NULL;
534
535 CleanupStaleJobs(bits_manager_);
536 bits_manager_ = NULL;
537
538 // Consider the url handled if it has been successfully downloaded or a
539 // 5xx has been received.
540 const bool is_handled =
541 SUCCEEDED(error) || IsHttpServerError(GetHttpStatusFromBitsError(error));
542
543 const int error_to_report = SUCCEEDED(error) ? 0 : error;
544
545 DownloadMetrics download_metrics;
546 download_metrics.url = url();
547 download_metrics.downloader = DownloadMetrics::kBits;
548 download_metrics.error = error_to_report;
549 download_metrics.downloaded_bytes = downloaded_bytes;
550 download_metrics.total_bytes = total_bytes;
551 download_metrics.download_time_ms = download_time.InMilliseconds();
552
553 Result result;
554 result.error = error_to_report;
555 result.response = response_;
556 result.downloaded_bytes = downloaded_bytes;
557 result.total_bytes = total_bytes;
558 main_task_runner_->PostTask(
559 FROM_HERE,
560 base::Bind(&BackgroundDownloader::OnDownloadComplete,
561 base::Unretained(this),
562 is_handled,
563 result,
564 download_metrics));
565
566 // Once the task is posted to the the main thread, this object may be deleted
567 // by its owner. It is not safe to access members of this object on the
568 // task runner from this point on. The timer is stopped and all BITS
569 // interface pointers have been released.
570 }
571
572 // Called when the BITS job has been transferred successfully. Completes the
573 // BITS job by removing it from the BITS queue and making the download
574 // available to the caller.
575 void BackgroundDownloader::OnStateTransferred() {
576 EndDownload(CompleteJob());
577 }
578
579 // Called when the job has encountered an error and no further progress can
580 // be made. Cancels this job and removes it from the BITS queue.
581 void BackgroundDownloader::OnStateError() {
582 HRESULT error_code = S_OK;
583 HRESULT hr = GetJobError(job_.get(), &error_code);
584 if (FAILED(hr))
585 error_code = hr;
586 DCHECK(FAILED(error_code));
587 EndDownload(error_code);
588 }
589
590 // Called when the job has encountered a transient error, such as a
591 // network disconnect, a server error, or some other recoverable error.
592 void BackgroundDownloader::OnStateTransientError() {
593 // If the job appears to be stuck, handle the transient error as if
594 // it were a final error. This causes the job to be cancelled and a specific
595 // error be returned, if the error was available.
596 if (IsStuck()) {
597 OnStateError();
598 return;
599 }
600
601 // Don't retry at all if the transient error was a 5xx.
602 HRESULT error_code = S_OK;
603 HRESULT hr = GetJobError(job_.get(), &error_code);
604 if (SUCCEEDED(hr) &&
605 IsHttpServerError(GetHttpStatusFromBitsError(error_code))) {
606 OnStateError();
607 return;
608 }
609 }
610
611 void BackgroundDownloader::OnStateQueued() {
612 if (IsStuck())
613 EndDownload(E_ABORT); // Return a generic error for now.
614 }
615
616 void BackgroundDownloader::OnStateTransferring() {
617 // Resets the baseline for detecting a stuck job since the job is transferring
618 // data and it is making progress.
619 job_stuck_begin_time_ = base::Time::Now();
620
621 int64_t downloaded_bytes = -1;
622 int64_t total_bytes = -1;
623 HRESULT hr = GetJobByteCount(job_.get(), &downloaded_bytes, &total_bytes);
624 if (FAILED(hr))
625 return;
626
627 Result result;
628 result.downloaded_bytes = downloaded_bytes;
629 result.total_bytes = total_bytes;
630
631 main_task_runner_->PostTask(
632 FROM_HERE,
633 base::Bind(&BackgroundDownloader::OnDownloadProgress,
634 base::Unretained(this),
635 result));
636 }
637
638 // Called when the download was cancelled. Since the observer should have
639 // been disconnected by now, this notification must not be seen.
640 void BackgroundDownloader::OnStateCancelled() {
641 EndDownload(E_UNEXPECTED);
642 }
643
644 // Called when the download was completed. Same as above.
645 void BackgroundDownloader::OnStateAcknowledged() {
646 EndDownload(E_UNEXPECTED);
647 }
648
649 // Creates or opens a job for the given url and queues it up. Tries to
650 // install a job observer but continues on if an observer can't be set up.
651 HRESULT BackgroundDownloader::QueueBitsJob(const GURL& url) {
652 DCHECK(task_runner_->RunsTasksOnCurrentThread());
653
654 HRESULT hr = S_OK;
655 if (bits_manager_.get() == NULL) {
656 hr = GetBitsManager(bits_manager_.Receive());
657 if (FAILED(hr))
658 return hr;
659 }
660
661 hr = CreateOrOpenJob(url);
662 if (FAILED(hr))
663 return hr;
664
665 if (hr == S_OK) {
666 hr = InitializeNewJob(url);
667 if (FAILED(hr))
668 return hr;
669 }
670
671 return job_->Resume();
672 }
673
674 HRESULT BackgroundDownloader::CreateOrOpenJob(const GURL& url) {
675 std::vector<ScopedComPtr<IBackgroundCopyJob> > jobs;
676 HRESULT hr = FindBitsJobIf(
677 std::bind2nd(JobFileUrlEqual(), base::SysUTF8ToWide(url.spec())),
678 bits_manager_.get(), &jobs);
679 if (SUCCEEDED(hr) && !jobs.empty()) {
680 job_ = jobs.front();
681 return S_FALSE;
682 }
683
684 // Use kJobDescription as a temporary job display name until the proper
685 // display name is initialized later on.
686 GUID guid = {0};
687 ScopedComPtr<IBackgroundCopyJob> job;
688 hr = bits_manager_->CreateJob(
689 kJobDescription, BG_JOB_TYPE_DOWNLOAD, &guid, job.Receive());
690 if (FAILED(hr))
691 return hr;
692
693 job_ = job;
694 return S_OK;
695 }
696
697 HRESULT BackgroundDownloader::InitializeNewJob(const GURL& url) {
698 const base::string16 filename(base::SysUTF8ToWide(url.ExtractFileName()));
699
700 base::FilePath tempdir;
701 if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_BITS_"),
702 &tempdir))
703 return E_FAIL;
704
705 HRESULT hr = job_->AddFile(base::SysUTF8ToWide(url.spec()).c_str(),
706 tempdir.Append(filename).AsUTF16Unsafe().c_str());
707 if (FAILED(hr))
708 return hr;
709
710 hr = job_->SetDisplayName(filename.c_str());
711 if (FAILED(hr))
712 return hr;
713
714 hr = job_->SetDescription(kJobDescription);
715 if (FAILED(hr))
716 return hr;
717
718 hr = job_->SetPriority(BG_JOB_PRIORITY_NORMAL);
719 if (FAILED(hr))
720 return hr;
721
722 hr = job_->SetMinimumRetryDelay(60 * kMinimumRetryDelayMin);
723 if (FAILED(hr))
724 return hr;
725
726 const int kSecondsDay = 60 * 60 * 24;
727 hr = job_->SetNoProgressTimeout(kSecondsDay * kSetNoProgressTimeoutDays);
728 if (FAILED(hr))
729 return hr;
730
731 return S_OK;
732 }
733
734 bool BackgroundDownloader::IsStuck() {
735 const base::TimeDelta job_stuck_timeout(
736 base::TimeDelta::FromMinutes(kJobStuckTimeoutMin));
737 return job_stuck_begin_time_ + job_stuck_timeout < base::Time::Now();
738 }
739
740 HRESULT BackgroundDownloader::CompleteJob() {
741 HRESULT hr = job_->Complete();
742 if (FAILED(hr) && hr != BG_S_UNABLE_TO_DELETE_FILES)
743 return hr;
744
745 std::vector<ScopedComPtr<IBackgroundCopyFile> > files;
746 hr = GetFilesInJob(job_.get(), &files);
747 if (FAILED(hr))
748 return hr;
749
750 if (files.empty())
751 return E_UNEXPECTED;
752
753 base::string16 local_name;
754 BG_FILE_PROGRESS progress = {0};
755 hr = GetJobFileProperties(files.front().get(), &local_name, NULL, &progress);
756 if (FAILED(hr))
757 return hr;
758
759 // Sanity check the post-conditions of a successful download, including
760 // the file and job invariants. The byte counts for a job and its file
761 // must match as a job only contains one file.
762 DCHECK(progress.Completed);
763 DCHECK_EQ(progress.BytesTotal, progress.BytesTransferred);
764
765 response_ = base::FilePath(local_name);
766
767 return S_OK;
768 }
769
770 } // namespace component_updater
OLDNEW
« no previous file with comments | « components/component_updater/background_downloader_win.h ('k') | components/component_updater/component_patcher.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698