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

Side by Side Diff: chrome/browser/component_updater/background_downloader_win.cc

Issue 105853002: Implement a background downloader using BITS in Windows Chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years 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 | Annotate | Revision Log
OLDNEW
(Empty)
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 #include <atlbase.h>
7 #include <atlcom.h>
8 #include <functional>
9 #include <iomanip>
10 #include <vector>
11 #include "base/file_util.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/time/time.h"
14 #include "base/win/scoped_co_mem.h"
15 #include "chrome/browser/component_updater/component_updater_utils.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "ui/base/win/atl_module.h"
18 #include "url/gurl.h"
19
20 using base::win::ScopedCoMem;
21 using base::win::ScopedComPtr;
22 using content::BrowserThread;
23
24 // The class BackgroundDownloader in this module is an adapter between
25 // the CrxDownloader interface and the BITS service interfaces.
26 // The interface exposed on the CrxDownloader code runs on the UI thread, while
27 // the BITS specific code runs in a single threaded apartment on the FILE
28 // thread.
29 // For every url to download, a BITS job is created, unless there is already
30 // an existing job for that url, in which case, the downloader connects to it.
31 // Once a job is associated with the url, the code looks for changes in the
32 // BITS job state.
33 // The changes arrive either as COM callbacks (due to the threading model, the
34 // events always arrive on the FILE thread) or by polling, triggered by a timer,
35 // as a last resort. The BITS job contains just one file to download. There
36 // could only be one download in progress at a time. If Chrome closes down
37 // before the download is complete, the BITS job remains active and finishes in
38 // the background, without any intervention. The job ban be completed next time
waffles 2013/12/05 01:15:07 ban -> can
Sorin Jianu 2013/12/05 01:43:07 Done.
39 // the code runs, if the file is still needed, otherwise it will be cleaned up
40 // on a periodic basis.
41
42 namespace component_updater {
43
44 namespace {
45
46 // All jobs created by this module have a specific description so they can
47 // be found at run-time or by using system administration tools.
48 const char16 kJobDescription[] = L"Chrome Component Updater";
49
50 // How often the code looks for changes in the BITS job state.
51 const int kJobPollingIntervalSec = 10;
52
53 // How often the jobs which were started but not completed for any reason
54 // are cleaned up. Reasons for jobs to be left behind include browser restarts,
55 // system restarts, etc. Also, the check to purge stale jobs only happens
56 // at most once a day.
57 const int kPurgeStaleJobsAfterDays = 7;
58 const int kPurgeStaleJobsIntervalBetweenChecksDays = 1;
59
60 // Returns the status code from a given BITS error.
61 int GetHttpStatusFromBitsError(HRESULT error) {
62 // BITS errors are defined in bitsmsg.h. Although not documented, it is
63 // clear that all errors corresponding to http status code have the high
64 // word equal to 0x8019 and the low word equal to the http status code.
65 const int kHttpStatusFirst = 100; // Continue.
66 const int kHttpStatusLast = 505; // Version not supported.
67 bool is_valid = HIWORD(error) == 0x8019 &&
68 LOWORD(error) >= kHttpStatusFirst &&
69 LOWORD(error) <= kHttpStatusLast;
70 return is_valid ? LOWORD(error) : 0;
71 }
72
73 // Returns the files in a BITS job.
74 HRESULT GetFilesInJob(IBackgroundCopyJob* job,
75 std::vector<ScopedComPtr<IBackgroundCopyFile> >* files) {
76 ScopedComPtr<IEnumBackgroundCopyFiles> enum_files;
77 HRESULT hr = job->EnumFiles(enum_files.Receive());
78 if (FAILED(hr))
79 return hr;
80
81 ULONG num_files = 0;
82 hr = enum_files->GetCount(&num_files);
83 if (FAILED(hr))
84 return hr;
85
86 for (ULONG i = 0; i != num_files; ++i) {
87 ScopedComPtr<IBackgroundCopyFile> file;
88 if (enum_files->Next(1, file.Receive(), NULL) == S_OK)
89 files->push_back(file);
90 }
91
92 return S_OK;
93 }
94
95 // Returns the file name, the url, and some per-file progress information.
96 // The function out parameters can be NULL if that data is not requested.
97 HRESULT GetJobFileProperties(IBackgroundCopyFile* file,
98 string16* local_name,
99 string16* remote_name,
100 BG_FILE_PROGRESS* progress) {
101 HRESULT hr = S_OK;
102
103 if (local_name) {
104 ScopedCoMem<char16> name;
105 hr = file->GetLocalName(&name);
106 if (FAILED(hr))
107 return hr;
108 local_name->assign(name);
109 }
110
111 if (remote_name) {
112 ScopedCoMem<char16> name;
113 hr = file->GetRemoteName(&name);
114 if (FAILED(hr))
115 return hr;
116 remote_name->assign(name);
117 }
118
119 if (progress) {
120 BG_FILE_PROGRESS bg_file_progress = {};
121 hr = file->GetProgress(&bg_file_progress);
122 if (FAILED(hr))
123 return hr;
124 *progress = bg_file_progress;
125 }
126
127 return hr;
128 }
129
130 // Finds the jobs matching the given predicate.
131 // Returns S_OK if the function has found at least one job, returns S_FALSE if
132 // no job was found, and it returns an error otherwise.
133 template<class Predicate>
134 HRESULT FindBitsJobIf(Predicate pred,
135 IBackgroundCopyManager* bits_manager,
136 std::vector<ScopedComPtr<IBackgroundCopyJob> >* jobs) {
137 ScopedComPtr<IEnumBackgroundCopyJobs> enum_jobs;
138 HRESULT hr = bits_manager->EnumJobs(0, enum_jobs.Receive());
139 if (FAILED(hr))
140 return hr;
141
142 ULONG job_count = 0;
143 hr = enum_jobs->GetCount(&job_count);
144 if (FAILED(hr))
145 return hr;
146
147 for (ULONG i = 0; i != job_count; ++i) {
148 ScopedComPtr<IBackgroundCopyJob> current_job;
149 if (enum_jobs->Next(1, current_job.Receive(), NULL) == S_OK &&
150 pred(current_job)) {
151 jobs->push_back(current_job);
152 }
153 }
154
155 return jobs->empty() ? S_FALSE : S_OK;
156 }
157
158 // Compares the description of a job matches |name|.
159 struct JobDescriptionEqual
160 : public std::binary_function<IBackgroundCopyJob*, const string16&, bool> {
161 bool operator()(IBackgroundCopyJob* job, const string16& name) const;
162 };
163
164 bool JobDescriptionEqual::operator()(IBackgroundCopyJob* job,
165 const string16& name) const {
166 ScopedCoMem<char16> description;
167 HRESULT hr = job->GetDescription(&description);
168 return SUCCEEDED(hr) && name.compare(description) == 0;
169 }
170
171 // Compares the job creation time and returns true if the job creation time
172 // is older than |num_days|.
173 struct JobCreationOlderThanDays
174 : public std::binary_function<IBackgroundCopyJob*, int, bool> {
175 bool operator()(IBackgroundCopyJob* job, int num_days) const;
176 };
177
178 bool JobCreationOlderThanDays::operator()(IBackgroundCopyJob* job,
179 int num_days) const {
180 if (!JobDescriptionEqual()(job, kJobDescription))
181 return false;
182
183 BG_JOB_TIMES times = {0};
184 HRESULT hr = job->GetTimes(&times);
185 const base::TimeDelta time_delta(base::TimeDelta::FromDays(num_days));
186 const base::Time creation_time(base::Time::FromFileTime(times.CreationTime));
187 return SUCCEEDED(hr) && (creation_time + time_delta < base::Time::Now());
188 }
189
190 // Compares the url of a file in a job and returns true if the remote name
191 // of any file in a job matches |url|.
192 struct JobFileUrlEqual
193 : public std::binary_function<IBackgroundCopyJob*, const string16&, bool> {
194 bool operator()(IBackgroundCopyJob* job, const string16& url) const;
195 };
196
197 bool JobFileUrlEqual::operator()(IBackgroundCopyJob* job,
198 const string16& url) const {
199 if (!JobDescriptionEqual()(job, kJobDescription))
200 return false;
201
202 std::vector<ScopedComPtr<IBackgroundCopyFile> > files;
203 HRESULT hr = GetFilesInJob(job, &files);
204 if (FAILED(hr))
205 return false;
206
207 for (size_t i = 0; i != files.size(); ++i) {
208 ScopedCoMem<char16> name;
209 if (SUCCEEDED(files[i]->GetRemoteName(&name)) && url.compare(name) == 0)
210 return true;
211 }
212
213 return false;
214 }
215
216 // Sets the proxy authentication credentials for the job.
217 HRESULT SetProxyAuthCredentials(IBackgroundCopyJob* job) {
218 ScopedComPtr<IBackgroundCopyJob2> job2;
219 HRESULT hr = job2.QueryFrom(job);
220 if (FAILED(hr))
221 return hr;
222
223 BG_AUTH_CREDENTIALS auth_cred = {};
224 auth_cred.Target = BG_AUTH_TARGET_PROXY;
225 auth_cred.Scheme = BG_AUTH_SCHEME_NEGOTIATE;
226
227 return job2->SetCredentials(&auth_cred);
228 }
229
230 HRESULT CancelJob(IBackgroundCopyJob* job) {
231 BG_JOB_STATE job_state = BG_JOB_STATE_ERROR;
232 HRESULT hr = job->GetState(&job_state);
233 if (FAILED(hr))
234 return hr;
235
236 if (job_state != BG_JOB_STATE_CANCELLED &&
237 job_state != BG_JOB_STATE_ACKNOWLEDGED) {
238 hr = job->Cancel();
239 if (FAILED(hr))
240 return hr;
241 }
242
243 return hr;
244 }
245
246 HRESULT PauseJob(IBackgroundCopyJob* job) {
247 BG_JOB_STATE job_state = BG_JOB_STATE_ERROR;
248 HRESULT hr = job->GetState(&job_state);
249 if (FAILED(hr))
250 return hr;
251
252 if (job_state != BG_JOB_STATE_TRANSFERRED &&
253 job_state != BG_JOB_STATE_ACKNOWLEDGED &&
254 job_state != BG_JOB_STATE_CANCELLED) {
255 hr = job->Suspend();
256 if (FAILED(hr))
257 return hr;
258 }
259
260 return hr;
261 }
262
263 HRESULT ResumeJob(IBackgroundCopyJob* job) {
264 BG_JOB_STATE job_state = BG_JOB_STATE_ERROR;
265 HRESULT hr = job->GetState(&job_state);
266 if (FAILED(hr))
267 return hr;
268
269 if (job_state != BG_JOB_STATE_SUSPENDED) {
270 hr = job->Resume();
271 if (FAILED(hr))
272 return hr;
273 }
274
275 return hr;
276 }
277
278 // Creates an instance of the BITS manager.
279 HRESULT GetBitsManager(IBackgroundCopyManager** bits_manager) {
280 ScopedComPtr<IBackgroundCopyManager> object;
281 HRESULT hr = object.CreateInstance(__uuidof(BackgroundCopyManager));
282 if (FAILED(hr)) {
283 VLOG(1) << "Failed to instantiate BITS." << std::hex << hr;
284 return hr;
285 }
286 *bits_manager = object.Detach();
287 return S_OK;
288 }
289
290 // JobObserver receives notifications when a BITS job has been completed,
291 // modified, or has encountered an error. This class lives on the FILE thread.
292 class JobObserver
293 : public CComObjectRootEx<CComSingleThreadModel>,
294 public IBackgroundCopyCallback {
295 public:
296 typedef base::Callback<void (void)> JobChangedCallback;
297
298 JobObserver() {}
299
300 virtual ~JobObserver() {}
301
302 void set_callback(const JobChangedCallback& callback) {
303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
304 callback_ = callback;
305 }
306
307 BEGIN_COM_MAP(JobObserver)
308 COM_INTERFACE_ENTRY(IBackgroundCopyCallback)
309 END_COM_MAP()
310
311 // IBackgroundCopyCallback methods.
312 STDMETHOD(JobTransferred)(IBackgroundCopyJob* job) OVERRIDE {
313 NotifyJobChanged();
314 return S_OK;
315 }
316
317 STDMETHOD(JobError)(IBackgroundCopyJob* job,
318 IBackgroundCopyError* error) OVERRIDE {
319 NotifyJobChanged();
320 return S_OK;
321 }
322
323 STDMETHOD(JobModification)(IBackgroundCopyJob* job,
324 DWORD reserved) OVERRIDE {
325 NotifyJobChanged();
326 return S_OK;
327 }
328
329 private:
330 void NotifyJobChanged() {
331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
332 if (!callback_.is_null())
333 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, callback_);
334 }
335
336 JobChangedCallback callback_;
337
338 DISALLOW_COPY_AND_ASSIGN(JobObserver);
339 };
340
341 } // namespace
342
343 BackgroundDownloader::BackgroundDownloader(
344 scoped_ptr<CrxDownloader> successor,
345 net::URLRequestContextGetter* context_getter,
346 scoped_refptr<base::SequencedTaskRunner> task_runner,
347 const DownloadCallback& download_callback)
348 : CrxDownloader(successor.Pass(), download_callback),
349 context_getter_(context_getter),
350 task_runner_(task_runner),
351 is_completed_(false) {
352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
353 }
354
355 BackgroundDownloader::~BackgroundDownloader() {
356 }
357
358 void BackgroundDownloader::DoStartDownload(const GURL& url) {
359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
360
361 BrowserThread::PostTask(
362 BrowserThread::FILE,
363 FROM_HERE,
364 base::Bind(&BackgroundDownloader::BeginDownload,
365 base::Unretained(this),
366 url));
367 }
368
369 // Called once when this class is asked to do a download. Creates or opens
370 // an existing bits job, hooks up the notifications, and starts the timer.
371 void BackgroundDownloader::BeginDownload(const GURL& url) {
372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
373
374 HRESULT hr = QueueBitsJob(url);
375 if (FAILED(hr)) {
376 if (job_)
377 CancelJob(job_);
378 EndDownload(hr);
379 return;
380 }
381
382 timer_.reset(new base::OneShotTimer<BackgroundDownloader>);
383 timer_->Start(FROM_HERE,
384 base::TimeDelta::FromSeconds(kJobPollingIntervalSec),
385 this,
386 &BackgroundDownloader::OnDownloading);
387 }
388
389 // Called any time there is a change in the state of the job or when
390 // the timer fires.
391 void BackgroundDownloader::OnDownloading() {
392 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
393
394 // Stop the timer in case this function has run due to a BITS event.
395 timer_->Stop();
396
397 BG_JOB_STATE job_state = BG_JOB_STATE_ERROR;
398 HRESULT hr = job_->GetState(&job_state);
399 if (FAILED(hr)) {
400 EndDownload(hr);
401 return;
402 }
403
404 switch (job_state) {
405 case BG_JOB_STATE_TRANSFERRED:
406 OnStateTransferred();
407 return;
408
409 case BG_JOB_STATE_ERROR:
410 OnStateError();
411 return;
412
413 case BG_JOB_STATE_CANCELLED:
414 OnStateCancelled();
waffles 2013/12/05 01:15:07 We fall through here?
Sorin Jianu 2013/12/05 01:43:07 we should not, thank you! I think this bug was an
415
416 case BG_JOB_STATE_ACKNOWLEDGED:
417 OnStateAcknowledged();
418 return;
419
420 // TODO: handle the non-final states, so that the download does not get
421 // stuck if BITS is not able to make progress on a given url.
422 case BG_JOB_STATE_TRANSIENT_ERROR:
423 case BG_JOB_STATE_QUEUED:
424 case BG_JOB_STATE_CONNECTING:
425 case BG_JOB_STATE_TRANSFERRING:
426 case BG_JOB_STATE_SUSPENDED:
427 default:
428 break;
429 }
430
431 timer_->Start(FROM_HERE,
432 base::TimeDelta::FromSeconds(kJobPollingIntervalSec),
433 this,
434 &BackgroundDownloader::OnDownloading);
435 }
436
437 // Completes the BITS download, picks up the file path of the response, and
438 // notifies the CrxDownloader. Usually called one time but it could be called
439 // multiple times due to the dual polling/event drive mechanism to receive
440 // job state changes.
441 void BackgroundDownloader::EndDownload(HRESULT error) {
442 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
443 DCHECK(!timer_->IsRunning());
444
445 timer_.reset();
446
447 if (is_completed_)
448 return;
449
450 base::FilePath response;
451 HRESULT hr = error;
452 if (SUCCEEDED(hr)) {
453 std::vector<ScopedComPtr<IBackgroundCopyFile> > files;
454 GetFilesInJob(job_, &files);
455 DCHECK(files.size() == 1);
456 string16 local_name;
457 BG_FILE_PROGRESS progress = {0};
458 hr = GetJobFileProperties(files[0], &local_name, NULL, &progress);
459 if (SUCCEEDED(hr)) {
460 DCHECK(progress.Completed);
461 response = base::FilePath(local_name);
462 }
463 }
464
465 // Consider the url handled if it has been successfully downloaded or a
466 // 5xx has been received.
467 const bool is_handled = SUCCEEDED(hr) ||
468 IsHttpServerError(GetHttpStatusFromBitsError(error));
469
470 Result result;
471 result.error = error;
472 result.is_background_download = true;
473 result.response = response;
474 BrowserThread::PostTask(
475 BrowserThread::UI,
476 FROM_HERE,
477 base::Bind(&BackgroundDownloader::OnDownloadComplete,
478 base::Unretained(this),
479 is_handled,
480 result));
481
482 is_completed_ = true;
483
484 CleanupStaleJobs(bits_manager_);
485 }
486
487 // Called when the BITS job has been transferred successfully. Completes the
488 // BITS job by removing it from the BITS queue and making the download
489 // available to the caller.
490 void BackgroundDownloader::OnStateTransferred() {
491 RemoveJobObserver();
492
493 HRESULT hr = job_->Complete();
494 if (SUCCEEDED(hr) || hr == BG_S_UNABLE_TO_DELETE_FILES)
495 hr = S_OK;
496 else
497 hr = job_->Cancel();
498
499 EndDownload(hr);
500 }
501
502 // Called when the job has encountered an error and no further progress can
503 // be made. Cancels this job and remove it from the BITS queue.
504 void BackgroundDownloader::OnStateError() {
505 RemoveJobObserver();
506
507 ScopedComPtr<IBackgroundCopyError> copy_error;
508 HRESULT hr = job_->GetError(copy_error.Receive());
509 if (SUCCEEDED(hr)) {
510 BG_ERROR_CONTEXT error_context = BG_ERROR_CONTEXT_NONE;
511 HRESULT error_code = E_FAIL;
512 hr = copy_error->GetError(&error_context, &error_code);
513 if (SUCCEEDED(hr)) {
514 EndDownload(error_code);
515 return;
516 }
517 }
518
519 hr = job_->Cancel();
520 EndDownload(hr);
521 }
522
523 // Called when the download was cancelled. Since the observer should have
524 // been disconnected by now, this notification must not be seen.
525 void BackgroundDownloader::OnStateCancelled() {
526 EndDownload(E_UNEXPECTED);
527 }
528
529 // Called when the download was completed. Same as above.
530 void BackgroundDownloader::OnStateAcknowledged() {
531 EndDownload(E_UNEXPECTED);
532 }
533
534 // Creates or opens a job for the given url and queues it up. Tries to
535 // install a job observer but continues on if an observer can't be set up.
536 HRESULT BackgroundDownloader::QueueBitsJob(const GURL& url) {
537 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
538
539 HRESULT hr = S_OK;
540 if (bits_manager_ == NULL) {
541 hr = GetBitsManager(bits_manager_.Receive());
542 if (FAILED(hr))
543 return hr;
544 }
545
546 hr = CreateOrOpenJob(url);
547 if (FAILED(hr))
548 return hr;
549
550 if (hr == S_OK) {
551 hr = InitializeNewJob(url);
552 if (FAILED(hr))
553 return hr;
554 }
555
556 InstallJobObserver();
557
558 return job_->Resume();
559 }
560
561 HRESULT BackgroundDownloader::CreateOrOpenJob(const GURL& url) {
562 std::vector<ScopedComPtr<IBackgroundCopyJob> > jobs;
563 HRESULT hr = FindBitsJobIf(
564 std::bind2nd(JobFileUrlEqual(), base::SysUTF8ToWide(url.spec())),
565 bits_manager_,
566 &jobs);
567 if (SUCCEEDED(hr) && !jobs.empty()) {
568 job_ = jobs.front();
569 return S_FALSE;
570 }
571
572 GUID guid = {0};
573 ScopedComPtr<IBackgroundCopyJob> job;
574 hr = bits_manager_->CreateJob(L"",
575 BG_JOB_TYPE_DOWNLOAD,
576 &guid,
577 job.Receive());
578 if (FAILED(hr))
579 return hr;
580
581 job_ = job;
582 return S_OK;
583 }
584
585 HRESULT BackgroundDownloader::InitializeNewJob(const GURL& url) {
586 const string16 filename(base::SysUTF8ToWide(url.ExtractFileName()));
587
588 base::FilePath tempdir;
589 if (!file_util::CreateNewTempDirectory(
590 FILE_PATH_LITERAL("chrome_component_updates_"),
591 &tempdir))
592 return E_FAIL;
593
594 HRESULT hr = job_->AddFile(
595 base::SysUTF8ToWide(base::StringPiece(url.spec())).c_str(),
596 tempdir.Append(filename).AsUTF16Unsafe().c_str());
597 if (FAILED(hr))
598 return hr;
599
600 hr = job_->SetDisplayName(filename.c_str());
601 if (FAILED(hr))
602 return hr;
603
604 hr = job_->SetDescription(kJobDescription);
605 if (FAILED(hr))
606 return hr;
607
608 hr = job_->SetPriority(BG_JOB_PRIORITY_NORMAL);
609 if (FAILED(hr))
610 return hr;
611
612 return S_OK;
613 }
614
615 HRESULT BackgroundDownloader::InstallJobObserver() {
616 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
617
618 // Make sure ATL is initialized in this module.
619 ui::win::CreateATLModuleIfNeeded();
620
621 CComObject<JobObserver>* job_observer = NULL;
622 HRESULT hr(CComObject<JobObserver>::CreateInstance(&job_observer));
623 if (FAILED(hr))
624 return hr;
625
626 job_observer->set_callback(
627 base::Bind(&BackgroundDownloader::OnDownloading,
628 base::Unretained(this)));
629
630 job_observer->AddRef();
631 job_observer_.Attach(job_observer);
632
633 hr = job_->SetNotifyInterface(job_observer_);
634 if (FAILED(hr))
635 return hr;
636
637 hr = job_->SetNotifyFlags(BG_NOTIFY_FILE_TRANSFERRED |
638 BG_NOTIFY_JOB_TRANSFERRED |
639 BG_NOTIFY_JOB_ERROR);
640 if (FAILED(hr))
641 return hr;
642
643 return S_OK;
644 }
645
646 HRESULT BackgroundDownloader::RemoveJobObserver() {
647 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
648
649 if (!job_ || !job_observer_)
650 return S_OK;
651
652 HRESULT hr = job_->SetNotifyFlags(0);
653 if (FAILED(hr))
654 return hr;
655
656 hr = job_->SetNotifyInterface(NULL);
657 if (FAILED(hr))
658 return hr;
659
660 static_cast<JobObserver*>(job_observer_.get())->set_callback(
661 JobObserver::JobChangedCallback());
662 job_observer_ = NULL;
663
664 return S_OK;
665 }
666
667 // Cleans up incompleted jobs that are too old.
668 HRESULT BackgroundDownloader::CleanupStaleJobs(
669 base::win::ScopedComPtr<IBackgroundCopyManager> bits_manager) {
670 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
671
672 if (!bits_manager)
673 return E_FAIL;
674
675 static base::Time last_sweep;
676
677 const base::TimeDelta time_delta(base::TimeDelta::FromDays(
678 kPurgeStaleJobsIntervalBetweenChecksDays));
679 const base::Time current_time(base::Time::Now());
680 if (last_sweep + time_delta > current_time)
681 return S_OK;
682
683 last_sweep = current_time;
684
685 std::vector<ScopedComPtr<IBackgroundCopyJob> > jobs;
686 HRESULT hr = FindBitsJobIf(
687 std::bind2nd(JobCreationOlderThanDays(), kPurgeStaleJobsAfterDays),
688 bits_manager,
689 &jobs);
690 if (FAILED(hr))
691 return hr;
692
693 for (size_t i = 0; i != jobs.size(); ++i) {
694 CancelJob(jobs[i]);
695 }
696
697 return S_OK;
698 }
699
700 } // namespace component_updater
701
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698