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