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

Side by Side Diff: chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.cc

Issue 663023007: Include high-fidelity metadata about a download in incident reports. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git/+/master
Patch Set: sync to r300417 Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698