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