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 #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. | |
cpu_(ooo_6.6-7.5)
2013/12/05 19:32:43
I haven't looked at the code yet but are we assumi
Sorin Jianu
2013/12/05 22:34:36
We do, in the sense that we expect different files
| |
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 can be completed next time | |
39 // the code runs, if the file is still needed, otherwise it will be cleaned up | |
40 // on a periodic basis. | |
41 | |
cpu_(ooo_6.6-7.5)
2013/12/05 19:32:43
can you put here the minimalist command line to li
Sorin Jianu
2013/12/05 22:34:36
Done.
| |
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; | |
cpu_(ooo_6.6-7.5)
2013/12/05 19:32:43
These two seem to be good to expose in the configu
Sorin Jianu
2013/12/05 22:34:36
will do.
| |
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 } | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
seems we should have here the logic of JobDescript
Sorin Jianu
2013/12/05 22:34:36
This function takes any predicate and it iterates
cpu_(ooo_6.6-7.5)
2013/12/06 00:12:51
Yes, but all the callers (predicates) filter out t
Sorin Jianu
2013/12/06 01:37:36
Done.
| |
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(×); | |
185 const base::TimeDelta time_delta(base::TimeDelta::FromDays(num_days)); | |
186 const base::Time creation_time(base::Time::FromFileTime(times.CreationTime)); | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
seems incorrect to call 186 regardless of the valu
Sorin Jianu
2013/12/05 22:34:36
Done.
| |
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 { | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
I think this should take a GURL& instead of a stri
Sorin Jianu
2013/12/05 22:34:36
The other predicates take string16 as well, at lea
cpu_(ooo_6.6-7.5)
2013/12/06 00:12:51
It is frowned upon to use url params not being GUR
Sorin Jianu
2013/12/06 01:37:36
Done.
| |
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; | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
is IBackgroundCopyJob2 Vista and above? also who
Sorin Jianu
2013/12/05 22:34:36
yes. At the moment, nobody is calling it, I will h
cpu_(ooo_6.6-7.5)
2013/12/06 00:12:51
Delete it please.
Sorin Jianu
2013/12/06 01:37:36
Done.
| |
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 | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
Is there an option for no credentials?
Sorin Jianu
2013/12/05 22:34:36
We can set proxies for a job. If we do a proxy req
cpu_(ooo_6.6-7.5)
2013/12/06 00:12:51
My only concern is leaking ntlm password hashes if
Sorin Jianu
2013/12/06 01:37:36
Understood. Let's talk about proxy authentication
| |
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; | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
function also seems unused.
Sorin Jianu
2013/12/05 22:34:36
Indeed, I wrote the functions that I will need soo
cpu_(ooo_6.6-7.5)
2013/12/06 00:12:51
delete it please, chrome style is not leave unused
Sorin Jianu
2013/12/06 01:37:36
Done.
| |
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) { | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
another unused function? unless you plan to use it
Sorin Jianu
2013/12/05 22:34:36
Done.
Sorin Jianu
2013/12/06 01:37:36
deleted.
| |
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; | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
todo: add UMA or pings here.
Sorin Jianu
2013/12/05 22:34:36
Done.
| |
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; | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
extra space between typedef and base::
Sorin Jianu
2013/12/05 22:34:36
Done.
| |
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 { | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
indent 324
Sorin Jianu
2013/12/05 22:34:36
Done.
| |
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), | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
can it really be unretained? remind me of the life
Sorin Jianu
2013/12/05 22:34:36
The caller own the head of the chain, who owns the
cpu_(ooo_6.6-7.5)
2013/12/06 00:12:51
So the top dog in the chain does not get deleted u
Sorin Jianu
2013/12/06 01:37:36
The life cycle of the downloader and its successor
| |
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)); | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
dcheck(timer_ is null) ?
Sorin Jianu
2013/12/05 22:34:36
Done.
Nothing breaks in the code if the timer is
cpu_(ooo_6.6-7.5)
2013/12/06 00:12:51
Not per se.
Sorin Jianu
2013/12/06 01:37:36
Done.
| |
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(); | |
415 return; | |
416 | |
417 case BG_JOB_STATE_ACKNOWLEDGED: | |
418 OnStateAcknowledged(); | |
419 return; | |
420 | |
421 // TODO: handle the non-final states, so that the download does not get | |
422 // stuck if BITS is not able to make progress on a given url. | |
423 case BG_JOB_STATE_TRANSIENT_ERROR: | |
424 case BG_JOB_STATE_QUEUED: | |
425 case BG_JOB_STATE_CONNECTING: | |
426 case BG_JOB_STATE_TRANSFERRING: | |
427 case BG_JOB_STATE_SUSPENDED: | |
428 default: | |
429 break; | |
430 } | |
431 | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
can you use timer_->Reset() instead?
Sorin Jianu
2013/12/05 22:34:36
Done. Please notice I removed the Stop call at the
cpu_(ooo_6.6-7.5)
2013/12/06 00:12:51
Cursory look tells me it is ok.
Sorin Jianu
2013/12/06 01:37:36
I put the call the Stop back since I check as a po
| |
432 timer_->Start(FROM_HERE, | |
433 base::TimeDelta::FromSeconds(kJobPollingIntervalSec), | |
434 this, | |
435 &BackgroundDownloader::OnDownloading); | |
436 } | |
437 | |
438 // Completes the BITS download, picks up the file path of the response, and | |
439 // notifies the CrxDownloader. Usually called one time but it could be called | |
440 // multiple times due to the dual polling/event drive mechanism to receive | |
441 // job state changes. | |
442 void BackgroundDownloader::EndDownload(HRESULT error) { | |
443 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
444 DCHECK(!timer_->IsRunning()); | |
445 | |
446 timer_.reset(); | |
447 | |
448 if (is_completed_) | |
449 return; | |
450 | |
451 base::FilePath response; | |
452 HRESULT hr = error; | |
453 if (SUCCEEDED(hr)) { | |
454 std::vector<ScopedComPtr<IBackgroundCopyFile> > files; | |
455 GetFilesInJob(job_, &files); | |
456 DCHECK(files.size() == 1); | |
457 string16 local_name; | |
458 BG_FILE_PROGRESS progress = {0}; | |
459 hr = GetJobFileProperties(files[0], &local_name, NULL, &progress); | |
460 if (SUCCEEDED(hr)) { | |
461 DCHECK(progress.Completed); | |
462 response = base::FilePath(local_name); | |
463 } | |
464 } | |
465 | |
466 // Consider the url handled if it has been successfully downloaded or a | |
467 // 5xx has been received. | |
468 const bool is_handled = SUCCEEDED(hr) || | |
469 IsHttpServerError(GetHttpStatusFromBitsError(error)); | |
470 | |
471 Result result; | |
472 result.error = error; | |
473 result.is_background_download = true; | |
474 result.response = response; | |
475 BrowserThread::PostTask( | |
476 BrowserThread::UI, | |
477 FROM_HERE, | |
478 base::Bind(&BackgroundDownloader::OnDownloadComplete, | |
479 base::Unretained(this), | |
480 is_handled, | |
481 result)); | |
482 | |
483 is_completed_ = true; | |
484 | |
485 CleanupStaleJobs(bits_manager_); | |
486 } | |
487 | |
488 // Called when the BITS job has been transferred successfully. Completes the | |
489 // BITS job by removing it from the BITS queue and making the download | |
490 // available to the caller. | |
491 void BackgroundDownloader::OnStateTransferred() { | |
492 RemoveJobObserver(); | |
493 | |
494 HRESULT hr = job_->Complete(); | |
495 if (SUCCEEDED(hr) || hr == BG_S_UNABLE_TO_DELETE_FILES) | |
496 hr = S_OK; | |
497 else | |
498 hr = job_->Cancel(); | |
499 | |
500 EndDownload(hr); | |
501 } | |
502 | |
503 // Called when the job has encountered an error and no further progress can | |
504 // be made. Cancels this job and remove it from the BITS queue. | |
505 void BackgroundDownloader::OnStateError() { | |
506 RemoveJobObserver(); | |
507 | |
508 ScopedComPtr<IBackgroundCopyError> copy_error; | |
509 HRESULT hr = job_->GetError(copy_error.Receive()); | |
510 if (SUCCEEDED(hr)) { | |
511 BG_ERROR_CONTEXT error_context = BG_ERROR_CONTEXT_NONE; | |
512 HRESULT error_code = E_FAIL; | |
513 hr = copy_error->GetError(&error_context, &error_code); | |
514 if (SUCCEEDED(hr)) { | |
515 EndDownload(error_code); | |
516 return; | |
517 } | |
518 } | |
519 | |
520 hr = job_->Cancel(); | |
521 EndDownload(hr); | |
522 } | |
523 | |
524 // Called when the download was cancelled. Since the observer should have | |
525 // been disconnected by now, this notification must not be seen. | |
526 void BackgroundDownloader::OnStateCancelled() { | |
527 EndDownload(E_UNEXPECTED); | |
528 } | |
529 | |
530 // Called when the download was completed. Same as above. | |
531 void BackgroundDownloader::OnStateAcknowledged() { | |
532 EndDownload(E_UNEXPECTED); | |
533 } | |
534 | |
535 // Creates or opens a job for the given url and queues it up. Tries to | |
536 // install a job observer but continues on if an observer can't be set up. | |
537 HRESULT BackgroundDownloader::QueueBitsJob(const GURL& url) { | |
538 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
539 | |
540 HRESULT hr = S_OK; | |
541 if (bits_manager_ == NULL) { | |
542 hr = GetBitsManager(bits_manager_.Receive()); | |
543 if (FAILED(hr)) | |
544 return hr; | |
545 } | |
546 | |
547 hr = CreateOrOpenJob(url); | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
I rather have CreateOrOpenJob just return the job
Sorin Jianu
2013/12/05 22:34:36
I know what you mean. I tried to do the code that
cpu_(ooo_6.6-7.5)
2013/12/06 00:12:51
I see. I think there is a happy medium point, it c
Sorin Jianu
2013/12/06 01:37:36
Thank you!
| |
548 if (FAILED(hr)) | |
549 return hr; | |
550 | |
551 if (hr == S_OK) { | |
552 hr = InitializeNewJob(url); | |
553 if (FAILED(hr)) | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
here you need to delete the job.
Sorin Jianu
2013/12/05 22:34:36
job_ is refcounted. When the function runs again i
cpu_(ooo_6.6-7.5)
2013/12/06 00:12:51
it seems weird to have job_ point to a valid, yet
Sorin Jianu
2013/12/06 01:37:36
Done. I moved resetting the job pointer @322 where
| |
554 return hr; | |
555 } | |
556 | |
557 InstallJobObserver(); | |
558 | |
559 return job_->Resume(); | |
560 } | |
561 | |
562 HRESULT BackgroundDownloader::CreateOrOpenJob(const GURL& url) { | |
563 std::vector<ScopedComPtr<IBackgroundCopyJob> > jobs; | |
564 HRESULT hr = FindBitsJobIf( | |
565 std::bind2nd(JobFileUrlEqual(), base::SysUTF8ToWide(url.spec())), | |
566 bits_manager_, | |
567 &jobs); | |
568 if (SUCCEEDED(hr) && !jobs.empty()) { | |
569 job_ = jobs.front(); | |
570 return S_FALSE; | |
571 } | |
572 | |
573 GUID guid = {0}; | |
574 ScopedComPtr<IBackgroundCopyJob> job; | |
575 hr = bits_manager_->CreateJob(L"", | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
L"" ?
Sorin Jianu
2013/12/05 22:34:36
I need a wide string literal, is the problem with
cpu_(ooo_6.6-7.5)
2013/12/06 00:12:51
The value, should it be something that helps findi
Sorin Jianu
2013/12/06 01:37:36
Oh, that parameter is the job display name, which
| |
576 BG_JOB_TYPE_DOWNLOAD, | |
577 &guid, | |
578 job.Receive()); | |
579 if (FAILED(hr)) | |
580 return hr; | |
581 | |
582 job_ = job; | |
583 return S_OK; | |
584 } | |
585 | |
586 HRESULT BackgroundDownloader::InitializeNewJob(const GURL& url) { | |
587 const string16 filename(base::SysUTF8ToWide(url.ExtractFileName())); | |
588 | |
589 base::FilePath tempdir; | |
590 if (!file_util::CreateNewTempDirectory( | |
591 FILE_PATH_LITERAL("chrome_component_updates_"), | |
592 &tempdir)) | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
bikeshed purple: "chrome_BITS_"
Sorin Jianu
2013/12/05 22:34:36
Done.
| |
593 return E_FAIL; | |
594 | |
595 HRESULT hr = job_->AddFile( | |
596 base::SysUTF8ToWide(base::StringPiece(url.spec())).c_str(), | |
cpu_(ooo_6.6-7.5)
2013/12/05 21:16:31
you need stringpiece there?
Sorin Jianu
2013/12/05 22:34:36
I don't. Thank you for pointing it out.
I notice
| |
597 tempdir.Append(filename).AsUTF16Unsafe().c_str()); | |
598 if (FAILED(hr)) | |
599 return hr; | |
600 | |
601 hr = job_->SetDisplayName(filename.c_str()); | |
602 if (FAILED(hr)) | |
603 return hr; | |
604 | |
605 hr = job_->SetDescription(kJobDescription); | |
606 if (FAILED(hr)) | |
607 return hr; | |
608 | |
609 hr = job_->SetPriority(BG_JOB_PRIORITY_NORMAL); | |
610 if (FAILED(hr)) | |
611 return hr; | |
612 | |
613 return S_OK; | |
614 } | |
615 | |
616 HRESULT BackgroundDownloader::InstallJobObserver() { | |
617 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
618 | |
619 // Make sure ATL is initialized in this module. | |
620 ui::win::CreateATLModuleIfNeeded(); | |
621 | |
622 CComObject<JobObserver>* job_observer = NULL; | |
623 HRESULT hr(CComObject<JobObserver>::CreateInstance(&job_observer)); | |
624 if (FAILED(hr)) | |
625 return hr; | |
626 | |
627 job_observer->set_callback( | |
628 base::Bind(&BackgroundDownloader::OnDownloading, | |
629 base::Unretained(this))); | |
630 | |
631 job_observer->AddRef(); | |
632 job_observer_.Attach(job_observer); | |
633 | |
634 hr = job_->SetNotifyInterface(job_observer_); | |
635 if (FAILED(hr)) | |
636 return hr; | |
637 | |
638 hr = job_->SetNotifyFlags(BG_NOTIFY_FILE_TRANSFERRED | | |
639 BG_NOTIFY_JOB_TRANSFERRED | | |
640 BG_NOTIFY_JOB_ERROR); | |
641 if (FAILED(hr)) | |
642 return hr; | |
643 | |
644 return S_OK; | |
645 } | |
646 | |
647 HRESULT BackgroundDownloader::RemoveJobObserver() { | |
648 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
649 | |
650 if (!job_ || !job_observer_) | |
651 return S_OK; | |
652 | |
653 HRESULT hr = job_->SetNotifyFlags(0); | |
654 if (FAILED(hr)) | |
655 return hr; | |
656 | |
657 hr = job_->SetNotifyInterface(NULL); | |
658 if (FAILED(hr)) | |
659 return hr; | |
660 | |
661 static_cast<JobObserver*>(job_observer_.get())->set_callback( | |
662 JobObserver::JobChangedCallback()); | |
663 job_observer_ = NULL; | |
664 | |
665 return S_OK; | |
666 } | |
667 | |
668 // Cleans up incompleted jobs that are too old. | |
669 HRESULT BackgroundDownloader::CleanupStaleJobs( | |
670 base::win::ScopedComPtr<IBackgroundCopyManager> bits_manager) { | |
671 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
672 | |
673 if (!bits_manager) | |
674 return E_FAIL; | |
675 | |
676 static base::Time last_sweep; | |
677 | |
678 const base::TimeDelta time_delta(base::TimeDelta::FromDays( | |
679 kPurgeStaleJobsIntervalBetweenChecksDays)); | |
680 const base::Time current_time(base::Time::Now()); | |
681 if (last_sweep + time_delta > current_time) | |
682 return S_OK; | |
683 | |
684 last_sweep = current_time; | |
685 | |
686 std::vector<ScopedComPtr<IBackgroundCopyJob> > jobs; | |
687 HRESULT hr = FindBitsJobIf( | |
688 std::bind2nd(JobCreationOlderThanDays(), kPurgeStaleJobsAfterDays), | |
689 bits_manager, | |
690 &jobs); | |
691 if (FAILED(hr)) | |
692 return hr; | |
693 | |
694 for (size_t i = 0; i != jobs.size(); ++i) { | |
695 CancelJob(jobs[i]); | |
696 } | |
697 | |
698 return S_OK; | |
699 } | |
700 | |
701 } // namespace component_updater | |
702 | |
OLD | NEW |