OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 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/safe_browsing/incident_reporting/download_metadata_mana ger.h" | |
6 | |
7 #include <list> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/callback.h" | |
11 #include "base/files/file.h" | |
12 #include "base/files/file_util.h" | |
13 #include "base/files/important_file_writer.h" | |
14 #include "base/location.h" | |
15 #include "base/metrics/histogram.h" | |
16 #include "base/sequenced_task_runner.h" | |
17 #include "base/threading/sequenced_worker_pool.h" | |
18 #include "chrome/common/safe_browsing/csd.pb.h" | |
19 #include "content/public/browser/browser_context.h" | |
20 #include "content/public/browser/download_item.h" | |
21 | |
22 namespace safe_browsing { | |
23 | |
24 namespace { | |
25 | |
26 // Histogram bucket values for metadata read operations. Do not reorder. | |
27 enum MetadataReadResult { | |
28 READ_SUCCESS = 0, | |
29 OPEN_FAILURE = 1, | |
30 NOT_FOUND = 2, | |
31 GET_INFO_FAILURE = 3, | |
32 FILE_TOO_BIG = 4, | |
33 READ_FAILURE = 5, | |
34 PARSE_FAILURE = 6, | |
35 MALFORMED_DATA = 7, | |
36 NUM_READ_RESULTS | |
37 }; | |
38 | |
39 // Histogram bucket values for metadata write operations. Do not reorder. | |
40 enum MetadataWriteResult { | |
41 WRITE_SUCCESS = 0, | |
42 SERIALIZATION_FAILURE = 1, | |
43 WRITE_FAILURE = 2, | |
44 NUM_WRITE_RESULTS | |
45 }; | |
46 | |
47 // The name of the metadata file in the profile directory. | |
48 const base::FilePath::CharType kDownloadMetadataBasename[] = | |
49 FILE_PATH_LITERAL("DownloadMetadata"); | |
50 | |
51 // Returns the path to the metadata file for |browser_context|. | |
52 base::FilePath GetMetadataPath(content::BrowserContext* browser_context) { | |
53 return browser_context->GetPath().Append(kDownloadMetadataBasename); | |
54 } | |
55 | |
56 // Returns true if |metadata| appears to be valid. | |
57 bool MetadataIsValid(const DownloadMetadata& metadata) { | |
58 return (metadata.has_download_id() && | |
59 metadata.has_download() && | |
60 metadata.download().has_download() && | |
61 metadata.download().download().has_url()); | |
62 } | |
63 | |
64 // Reads and parses a DownloadMetadata message from |metadata_path| into | |
65 // |metadata|. | |
66 void ReadMetadataOnWorkerPool(const base::FilePath& metadata_path, | |
67 DownloadMetadata* metadata) { | |
68 using base::File; | |
69 DCHECK(metadata); | |
70 MetadataReadResult result = NUM_READ_RESULTS; | |
71 File metadata_file(metadata_path, File::FLAG_OPEN | File::FLAG_READ); | |
72 if (metadata_file.IsValid()) { | |
73 base::File::Info info; | |
74 if (metadata_file.GetInfo(&info)) { | |
75 if (info.size <= INT_MAX) { | |
76 const int size = static_cast<int>(info.size); | |
77 scoped_ptr<char[]> file_data(new char[info.size]); | |
78 if (metadata_file.Read(0, file_data.get(), size)) { | |
79 if (!metadata->ParseFromArray(file_data.get(), size)) | |
80 result = PARSE_FAILURE; | |
81 else if (!MetadataIsValid(*metadata)) | |
82 result = MALFORMED_DATA; | |
83 else | |
84 result = READ_SUCCESS; | |
85 } else { | |
86 result = READ_FAILURE; | |
87 } | |
88 } else { | |
89 result = FILE_TOO_BIG; | |
90 } | |
91 } else { | |
92 result = GET_INFO_FAILURE; | |
93 } | |
94 } else if (metadata_file.error_details() != File::FILE_ERROR_NOT_FOUND) { | |
95 result = OPEN_FAILURE; | |
96 } else { | |
97 result = NOT_FOUND; | |
98 } | |
99 if (result != READ_SUCCESS) | |
100 metadata->Clear(); | |
101 UMA_HISTOGRAM_ENUMERATION( | |
102 "SBIRS.DownloadMetadataReadResult", result, NUM_READ_RESULTS); | |
103 } | |
104 | |
105 // Writes |download_metadata| to |metadata_path|. | |
106 void WriteMetadataOnWorkerPool(const base::FilePath& metadata_path, | |
107 DownloadMetadata* download_metadata) { | |
108 MetadataWriteResult result = NUM_WRITE_RESULTS; | |
109 std::string file_data; | |
110 if (download_metadata->SerializeToString(&file_data)) { | |
111 result = | |
112 base::ImportantFileWriter::WriteFileAtomically(metadata_path, file_data) | |
113 ? WRITE_SUCCESS | |
114 : WRITE_FAILURE; | |
115 } else { | |
116 result = SERIALIZATION_FAILURE; | |
117 } | |
118 UMA_HISTOGRAM_ENUMERATION( | |
119 "SBIRS.DownloadMetadataWriteResult", result, NUM_WRITE_RESULTS); | |
120 } | |
121 | |
122 // Deletes |metadata_path|. | |
123 void DeleteMetadataOnWorkerPool(const base::FilePath& metadata_path) { | |
124 bool success = base::DeleteFile(metadata_path, false /* not recursive */); | |
125 UMA_HISTOGRAM_BOOLEAN("SBIRS.DownloadMetadataDeleteSuccess", success); | |
126 } | |
127 | |
128 // Runs |callback| with the DownloadDetails in |download_metadata|. | |
129 void ReturnResults( | |
130 const DownloadMetadataManager::GetDownloadDetailsCallback& callback, | |
131 scoped_ptr<DownloadMetadata> download_metadata) { | |
132 if (!download_metadata->has_download_id()) | |
133 callback.Run(scoped_ptr<ClientIncidentReport_DownloadDetails>()); | |
134 else | |
135 callback.Run(make_scoped_ptr(download_metadata->release_download()).Pass()); | |
136 } | |
137 | |
138 } // namespace | |
139 | |
140 // Applies operations to the profile's persistent DownloadMetadata as they occur | |
141 // on its corresponding download item. An instance can be in one of three | |
142 // states: waiting for metatada load, waiting for metadata to load after its | |
143 // corresponding DownloadManager has gone down, and not waiting for metadata to | |
144 // load. While it is waiting for metadata to load, it observes all download | |
145 // items and records operations on them. Once the metadata is ready, recorded | |
146 // operations are applied and observers are removed from all items but the one | |
147 // corresponding to the metadata (if it exists). The instance continues to | |
148 // observe its related item, and applies operations on it accordingly. While | |
149 // waiting for metadata to load, an instance also tracks callbacks to be run to | |
150 // provide cosnumers with persisted details of a download. | |
asanka
2014/10/28 20:55:20
nit: consumers
grt (UTC plus 2)
2014/10/30 15:11:40
Done.
| |
151 class DownloadMetadataManager::ManagerContext | |
152 : public content::DownloadItem::Observer { | |
153 public: | |
154 ManagerContext(const scoped_refptr<base::SequencedTaskRunner>& read_runner, | |
155 const scoped_refptr<base::SequencedTaskRunner>& write_runner, | |
156 content::DownloadManager* download_manager); | |
157 | |
158 // Detaches this context from its owner. The owner must not access the context | |
159 // following this call. The context will be deleted immediately if it is not | |
160 // waiting for a metadata load with either recorded operations or pending | |
161 // callbacks. | |
162 void Detach(); | |
163 | |
164 // Notifies the context that |download| has been added to its manager. | |
165 void OnDownloadCreated(content::DownloadItem* download); | |
166 | |
167 // Sets |request| as the relevant metadata to persist for |download|. | |
168 void SetRequest(content::DownloadItem* download, | |
169 const ClientDownloadRequest& request); | |
170 | |
171 // Removes metadata for the context from memory and posts a task in the worker | |
172 // pool to delete it on disk. | |
173 void RemoveMetadata(); | |
174 | |
175 // Gets the persisted DownloadDetails. |callback| will be run immediately if | |
176 // the data is available. Otherwise, it will be run later on the caller's | |
177 // thread. | |
178 void GetDownloadDetails(const GetDownloadDetailsCallback& callback); | |
179 | |
180 protected: | |
181 // content::DownloadItem::Observer methods. | |
182 void OnDownloadOpened(content::DownloadItem* download) override; | |
183 void OnDownloadRemoved(content::DownloadItem* download) override; | |
184 void OnDownloadDestroyed(content::DownloadItem* download) override; | |
185 | |
186 private: | |
187 enum State { | |
188 // The context is waiting for the metadata file to be loaded. | |
189 WAITING_FOR_LOAD, | |
190 | |
191 // The context is waiting for the metadata file to be loaded and its | |
192 // corresponding DownloadManager has gone away. | |
193 DETACHED_WAIT, | |
194 | |
195 // The context has loaded the metadata file. | |
196 LOAD_COMPLETE, | |
197 }; | |
198 | |
199 struct ItemData { | |
200 ItemData() : item(), removed() {} | |
201 // null if the download has been destroyed. If non-null, the item is being | |
202 // observed. | |
203 content::DownloadItem* item; | |
204 base::Time last_opened_time; | |
205 bool removed; | |
206 }; | |
207 | |
208 // A mapping of download IDs to their corresponding data. | |
209 typedef std::map<uint32_t, ItemData> ItemDataMap; | |
210 | |
211 virtual ~ManagerContext(); | |
212 | |
213 // Clears the |pending_items_| mapping, removing observers in the process. | |
214 void ClearPendingItems(); | |
215 | |
216 // Runs all |get_details_callbacks_| with the current metadata. | |
217 void RunCallbacks(); | |
218 | |
219 // Returns the id of the download corresponding to the loaded metadata, or | |
220 // kInvalidId if metadata has not finished loading or is not present. | |
221 uint32_t GetDownloadId() const; | |
222 | |
223 // Posts a task in the worker pool to read the metadata from disk. | |
224 void ReadMetadata(); | |
225 | |
226 // A callback run on the main thread with the results from reading the | |
227 // metadata file from disk. | |
228 void OnMetadataReady(scoped_ptr<DownloadMetadata> download_metadata); | |
229 | |
230 // Updates the last opened time in the metadata and writes it to disk. | |
231 void UpdateLastOpenedTime(const base::Time& last_opened_time); | |
232 | |
233 // Posts a task in the worker pool to write the metadata to disk. | |
234 void WriteMetadata(); | |
235 | |
236 // A task runner to which read tasks are posted. | |
237 scoped_refptr<base::SequencedTaskRunner> read_runner_; | |
238 | |
239 // A task runner to which write tasks are posted. | |
240 scoped_refptr<base::SequencedTaskRunner> write_runner_; | |
241 | |
242 // The path to the metadata file for this context. | |
243 base::FilePath metadata_path_; | |
244 | |
245 // When true, the context is waiting for a pending read operation to complete. | |
asanka
2014/10/28 20:55:20
Nit: this appears to document a boolean.
grt (UTC plus 2)
2014/10/30 15:11:40
Done.
| |
246 // While this is the case, all added download items are observed and events | |
247 // are temporarily recorded in |pending_items_|. Once the read completes, | |
248 // pending operations for the item correponding to the metadata file are | |
249 // applied to the file and all other recorded data are dropped (and | |
250 // corresponding observers are removed). Queued GetDownloadDetailsCallbacks | |
251 // are run upon read completion as well. | |
252 State state_; | |
253 | |
254 // The current metadata for the context. May be supplied either by reading | |
255 // from the file or by having been set via |SetRequest|. | |
256 scoped_ptr<DownloadMetadata> download_metadata_; | |
257 | |
258 // The operation data that accumulates for added download items while the | |
259 // metadata file is being read. | |
260 ItemDataMap pending_items_; | |
261 | |
262 // The download item corresponding to the download_metadata_. When non-null, | |
263 // the context observes events on this item only. | |
264 content::DownloadItem* item_; | |
265 | |
266 // Pending callbacks in response to GetDownloadDetails. The callbacks are run | |
267 // in order when a pending read operation completes. | |
268 std::list<GetDownloadDetailsCallback> get_details_callbacks_; | |
269 | |
270 base::WeakPtrFactory<ManagerContext> weak_factory_; | |
271 | |
272 DISALLOW_COPY_AND_ASSIGN(ManagerContext); | |
273 }; | |
274 | |
275 DownloadMetadataManager::DownloadMetadataManager( | |
276 const scoped_refptr<base::SequencedWorkerPool>& worker_pool) { | |
277 base::SequencedWorkerPool::SequenceToken token( | |
278 worker_pool->GetSequenceToken()); | |
279 // Do not block shutdown on reads since nothing will come of it. | |
280 read_runner_ = worker_pool->GetSequencedTaskRunnerWithShutdownBehavior( | |
281 token, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); | |
282 // Block shutdown on writes only if they've already begun. | |
283 write_runner_ = worker_pool->GetSequencedTaskRunnerWithShutdownBehavior( | |
284 token, base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); | |
285 } | |
286 | |
287 DownloadMetadataManager::DownloadMetadataManager( | |
288 const scoped_refptr<base::SequencedTaskRunner>& task_runner) | |
289 : read_runner_(task_runner), | |
290 write_runner_(task_runner) { | |
291 } | |
292 | |
293 DownloadMetadataManager::~DownloadMetadataManager() { | |
294 // All download managers must have gone down prior to this. | |
295 DCHECK(contexts_.empty()); | |
296 } | |
297 | |
298 void DownloadMetadataManager::AddDownloadManager( | |
299 content::DownloadManager* download_manager) { | |
300 DCHECK_EQ(contexts_.count(download_manager), 0U); | |
301 download_manager->AddObserver(this); | |
302 contexts_[download_manager] = | |
303 new ManagerContext(read_runner_, write_runner_, download_manager); | |
304 } | |
305 | |
306 void DownloadMetadataManager::SetRequest(content::DownloadItem* item, | |
307 const ClientDownloadRequest* request) { | |
308 content::DownloadManager* download_manager = | |
309 GetDownloadManagerForBrowserContext(item->GetBrowserContext()); | |
310 DCHECK_EQ(contexts_.count(download_manager), 1U); | |
311 if (request) | |
312 contexts_[download_manager]->SetRequest(item, *request); | |
313 else | |
314 contexts_[download_manager]->RemoveMetadata(); | |
315 } | |
316 | |
317 void DownloadMetadataManager::GetDownloadDetails( | |
318 content::BrowserContext* browser_context, | |
319 const GetDownloadDetailsCallback& callback) { | |
320 DCHECK(browser_context); | |
321 // The DownloadManager for |browser_context| may not have been created yet. In | |
322 // this case, asking for it would cause history to load in the background and | |
323 // wouldn't really help much. Instead, scan the contexts to see if one belongs | |
324 // to |browser_context|. If one is not found, read the metadata and return it. | |
325 scoped_ptr<ClientIncidentReport_DownloadDetails> download_details; | |
326 for (const auto& value : contexts_) { | |
327 if (value.first->GetBrowserContext() == browser_context) { | |
328 value.second->GetDownloadDetails(callback); | |
329 return; | |
330 } | |
331 } | |
332 | |
333 // Fire off a task to load the details and return them to the caller. | |
334 DownloadMetadata* metadata = new DownloadMetadata(); | |
335 read_runner_->PostTaskAndReply( | |
336 FROM_HERE, | |
337 base::Bind(&ReadMetadataOnWorkerPool, | |
338 GetMetadataPath(browser_context), | |
339 metadata), | |
340 base::Bind( | |
341 &ReturnResults, callback, base::Passed(make_scoped_ptr(metadata)))); | |
342 } | |
343 | |
344 content::DownloadManager* | |
345 DownloadMetadataManager::GetDownloadManagerForBrowserContext( | |
346 content::BrowserContext* context) { | |
347 return content::BrowserContext::GetDownloadManager(context); | |
348 } | |
349 | |
350 void DownloadMetadataManager::OnDownloadCreated( | |
351 content::DownloadManager* download_manager, | |
352 content::DownloadItem* item) { | |
353 DCHECK_EQ(contexts_.count(download_manager), 1U); | |
354 contexts_[download_manager]->OnDownloadCreated(item); | |
355 } | |
356 | |
357 void DownloadMetadataManager::ManagerGoingDown( | |
358 content::DownloadManager* download_manager) { | |
359 DCHECK_EQ(contexts_.count(download_manager), 1U); | |
360 auto iter = contexts_.find(download_manager); | |
361 iter->second->Detach(); | |
362 contexts_.erase(iter); | |
363 } | |
364 | |
365 DownloadMetadataManager::ManagerContext::ManagerContext( | |
366 const scoped_refptr<base::SequencedTaskRunner>& read_runner, | |
367 const scoped_refptr<base::SequencedTaskRunner>& write_runner, | |
368 content::DownloadManager* download_manager) | |
369 : read_runner_(read_runner), | |
370 write_runner_(write_runner), | |
371 metadata_path_(GetMetadataPath(download_manager->GetBrowserContext())), | |
372 state_(WAITING_FOR_LOAD), | |
373 item_(), | |
374 weak_factory_(this) { | |
375 // Start the asynchronous task to read the persistent metadata. | |
376 ReadMetadata(); | |
377 } | |
378 | |
379 void DownloadMetadataManager::ManagerContext::Detach() { | |
380 // Delete the instance immediately if there's no work to process after a | |
381 // pending read completes. | |
382 if (state_ == LOAD_COMPLETE || | |
383 (get_details_callbacks_.empty() && pending_items_.empty())) { | |
asanka
2014/10/28 20:55:20
nit: state == LOAD_COMPLETE => (no callbacks && no
grt (UTC plus 2)
2014/10/30 15:11:40
Done.
| |
384 delete this; | |
385 } else { | |
386 // delete the instance in OnMetadataReady. | |
387 state_ = DETACHED_WAIT; | |
388 } | |
389 } | |
390 | |
391 void DownloadMetadataManager::ManagerContext::OnDownloadCreated( | |
392 content::DownloadItem* download) { | |
393 const uint32_t id = download->GetId(); | |
394 if (state_ != LOAD_COMPLETE) { | |
395 // Observe all downloads and record operations while waiting for metadata to | |
396 // load. | |
397 download->AddObserver(this); | |
398 pending_items_[id].item = download; | |
399 } else if (id == GetDownloadId()) { | |
400 // Observe this one item if it is the important one. | |
401 DCHECK_EQ(item_, static_cast<content::DownloadItem*>(nullptr)); | |
402 item_ = download; | |
403 download->AddObserver(this); | |
404 } | |
405 } | |
406 | |
407 void DownloadMetadataManager::ManagerContext::SetRequest( | |
408 content::DownloadItem* download, | |
409 const ClientDownloadRequest& request) { | |
410 if (state_ != LOAD_COMPLETE) { | |
411 // Abandon the read task since |download| is the new top dog. | |
412 weak_factory_.InvalidateWeakPtrs(); | |
413 state_ = LOAD_COMPLETE; | |
414 // Stop observing all items and drop any recorded operations. | |
415 ClearPendingItems(); | |
416 } | |
417 // Observe this item from here on out. | |
418 if (item_ != download) { | |
419 if (item_) | |
420 item_->RemoveObserver(this); | |
421 download->AddObserver(this); | |
422 item_ = download; | |
423 } | |
424 // Take the request. | |
425 download_metadata_.reset(new DownloadMetadata); | |
426 download_metadata_->set_download_id(download->GetId()); | |
427 download_metadata_->mutable_download()->mutable_download()->CopyFrom(request); | |
428 base::Time end_time = download->GetEndTime(); | |
429 // The item may have yet to be marked as complete, so consider the current | |
430 // time as being its download time. | |
431 if (end_time.is_null()) | |
432 end_time = base::Time::Now(); | |
433 download_metadata_->mutable_download()->set_download_time_msec( | |
434 end_time.ToJavaTime()); | |
435 // Persist it. | |
436 WriteMetadata(); | |
437 // Run callbacks (only present in case of a transition to LOAD_COMPLETE). | |
438 RunCallbacks(); | |
439 } | |
440 | |
441 void DownloadMetadataManager::ManagerContext::RemoveMetadata() { | |
442 if (state_ != LOAD_COMPLETE) { | |
443 // Abandon the read task since the file is to be removed. | |
444 weak_factory_.InvalidateWeakPtrs(); | |
445 state_ = LOAD_COMPLETE; | |
446 // Stop observing all items and drop any recorded operations. | |
447 ClearPendingItems(); | |
448 } | |
449 // Remove any metadata. | |
450 download_metadata_.reset(); | |
451 write_runner_->PostTask( | |
452 FROM_HERE, base::Bind(&DeleteMetadataOnWorkerPool, metadata_path_)); | |
453 // Run callbacks (only present in case of a transition to LOAD_COMPLETE). | |
454 RunCallbacks(); | |
455 } | |
456 | |
457 void DownloadMetadataManager::ManagerContext::GetDownloadDetails( | |
458 const GetDownloadDetailsCallback& callback) { | |
459 if (state_ != LOAD_COMPLETE) { | |
460 get_details_callbacks_.push_back(callback); | |
461 } else { | |
462 if (download_metadata_) { | |
463 callback.Run(make_scoped_ptr(new ClientIncidentReport_DownloadDetails( | |
464 download_metadata_->download())).Pass()); | |
465 } else { | |
466 callback.Run(scoped_ptr<ClientIncidentReport_DownloadDetails>()); | |
467 } | |
468 } | |
469 } | |
470 | |
471 void DownloadMetadataManager::ManagerContext::OnDownloadOpened( | |
472 content::DownloadItem* download) { | |
473 const base::Time now = base::Time::Now(); | |
474 if (state_ != LOAD_COMPLETE) | |
475 pending_items_[download->GetId()].last_opened_time = now; | |
476 else | |
477 UpdateLastOpenedTime(now); | |
478 } | |
479 | |
480 void DownloadMetadataManager::ManagerContext::OnDownloadRemoved( | |
481 content::DownloadItem* download) { | |
482 if (state_ != LOAD_COMPLETE) | |
483 pending_items_[download->GetId()].removed = true; | |
484 else | |
485 RemoveMetadata(); | |
486 } | |
487 | |
488 void DownloadMetadataManager::ManagerContext::OnDownloadDestroyed( | |
489 content::DownloadItem* download) { | |
490 if (state_ != LOAD_COMPLETE) { | |
491 // Erase the data for this item if nothing of import happened. Otherwise | |
492 // clear its item pointer since it is now invalid. | |
493 auto iter = pending_items_.find(download->GetId()); | |
494 DCHECK(iter != pending_items_.end()); | |
495 if (!iter->second.removed && iter->second.last_opened_time.is_null()) | |
496 pending_items_.erase(iter); | |
497 else | |
498 iter->second.item = nullptr; | |
499 } else if (item_) { | |
500 // This item is no longer being observed. | |
501 DCHECK_EQ(item_, download); | |
502 item_ = nullptr; | |
503 } | |
504 } | |
505 | |
506 DownloadMetadataManager::ManagerContext::~ManagerContext() { | |
507 // All downloads must have been destroyed prior to deleting the context and | |
508 // all recorded operations and callbacks must have been handled. | |
509 DCHECK(pending_items_.empty()); | |
510 DCHECK_EQ(item_, static_cast<content::DownloadItem*>(nullptr)); | |
511 DCHECK(get_details_callbacks_.empty()); | |
512 } | |
513 | |
514 void DownloadMetadataManager::ManagerContext::ClearPendingItems() { | |
515 for (const auto& value : pending_items_) { | |
516 if (value.second.item) | |
517 value.second.item->RemoveObserver(this); | |
518 } | |
519 pending_items_.clear(); | |
520 } | |
521 | |
522 void DownloadMetadataManager::ManagerContext::RunCallbacks() { | |
523 while (!get_details_callbacks_.empty()) { | |
524 const auto& callback = get_details_callbacks_.front(); | |
525 if (download_metadata_) { | |
526 callback.Run(make_scoped_ptr(new ClientIncidentReport_DownloadDetails( | |
527 download_metadata_->download())).Pass()); | |
528 } else { | |
529 callback.Run(scoped_ptr<ClientIncidentReport_DownloadDetails>()); | |
530 } | |
531 get_details_callbacks_.pop_front(); | |
532 } | |
533 } | |
534 | |
535 uint32_t DownloadMetadataManager::ManagerContext::GetDownloadId() const { | |
536 if (state_ != LOAD_COMPLETE || !download_metadata_) | |
537 return content::DownloadItem::kInvalidId; | |
538 return download_metadata_->download_id(); | |
539 } | |
540 | |
541 void DownloadMetadataManager::ManagerContext::ReadMetadata() { | |
542 DCHECK_NE(state_, LOAD_COMPLETE); | |
543 | |
544 DownloadMetadata* metadata = new DownloadMetadata(); | |
545 // Do not block shutdown on this read since nothing will come of it. | |
546 read_runner_->PostTaskAndReply( | |
547 FROM_HERE, | |
548 base::Bind(&ReadMetadataOnWorkerPool, metadata_path_, metadata), | |
549 base::Bind(&DownloadMetadataManager::ManagerContext::OnMetadataReady, | |
550 weak_factory_.GetWeakPtr(), | |
551 base::Passed(make_scoped_ptr(metadata)))); | |
552 } | |
553 | |
554 void DownloadMetadataManager::ManagerContext::OnMetadataReady( | |
555 scoped_ptr<DownloadMetadata> download_metadata) { | |
556 DCHECK_NE(state_, LOAD_COMPLETE); | |
557 | |
558 const bool is_detached = (state_ == DETACHED_WAIT); | |
559 | |
560 // Note that any available data has been read. | |
561 state_ = LOAD_COMPLETE; | |
562 if (download_metadata->has_download_id()) | |
563 download_metadata_ = download_metadata.Pass(); | |
564 else | |
565 download_metadata_.reset(); | |
566 | |
567 // Process all operations that had been held while waiting for the metadata. | |
568 content::DownloadItem* download = nullptr; | |
569 { | |
570 const auto& iter = pending_items_.find(GetDownloadId()); | |
571 if (iter != pending_items_.end()) { | |
572 const ItemData& item_data = iter->second; | |
573 download = item_data.item; // non-null if not destroyed. | |
574 if (item_data.removed) | |
575 RemoveMetadata(); | |
576 else if (!item_data.last_opened_time.is_null()) | |
577 UpdateLastOpenedTime(item_data.last_opened_time); | |
578 } | |
579 } | |
580 | |
581 // Stop observing all items. | |
582 ClearPendingItems(); | |
583 | |
584 // If the download was known, observe it from here on out. | |
585 if (download) { | |
586 download->AddObserver(this); | |
587 item_ = download; | |
588 } | |
589 | |
590 // Run callbacks. | |
591 RunCallbacks(); | |
592 | |
593 // Delete the context now if it has been detached. | |
594 if (is_detached) | |
595 delete this; | |
596 } | |
597 | |
598 void DownloadMetadataManager::ManagerContext::UpdateLastOpenedTime( | |
599 const base::Time& last_opened_time) { | |
600 download_metadata_->mutable_download()->set_open_time_msec( | |
601 last_opened_time.ToJavaTime()); | |
602 WriteMetadata(); | |
603 } | |
604 | |
605 void DownloadMetadataManager::ManagerContext::WriteMetadata() { | |
606 write_runner_->PostTask( | |
607 FROM_HERE, | |
608 base::Bind(&WriteMetadataOnWorkerPool, | |
609 metadata_path_, | |
610 base::Owned(new DownloadMetadata(*download_metadata_)))); | |
611 } | |
612 | |
613 } // namespace safe_browsing | |
OLD | NEW |