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

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

Powered by Google App Engine
This is Rietveld 408576698