OLD | NEW |
(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(×); |
| 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 // Creates an instance of the BITS manager. |
| 242 HRESULT GetBitsManager(IBackgroundCopyManager** bits_manager) { |
| 243 ScopedComPtr<IBackgroundCopyManager> object; |
| 244 HRESULT hr = object.CreateInstance(__uuidof(BackgroundCopyManager)); |
| 245 if (FAILED(hr)) { |
| 246 VLOG(1) << "Failed to instantiate BITS." << std::hex << hr; |
| 247 // TODO: add UMA pings. |
| 248 return hr; |
| 249 } |
| 250 *bits_manager = object.Detach(); |
| 251 return S_OK; |
| 252 } |
| 253 |
| 254 // JobObserver receives notifications when a BITS job has been completed, |
| 255 // modified, or has encountered an error. This class lives on the FILE thread. |
| 256 class JobObserver |
| 257 : public CComObjectRootEx<CComSingleThreadModel>, |
| 258 public IBackgroundCopyCallback { |
| 259 public: |
| 260 typedef base::Callback<void (void)> JobChangedCallback; |
| 261 |
| 262 JobObserver() {} |
| 263 |
| 264 virtual ~JobObserver() {} |
| 265 |
| 266 void set_callback(const JobChangedCallback& callback) { |
| 267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 268 callback_ = callback; |
| 269 } |
| 270 |
| 271 BEGIN_COM_MAP(JobObserver) |
| 272 COM_INTERFACE_ENTRY(IBackgroundCopyCallback) |
| 273 END_COM_MAP() |
| 274 |
| 275 // IBackgroundCopyCallback methods. |
| 276 STDMETHOD(JobTransferred)(IBackgroundCopyJob* job) OVERRIDE { |
| 277 NotifyJobChanged(); |
| 278 return S_OK; |
| 279 } |
| 280 |
| 281 STDMETHOD(JobError)(IBackgroundCopyJob* job, |
| 282 IBackgroundCopyError* error) OVERRIDE { |
| 283 NotifyJobChanged(); |
| 284 return S_OK; |
| 285 } |
| 286 |
| 287 STDMETHOD(JobModification)(IBackgroundCopyJob* job, |
| 288 DWORD reserved) OVERRIDE { |
| 289 NotifyJobChanged(); |
| 290 return S_OK; |
| 291 } |
| 292 |
| 293 private: |
| 294 void NotifyJobChanged() { |
| 295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 296 if (!callback_.is_null()) |
| 297 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, callback_); |
| 298 } |
| 299 |
| 300 JobChangedCallback callback_; |
| 301 |
| 302 DISALLOW_COPY_AND_ASSIGN(JobObserver); |
| 303 }; |
| 304 |
| 305 } // namespace |
| 306 |
| 307 BackgroundDownloader::BackgroundDownloader( |
| 308 scoped_ptr<CrxDownloader> successor, |
| 309 net::URLRequestContextGetter* context_getter, |
| 310 scoped_refptr<base::SequencedTaskRunner> task_runner, |
| 311 const DownloadCallback& download_callback) |
| 312 : CrxDownloader(successor.Pass(), download_callback), |
| 313 context_getter_(context_getter), |
| 314 task_runner_(task_runner), |
| 315 is_completed_(false) { |
| 316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 317 } |
| 318 |
| 319 BackgroundDownloader::~BackgroundDownloader() { |
| 320 } |
| 321 |
| 322 void BackgroundDownloader::DoStartDownload(const GURL& url) { |
| 323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 324 |
| 325 BrowserThread::PostTask( |
| 326 BrowserThread::FILE, |
| 327 FROM_HERE, |
| 328 base::Bind(&BackgroundDownloader::BeginDownload, |
| 329 base::Unretained(this), |
| 330 url)); |
| 331 } |
| 332 |
| 333 // Called once when this class is asked to do a download. Creates or opens |
| 334 // an existing bits job, hooks up the notifications, and starts the timer. |
| 335 void BackgroundDownloader::BeginDownload(const GURL& url) { |
| 336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 337 |
| 338 DCHECK(!timer_); |
| 339 |
| 340 HRESULT hr = QueueBitsJob(url); |
| 341 if (FAILED(hr)) { |
| 342 if (job_) |
| 343 job_->Cancel(); |
| 344 EndDownload(hr); |
| 345 return; |
| 346 } |
| 347 |
| 348 timer_.reset(new base::OneShotTimer<BackgroundDownloader>); |
| 349 timer_->Start(FROM_HERE, |
| 350 base::TimeDelta::FromSeconds(kJobPollingIntervalSec), |
| 351 this, |
| 352 &BackgroundDownloader::OnDownloading); |
| 353 } |
| 354 |
| 355 // Called any time there is a change in the state of the job or when |
| 356 // the timer fires. |
| 357 void BackgroundDownloader::OnDownloading() { |
| 358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 359 |
| 360 // Stop the timer in case this function was called by BITS event. |
| 361 timer_->Stop(); |
| 362 |
| 363 BG_JOB_STATE job_state = BG_JOB_STATE_ERROR; |
| 364 HRESULT hr = job_->GetState(&job_state); |
| 365 if (FAILED(hr)) { |
| 366 EndDownload(hr); |
| 367 return; |
| 368 } |
| 369 |
| 370 switch (job_state) { |
| 371 case BG_JOB_STATE_TRANSFERRED: |
| 372 OnStateTransferred(); |
| 373 return; |
| 374 |
| 375 case BG_JOB_STATE_ERROR: |
| 376 OnStateError(); |
| 377 return; |
| 378 |
| 379 case BG_JOB_STATE_CANCELLED: |
| 380 OnStateCancelled(); |
| 381 return; |
| 382 |
| 383 case BG_JOB_STATE_ACKNOWLEDGED: |
| 384 OnStateAcknowledged(); |
| 385 return; |
| 386 |
| 387 // TODO: handle the non-final states, so that the download does not get |
| 388 // stuck if BITS is not able to make progress on a given url. |
| 389 case BG_JOB_STATE_TRANSIENT_ERROR: |
| 390 case BG_JOB_STATE_QUEUED: |
| 391 case BG_JOB_STATE_CONNECTING: |
| 392 case BG_JOB_STATE_TRANSFERRING: |
| 393 case BG_JOB_STATE_SUSPENDED: |
| 394 default: |
| 395 break; |
| 396 } |
| 397 |
| 398 timer_->Reset(); |
| 399 } |
| 400 |
| 401 // Completes the BITS download, picks up the file path of the response, and |
| 402 // notifies the CrxDownloader. Usually called one time but it could be called |
| 403 // multiple times due to the dual polling/event drive mechanism to receive |
| 404 // job state changes. |
| 405 void BackgroundDownloader::EndDownload(HRESULT error) { |
| 406 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 407 DCHECK(!timer_->IsRunning()); |
| 408 |
| 409 timer_.reset(); |
| 410 |
| 411 if (is_completed_) |
| 412 return; |
| 413 |
| 414 base::FilePath response; |
| 415 HRESULT hr = error; |
| 416 if (SUCCEEDED(hr)) { |
| 417 std::vector<ScopedComPtr<IBackgroundCopyFile> > files; |
| 418 GetFilesInJob(job_, &files); |
| 419 DCHECK(files.size() == 1); |
| 420 string16 local_name; |
| 421 BG_FILE_PROGRESS progress = {0}; |
| 422 hr = GetJobFileProperties(files[0], &local_name, NULL, &progress); |
| 423 if (SUCCEEDED(hr)) { |
| 424 DCHECK(progress.Completed); |
| 425 response = base::FilePath(local_name); |
| 426 } |
| 427 } |
| 428 |
| 429 // Consider the url handled if it has been successfully downloaded or a |
| 430 // 5xx has been received. |
| 431 const bool is_handled = SUCCEEDED(hr) || |
| 432 IsHttpServerError(GetHttpStatusFromBitsError(error)); |
| 433 |
| 434 Result result; |
| 435 result.error = error; |
| 436 result.is_background_download = true; |
| 437 result.response = response; |
| 438 BrowserThread::PostTask( |
| 439 BrowserThread::UI, |
| 440 FROM_HERE, |
| 441 base::Bind(&BackgroundDownloader::OnDownloadComplete, |
| 442 base::Unretained(this), |
| 443 is_handled, |
| 444 result)); |
| 445 |
| 446 is_completed_ = true; |
| 447 |
| 448 CleanupStaleJobs(bits_manager_); |
| 449 } |
| 450 |
| 451 // Called when the BITS job has been transferred successfully. Completes the |
| 452 // BITS job by removing it from the BITS queue and making the download |
| 453 // available to the caller. |
| 454 void BackgroundDownloader::OnStateTransferred() { |
| 455 RemoveJobObserver(); |
| 456 |
| 457 HRESULT hr = job_->Complete(); |
| 458 if (SUCCEEDED(hr) || hr == BG_S_UNABLE_TO_DELETE_FILES) |
| 459 hr = S_OK; |
| 460 else |
| 461 hr = job_->Cancel(); |
| 462 |
| 463 EndDownload(hr); |
| 464 } |
| 465 |
| 466 // Called when the job has encountered an error and no further progress can |
| 467 // be made. Cancels this job and remove it from the BITS queue. |
| 468 void BackgroundDownloader::OnStateError() { |
| 469 RemoveJobObserver(); |
| 470 |
| 471 ScopedComPtr<IBackgroundCopyError> copy_error; |
| 472 HRESULT hr = job_->GetError(copy_error.Receive()); |
| 473 if (SUCCEEDED(hr)) { |
| 474 BG_ERROR_CONTEXT error_context = BG_ERROR_CONTEXT_NONE; |
| 475 HRESULT error_code = E_FAIL; |
| 476 hr = copy_error->GetError(&error_context, &error_code); |
| 477 if (SUCCEEDED(hr)) { |
| 478 EndDownload(error_code); |
| 479 return; |
| 480 } |
| 481 } |
| 482 |
| 483 hr = job_->Cancel(); |
| 484 EndDownload(hr); |
| 485 } |
| 486 |
| 487 // Called when the download was cancelled. Since the observer should have |
| 488 // been disconnected by now, this notification must not be seen. |
| 489 void BackgroundDownloader::OnStateCancelled() { |
| 490 EndDownload(E_UNEXPECTED); |
| 491 } |
| 492 |
| 493 // Called when the download was completed. Same as above. |
| 494 void BackgroundDownloader::OnStateAcknowledged() { |
| 495 EndDownload(E_UNEXPECTED); |
| 496 } |
| 497 |
| 498 // Creates or opens a job for the given url and queues it up. Tries to |
| 499 // install a job observer but continues on if an observer can't be set up. |
| 500 HRESULT BackgroundDownloader::QueueBitsJob(const GURL& url) { |
| 501 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 502 |
| 503 HRESULT hr = S_OK; |
| 504 if (bits_manager_ == NULL) { |
| 505 hr = GetBitsManager(bits_manager_.Receive()); |
| 506 if (FAILED(hr)) |
| 507 return hr; |
| 508 } |
| 509 |
| 510 hr = CreateOrOpenJob(url); |
| 511 if (FAILED(hr)) |
| 512 return hr; |
| 513 |
| 514 if (hr == S_OK) { |
| 515 hr = InitializeNewJob(url); |
| 516 if (FAILED(hr)) |
| 517 return hr; |
| 518 } |
| 519 |
| 520 InstallJobObserver(); |
| 521 |
| 522 return job_->Resume(); |
| 523 } |
| 524 |
| 525 HRESULT BackgroundDownloader::CreateOrOpenJob(const GURL& url) { |
| 526 std::vector<ScopedComPtr<IBackgroundCopyJob> > jobs; |
| 527 HRESULT hr = FindBitsJobIf( |
| 528 std::bind2nd(JobFileUrlEqual(), base::SysUTF8ToWide(url.spec())), |
| 529 bits_manager_, |
| 530 &jobs); |
| 531 if (SUCCEEDED(hr) && !jobs.empty()) { |
| 532 job_ = jobs.front(); |
| 533 return S_FALSE; |
| 534 } |
| 535 |
| 536 GUID guid = {0}; |
| 537 ScopedComPtr<IBackgroundCopyJob> job; |
| 538 hr = bits_manager_->CreateJob(L"", |
| 539 BG_JOB_TYPE_DOWNLOAD, |
| 540 &guid, |
| 541 job.Receive()); |
| 542 if (FAILED(hr)) |
| 543 return hr; |
| 544 |
| 545 job_ = job; |
| 546 return S_OK; |
| 547 } |
| 548 |
| 549 HRESULT BackgroundDownloader::InitializeNewJob(const GURL& url) { |
| 550 const string16 filename(base::SysUTF8ToWide(url.ExtractFileName())); |
| 551 |
| 552 base::FilePath tempdir; |
| 553 if (!base::CreateNewTempDirectory( |
| 554 FILE_PATH_LITERAL("chrome_BITS_"), |
| 555 &tempdir)) |
| 556 return E_FAIL; |
| 557 |
| 558 HRESULT hr = job_->AddFile( |
| 559 base::SysUTF8ToWide(url.spec()).c_str(), |
| 560 tempdir.Append(filename).AsUTF16Unsafe().c_str()); |
| 561 if (FAILED(hr)) |
| 562 return hr; |
| 563 |
| 564 hr = job_->SetDisplayName(filename.c_str()); |
| 565 if (FAILED(hr)) |
| 566 return hr; |
| 567 |
| 568 hr = job_->SetDescription(kJobDescription); |
| 569 if (FAILED(hr)) |
| 570 return hr; |
| 571 |
| 572 hr = job_->SetPriority(BG_JOB_PRIORITY_NORMAL); |
| 573 if (FAILED(hr)) |
| 574 return hr; |
| 575 |
| 576 return S_OK; |
| 577 } |
| 578 |
| 579 HRESULT BackgroundDownloader::InstallJobObserver() { |
| 580 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 581 |
| 582 // Make sure ATL is initialized in this module. |
| 583 ui::win::CreateATLModuleIfNeeded(); |
| 584 |
| 585 CComObject<JobObserver>* job_observer = NULL; |
| 586 HRESULT hr(CComObject<JobObserver>::CreateInstance(&job_observer)); |
| 587 if (FAILED(hr)) |
| 588 return hr; |
| 589 |
| 590 job_observer->set_callback( |
| 591 base::Bind(&BackgroundDownloader::OnDownloading, |
| 592 base::Unretained(this))); |
| 593 |
| 594 job_observer->AddRef(); |
| 595 job_observer_.Attach(job_observer); |
| 596 |
| 597 hr = job_->SetNotifyInterface(job_observer_); |
| 598 if (FAILED(hr)) |
| 599 return hr; |
| 600 |
| 601 hr = job_->SetNotifyFlags(BG_NOTIFY_FILE_TRANSFERRED | |
| 602 BG_NOTIFY_JOB_TRANSFERRED | |
| 603 BG_NOTIFY_JOB_ERROR); |
| 604 if (FAILED(hr)) |
| 605 return hr; |
| 606 |
| 607 return S_OK; |
| 608 } |
| 609 |
| 610 HRESULT BackgroundDownloader::RemoveJobObserver() { |
| 611 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 612 |
| 613 if (!job_ || !job_observer_) |
| 614 return S_OK; |
| 615 |
| 616 HRESULT hr = job_->SetNotifyFlags(0); |
| 617 if (FAILED(hr)) |
| 618 return hr; |
| 619 |
| 620 hr = job_->SetNotifyInterface(NULL); |
| 621 if (FAILED(hr)) |
| 622 return hr; |
| 623 |
| 624 static_cast<JobObserver*>(job_observer_.get())->set_callback( |
| 625 JobObserver::JobChangedCallback()); |
| 626 job_observer_ = NULL; |
| 627 |
| 628 return S_OK; |
| 629 } |
| 630 |
| 631 // Cleans up incompleted jobs that are too old. |
| 632 HRESULT BackgroundDownloader::CleanupStaleJobs( |
| 633 base::win::ScopedComPtr<IBackgroundCopyManager> bits_manager) { |
| 634 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 635 |
| 636 if (!bits_manager) |
| 637 return E_FAIL; |
| 638 |
| 639 static base::Time last_sweep; |
| 640 |
| 641 const base::TimeDelta time_delta(base::TimeDelta::FromDays( |
| 642 kPurgeStaleJobsIntervalBetweenChecksDays)); |
| 643 const base::Time current_time(base::Time::Now()); |
| 644 if (last_sweep + time_delta > current_time) |
| 645 return S_OK; |
| 646 |
| 647 last_sweep = current_time; |
| 648 |
| 649 std::vector<ScopedComPtr<IBackgroundCopyJob> > jobs; |
| 650 HRESULT hr = FindBitsJobIf( |
| 651 std::bind2nd(JobCreationOlderThanDays(), kPurgeStaleJobsAfterDays), |
| 652 bits_manager, |
| 653 &jobs); |
| 654 if (FAILED(hr)) |
| 655 return hr; |
| 656 |
| 657 for (size_t i = 0; i != jobs.size(); ++i) { |
| 658 jobs[i]->Cancel(); |
| 659 } |
| 660 |
| 661 return S_OK; |
| 662 } |
| 663 |
| 664 } // namespace component_updater |
| 665 |
OLD | NEW |