Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / chrome / browser / safe_browsing / incident_reporting / download_metadata_manager.cc
blob3fc7549b35502ac01d22e7e0bb05e235bd88303e
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.
5 #include "chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.h"
7 #include <list>
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/macros.h"
16 #include "base/metrics/histogram.h"
17 #include "base/sequenced_task_runner.h"
18 #include "base/threading/sequenced_worker_pool.h"
19 #include "chrome/common/safe_browsing/csd.pb.h"
20 #include "content/public/browser/browser_context.h"
21 #include "content/public/browser/download_item.h"
23 namespace safe_browsing {
25 namespace {
27 // Histogram bucket values for metadata read operations. Do not reorder.
28 enum MetadataReadResult {
29 READ_SUCCESS = 0,
30 OPEN_FAILURE = 1,
31 NOT_FOUND = 2,
32 GET_INFO_FAILURE = 3,
33 FILE_TOO_BIG = 4,
34 READ_FAILURE = 5,
35 PARSE_FAILURE = 6,
36 MALFORMED_DATA = 7,
37 NUM_READ_RESULTS
40 // Histogram bucket values for metadata write operations. Do not reorder.
41 enum MetadataWriteResult {
42 WRITE_SUCCESS = 0,
43 SERIALIZATION_FAILURE = 1,
44 WRITE_FAILURE = 2,
45 NUM_WRITE_RESULTS
48 // The name of the metadata file in the profile directory.
49 const base::FilePath::CharType kDownloadMetadataBasename[] =
50 FILE_PATH_LITERAL("DownloadMetadata");
53 // DownloadItemData ------------------------------------------------------------
55 // A UserData object that holds the ClientDownloadRequest for a download while
56 // it is in progress.
57 class DownloadItemData : public base::SupportsUserData::Data {
58 public:
59 // Sets the ClientDownloadRequest for a given DownloadItem.
60 static void SetRequestForDownload(content::DownloadItem* item,
61 scoped_ptr<ClientDownloadRequest> request);
63 // Returns the ClientDownloadRequest for a download or null if there is none.
64 static scoped_ptr<ClientDownloadRequest> TakeRequestForDownload(
65 content::DownloadItem* item);
67 private:
68 // A unique id for associating metadata with a content::DownloadItem.
69 static const void* const kKey_;
71 explicit DownloadItemData(scoped_ptr<ClientDownloadRequest> request)
72 : request_(request.Pass()) {}
73 ~DownloadItemData() override {}
75 scoped_ptr<ClientDownloadRequest> request_;
77 DISALLOW_COPY_AND_ASSIGN(DownloadItemData);
80 // Make the key's value unique by setting it to its own location.
81 // static
82 const void* const DownloadItemData::kKey_ = &DownloadItemData::kKey_;
84 // static
85 void DownloadItemData::SetRequestForDownload(
86 content::DownloadItem* item,
87 scoped_ptr<ClientDownloadRequest> request) {
88 item->SetUserData(&kKey_, new DownloadItemData(request.Pass()));
91 // static
92 scoped_ptr<ClientDownloadRequest> DownloadItemData::TakeRequestForDownload(
93 content::DownloadItem* item) {
94 DownloadItemData* data =
95 static_cast<DownloadItemData*>(item->GetUserData(&kKey_));
96 if (!data)
97 return nullptr;
98 scoped_ptr<ClientDownloadRequest> request = data->request_.Pass();
99 item->RemoveUserData(&kKey_);
100 return request.Pass();
104 // Utility functions------------------------------------------------------------
106 // Returns the path to the metadata file for |browser_context|.
107 base::FilePath GetMetadataPath(content::BrowserContext* browser_context) {
108 return browser_context->GetPath().Append(kDownloadMetadataBasename);
111 // Returns true if |metadata| appears to be valid.
112 bool MetadataIsValid(const DownloadMetadata& metadata) {
113 return (metadata.has_download_id() &&
114 metadata.has_download() &&
115 metadata.download().has_download() &&
116 metadata.download().download().has_url());
119 // Reads and parses a DownloadMetadata message from |metadata_path| into
120 // |metadata|.
121 void ReadMetadataOnWorkerPool(const base::FilePath& metadata_path,
122 DownloadMetadata* metadata) {
123 using base::File;
124 DCHECK(metadata);
125 MetadataReadResult result = NUM_READ_RESULTS;
126 File metadata_file(metadata_path, File::FLAG_OPEN | File::FLAG_READ);
127 if (metadata_file.IsValid()) {
128 base::File::Info info;
129 if (metadata_file.GetInfo(&info)) {
130 if (info.size <= INT_MAX) {
131 const int size = static_cast<int>(info.size);
132 scoped_ptr<char[]> file_data(new char[info.size]);
133 if (metadata_file.Read(0, file_data.get(), size)) {
134 if (!metadata->ParseFromArray(file_data.get(), size))
135 result = PARSE_FAILURE;
136 else if (!MetadataIsValid(*metadata))
137 result = MALFORMED_DATA;
138 else
139 result = READ_SUCCESS;
140 } else {
141 result = READ_FAILURE;
143 } else {
144 result = FILE_TOO_BIG;
146 } else {
147 result = GET_INFO_FAILURE;
149 } else if (metadata_file.error_details() != File::FILE_ERROR_NOT_FOUND) {
150 result = OPEN_FAILURE;
151 } else {
152 result = NOT_FOUND;
154 if (result != READ_SUCCESS)
155 metadata->Clear();
156 UMA_HISTOGRAM_ENUMERATION(
157 "SBIRS.DownloadMetadata.ReadResult", result, NUM_READ_RESULTS);
160 // Writes |download_metadata| to |metadata_path|.
161 void WriteMetadataOnWorkerPool(const base::FilePath& metadata_path,
162 DownloadMetadata* download_metadata) {
163 MetadataWriteResult result = NUM_WRITE_RESULTS;
164 std::string file_data;
165 if (download_metadata->SerializeToString(&file_data)) {
166 result =
167 base::ImportantFileWriter::WriteFileAtomically(metadata_path, file_data)
168 ? WRITE_SUCCESS
169 : WRITE_FAILURE;
170 } else {
171 result = SERIALIZATION_FAILURE;
173 UMA_HISTOGRAM_ENUMERATION(
174 "SBIRS.DownloadMetadata.WriteResult", result, NUM_WRITE_RESULTS);
177 // Deletes |metadata_path|.
178 void DeleteMetadataOnWorkerPool(const base::FilePath& metadata_path) {
179 bool success = base::DeleteFile(metadata_path, false /* not recursive */);
180 UMA_HISTOGRAM_BOOLEAN("SBIRS.DownloadMetadata.DeleteSuccess", success);
183 // Runs |callback| with the DownloadDetails in |download_metadata|.
184 void ReturnResults(
185 const DownloadMetadataManager::GetDownloadDetailsCallback& callback,
186 scoped_ptr<DownloadMetadata> download_metadata) {
187 if (!download_metadata->has_download_id())
188 callback.Run(scoped_ptr<ClientIncidentReport_DownloadDetails>());
189 else
190 callback.Run(make_scoped_ptr(download_metadata->release_download()).Pass());
193 } // namespace
195 // Applies operations to the profile's persistent DownloadMetadata as they occur
196 // on its corresponding download item. An instance can be in one of three
197 // states: waiting for metatada load, waiting for metadata to load after its
198 // corresponding DownloadManager has gone down, and not waiting for metadata to
199 // load. The instance observes all download items beloing to its manager. While
200 // it is waiting for metadata to load, it records all operations on download
201 // items that must be reflected in the metadata. Once the metadata is ready,
202 // recorded operations are applied to the metadata. The instance continues to
203 // observe all download items to keep the existing metadata up to date. While
204 // waiting for metadata to load, an instance also tracks callbacks to be run to
205 // provide consumers with persisted details of a download.
206 class DownloadMetadataManager::ManagerContext
207 : public content::DownloadItem::Observer {
208 public:
209 ManagerContext(const scoped_refptr<base::SequencedTaskRunner>& read_runner,
210 const scoped_refptr<base::SequencedTaskRunner>& write_runner,
211 content::DownloadManager* download_manager);
213 // Detaches this context from its owner. The owner must not access the context
214 // following this call. The context will be deleted immediately if it is not
215 // waiting for a metadata load with either recorded operations or pending
216 // callbacks.
217 void Detach(content::DownloadManager* download_manager);
219 // Notifies the context that |download| has been added to its manager.
220 void OnDownloadCreated(content::DownloadItem* download);
222 // Sets |request| as the relevant metadata to persist for |download| if or
223 // when it is complete. If |request| is null, the metadata for |download|
224 // is/will be removed.
225 void SetRequest(content::DownloadItem* download,
226 scoped_ptr<ClientDownloadRequest> request);
228 // Gets the persisted DownloadDetails. |callback| will be run immediately if
229 // the data is available. Otherwise, it will be run later on the caller's
230 // thread.
231 void GetDownloadDetails(const GetDownloadDetailsCallback& callback);
233 protected:
234 // content::DownloadItem::Observer methods.
235 void OnDownloadUpdated(content::DownloadItem* download) override;
236 void OnDownloadOpened(content::DownloadItem* download) override;
237 void OnDownloadRemoved(content::DownloadItem* download) override;
239 private:
240 enum State {
241 // The context is waiting for the metadata file to be loaded.
242 WAITING_FOR_LOAD,
244 // The context is waiting for the metadata file to be loaded and its
245 // corresponding DownloadManager has gone away.
246 DETACHED_WAIT,
248 // The context has loaded the metadata file.
249 LOAD_COMPLETE,
252 struct ItemData {
253 ItemData() : removed() {}
254 base::Time last_opened_time;
255 bool removed;
258 // A mapping of download IDs to their corresponding data.
259 typedef std::map<uint32_t, ItemData> ItemDataMap;
261 ~ManagerContext() override;
263 // Commits |request| to the DownloadDetails for |item|'s BrowserContext.
264 // Callbacks will be run immediately if the context had been waiting for a
265 // load (which will be abandoned).
266 void CommitRequest(content::DownloadItem* item,
267 scoped_ptr<ClientDownloadRequest> request);
269 // Posts a task in the worker pool to read the metadata from disk.
270 void ReadMetadata();
272 // Posts a task in the worker pool to write the metadata to disk.
273 void WriteMetadata();
275 // Removes metadata for the context from memory and posts a task in the worker
276 // pool to delete it on disk.
277 void RemoveMetadata();
279 // Clears the |pending_items_| mapping.
280 void ClearPendingItems();
282 // Runs all |get_details_callbacks_| with the current metadata.
283 void RunCallbacks();
285 // Returns true if metadata corresponding to |item| is available.
286 bool HasMetadataFor(const content::DownloadItem* item) const;
288 // A callback run on the main thread with the results from reading the
289 // metadata file from disk.
290 void OnMetadataReady(scoped_ptr<DownloadMetadata> download_metadata);
292 // Updates the last opened time in the metadata and writes it to disk.
293 void UpdateLastOpenedTime(const base::Time& last_opened_time);
295 // A task runner to which read tasks are posted.
296 scoped_refptr<base::SequencedTaskRunner> read_runner_;
298 // A task runner to which write tasks are posted.
299 scoped_refptr<base::SequencedTaskRunner> write_runner_;
301 // The path to the metadata file for this context.
302 base::FilePath metadata_path_;
304 // When not LOAD_COMPLETE, the context is waiting for a pending read operation
305 // to complete. While this is the case, events are temporarily recorded in
306 // |pending_items_|. Once the read completes, pending operations for the item
307 // corresponding to the metadata file are applied to the file and all other
308 // recorded data are dropped. Queued GetDownloadDetailsCallbacks are run upon
309 // read completion as well. The context is moved to the DETACHED_WAIT state if
310 // the corresponding DownloadManager goes away while a read operation is
311 // outstanding. When the read subsequently completes, the context is destroyed
312 // after the processing described above is performed.
313 State state_;
315 // The current metadata for the context. May be supplied either by reading
316 // from the file or by having been set via |SetRequest|.
317 scoped_ptr<DownloadMetadata> download_metadata_;
319 // The operation data that accumulates for added download items while the
320 // metadata file is being read.
321 ItemDataMap pending_items_;
323 // Pending callbacks in response to GetDownloadDetails. The callbacks are run
324 // in order when a pending read operation completes.
325 std::list<GetDownloadDetailsCallback> get_details_callbacks_;
327 base::WeakPtrFactory<ManagerContext> weak_factory_;
329 DISALLOW_COPY_AND_ASSIGN(ManagerContext);
333 // DownloadMetadataManager -----------------------------------------------------
335 DownloadMetadataManager::DownloadMetadataManager(
336 const scoped_refptr<base::SequencedWorkerPool>& worker_pool) {
337 base::SequencedWorkerPool::SequenceToken token(
338 worker_pool->GetSequenceToken());
339 // Do not block shutdown on reads since nothing will come of it.
340 read_runner_ = worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
341 token, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
342 // Block shutdown on writes only if they've already begun.
343 write_runner_ = worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
344 token, base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
347 DownloadMetadataManager::DownloadMetadataManager(
348 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
349 : read_runner_(task_runner), write_runner_(task_runner) {
352 DownloadMetadataManager::~DownloadMetadataManager() {
353 // Destruction may have taken place before managers have gone down.
354 for (const auto& manager_context_pair : contexts_) {
355 manager_context_pair.first->RemoveObserver(this);
356 manager_context_pair.second->Detach(manager_context_pair.first);
358 contexts_.clear();
361 void DownloadMetadataManager::AddDownloadManager(
362 content::DownloadManager* download_manager) {
363 DCHECK_EQ(contexts_.count(download_manager), 0U);
364 download_manager->AddObserver(this);
365 contexts_[download_manager] =
366 new ManagerContext(read_runner_, write_runner_, download_manager);
369 void DownloadMetadataManager::SetRequest(content::DownloadItem* item,
370 const ClientDownloadRequest* request) {
371 DCHECK(request);
372 content::DownloadManager* download_manager =
373 GetDownloadManagerForBrowserContext(item->GetBrowserContext());
374 DCHECK_EQ(contexts_.count(download_manager), 1U);
375 contexts_[download_manager]->SetRequest(
376 item, make_scoped_ptr(new ClientDownloadRequest(*request)));
379 void DownloadMetadataManager::GetDownloadDetails(
380 content::BrowserContext* browser_context,
381 const GetDownloadDetailsCallback& callback) {
382 DCHECK(browser_context);
383 // The DownloadManager for |browser_context| may not have been created yet. In
384 // this case, asking for it would cause history to load in the background and
385 // wouldn't really help much. Instead, scan the contexts to see if one belongs
386 // to |browser_context|. If one is not found, read the metadata and return it.
387 scoped_ptr<ClientIncidentReport_DownloadDetails> download_details;
388 for (const auto& manager_context_pair : contexts_) {
389 if (manager_context_pair.first->GetBrowserContext() == browser_context) {
390 manager_context_pair.second->GetDownloadDetails(callback);
391 return;
395 // Fire off a task to load the details and return them to the caller.
396 DownloadMetadata* metadata = new DownloadMetadata();
397 read_runner_->PostTaskAndReply(
398 FROM_HERE,
399 base::Bind(&ReadMetadataOnWorkerPool,
400 GetMetadataPath(browser_context),
401 metadata),
402 base::Bind(
403 &ReturnResults, callback, base::Passed(make_scoped_ptr(metadata))));
406 content::DownloadManager*
407 DownloadMetadataManager::GetDownloadManagerForBrowserContext(
408 content::BrowserContext* context) {
409 return content::BrowserContext::GetDownloadManager(context);
412 void DownloadMetadataManager::OnDownloadCreated(
413 content::DownloadManager* download_manager,
414 content::DownloadItem* item) {
415 DCHECK_EQ(contexts_.count(download_manager), 1U);
416 contexts_[download_manager]->OnDownloadCreated(item);
419 void DownloadMetadataManager::ManagerGoingDown(
420 content::DownloadManager* download_manager) {
421 DCHECK_EQ(contexts_.count(download_manager), 1U);
422 auto iter = contexts_.find(download_manager);
423 iter->first->RemoveObserver(this);
424 iter->second->Detach(download_manager);
425 contexts_.erase(iter);
429 // DownloadMetadataManager::ManagerContext -------------------------------------
431 DownloadMetadataManager::ManagerContext::ManagerContext(
432 const scoped_refptr<base::SequencedTaskRunner>& read_runner,
433 const scoped_refptr<base::SequencedTaskRunner>& write_runner,
434 content::DownloadManager* download_manager)
435 : read_runner_(read_runner),
436 write_runner_(write_runner),
437 metadata_path_(GetMetadataPath(download_manager->GetBrowserContext())),
438 state_(WAITING_FOR_LOAD),
439 weak_factory_(this) {
440 // Observe all pre-existing items in the manager.
441 content::DownloadManager::DownloadVector items;
442 download_manager->GetAllDownloads(&items);
443 for (auto* download_item : items)
444 download_item->AddObserver(this);
446 // Start the asynchronous task to read the persistent metadata.
447 ReadMetadata();
450 void DownloadMetadataManager::ManagerContext::Detach(
451 content::DownloadManager* download_manager) {
452 // Stop observing all items belonging to the manager.
453 content::DownloadManager::DownloadVector items;
454 download_manager->GetAllDownloads(&items);
455 for (auto* download_item : items)
456 download_item->RemoveObserver(this);
458 // Delete the instance immediately if there's no work to process after a
459 // pending read completes.
460 if (get_details_callbacks_.empty() && pending_items_.empty()) {
461 delete this;
462 } else {
463 // delete the instance in OnMetadataReady.
464 state_ = DETACHED_WAIT;
468 void DownloadMetadataManager::ManagerContext::OnDownloadCreated(
469 content::DownloadItem* download) {
470 download->AddObserver(this);
473 void DownloadMetadataManager::ManagerContext::SetRequest(
474 content::DownloadItem* download,
475 scoped_ptr<ClientDownloadRequest> request) {
476 DCHECK(request);
477 // Hold on to the request for completion time if the download is in progress.
478 // Otherwise, commit the request.
479 if (download->GetState() == content::DownloadItem::IN_PROGRESS)
480 DownloadItemData::SetRequestForDownload(download, request.Pass());
481 else
482 CommitRequest(download, request.Pass());
485 void DownloadMetadataManager::ManagerContext::GetDownloadDetails(
486 const GetDownloadDetailsCallback& callback) {
487 if (state_ != LOAD_COMPLETE) {
488 get_details_callbacks_.push_back(callback);
489 } else {
490 callback.Run(download_metadata_ ?
491 make_scoped_ptr(new ClientIncidentReport_DownloadDetails(
492 download_metadata_->download())) :
493 nullptr);
497 void DownloadMetadataManager::ManagerContext::OnDownloadUpdated(
498 content::DownloadItem* download) {
499 // Persist metadata for this download if it has just completed.
500 if (download->GetState() == content::DownloadItem::COMPLETE) {
501 // Ignore downloads we don't have a ClientDownloadRequest for.
502 scoped_ptr<ClientDownloadRequest> request =
503 DownloadItemData::TakeRequestForDownload(download);
504 if (request)
505 CommitRequest(download, request.Pass());
509 void DownloadMetadataManager::ManagerContext::OnDownloadOpened(
510 content::DownloadItem* download) {
511 const base::Time now = base::Time::Now();
512 if (state_ != LOAD_COMPLETE)
513 pending_items_[download->GetId()].last_opened_time = now;
514 else if (HasMetadataFor(download))
515 UpdateLastOpenedTime(now);
518 void DownloadMetadataManager::ManagerContext::OnDownloadRemoved(
519 content::DownloadItem* download) {
520 if (state_ != LOAD_COMPLETE)
521 pending_items_[download->GetId()].removed = true;
522 else if (HasMetadataFor(download))
523 RemoveMetadata();
526 DownloadMetadataManager::ManagerContext::~ManagerContext() {
527 // A context should not be deleted while waiting for a load to complete.
528 DCHECK(pending_items_.empty());
529 DCHECK(get_details_callbacks_.empty());
532 void DownloadMetadataManager::ManagerContext::CommitRequest(
533 content::DownloadItem* item,
534 scoped_ptr<ClientDownloadRequest> request) {
535 DCHECK_EQ(content::DownloadItem::COMPLETE, item->GetState());
536 if (state_ != LOAD_COMPLETE) {
537 // Abandon the read task since |item| is the new top dog.
538 weak_factory_.InvalidateWeakPtrs();
539 state_ = LOAD_COMPLETE;
540 // Drop any recorded operations.
541 ClearPendingItems();
543 // Take the request.
544 download_metadata_.reset(new DownloadMetadata);
545 download_metadata_->set_download_id(item->GetId());
546 download_metadata_->mutable_download()->set_allocated_download(
547 request.release());
548 download_metadata_->mutable_download()->set_download_time_msec(
549 item->GetEndTime().ToJavaTime());
550 // Persist it.
551 WriteMetadata();
552 // Run callbacks (only present in case of a transition to LOAD_COMPLETE).
553 RunCallbacks();
556 void DownloadMetadataManager::ManagerContext::ReadMetadata() {
557 DCHECK_NE(state_, LOAD_COMPLETE);
559 DownloadMetadata* metadata = new DownloadMetadata();
560 // Do not block shutdown on this read since nothing will come of it.
561 read_runner_->PostTaskAndReply(
562 FROM_HERE,
563 base::Bind(&ReadMetadataOnWorkerPool, metadata_path_, metadata),
564 base::Bind(&DownloadMetadataManager::ManagerContext::OnMetadataReady,
565 weak_factory_.GetWeakPtr(),
566 base::Passed(make_scoped_ptr(metadata))));
569 void DownloadMetadataManager::ManagerContext::WriteMetadata() {
570 write_runner_->PostTask(
571 FROM_HERE,
572 base::Bind(&WriteMetadataOnWorkerPool,
573 metadata_path_,
574 base::Owned(new DownloadMetadata(*download_metadata_))));
577 void DownloadMetadataManager::ManagerContext::RemoveMetadata() {
578 if (state_ != LOAD_COMPLETE) {
579 // Abandon the read task since the file is to be removed.
580 weak_factory_.InvalidateWeakPtrs();
581 state_ = LOAD_COMPLETE;
582 // Drop any recorded operations.
583 ClearPendingItems();
585 // Remove any metadata.
586 download_metadata_.reset();
587 write_runner_->PostTask(
588 FROM_HERE, base::Bind(&DeleteMetadataOnWorkerPool, metadata_path_));
589 // Run callbacks (only present in case of a transition to LOAD_COMPLETE).
590 RunCallbacks();
593 void DownloadMetadataManager::ManagerContext::ClearPendingItems() {
594 pending_items_.clear();
597 void DownloadMetadataManager::ManagerContext::RunCallbacks() {
598 while (!get_details_callbacks_.empty()) {
599 const auto& callback = get_details_callbacks_.front();
600 callback.Run(download_metadata_ ?
601 make_scoped_ptr(new ClientIncidentReport_DownloadDetails(
602 download_metadata_->download())) :
603 nullptr);
604 get_details_callbacks_.pop_front();
608 bool DownloadMetadataManager::ManagerContext::HasMetadataFor(
609 const content::DownloadItem* item) const {
610 // There must not be metadata if the load is not complete.
611 DCHECK(state_ == LOAD_COMPLETE || !download_metadata_);
612 return (download_metadata_ &&
613 download_metadata_->download_id() == item->GetId());
616 void DownloadMetadataManager::ManagerContext::OnMetadataReady(
617 scoped_ptr<DownloadMetadata> download_metadata) {
618 DCHECK_NE(state_, LOAD_COMPLETE);
620 const bool is_detached = (state_ == DETACHED_WAIT);
622 // Note that any available data has been read.
623 state_ = LOAD_COMPLETE;
624 if (download_metadata->has_download_id())
625 download_metadata_ = download_metadata.Pass();
626 else
627 download_metadata_.reset();
629 // Process all operations that had been held while waiting for the metadata.
630 if (download_metadata_) {
631 const auto& iter = pending_items_.find(download_metadata_->download_id());
632 if (iter != pending_items_.end()) {
633 const ItemData& item_data = iter->second;
634 if (item_data.removed)
635 RemoveMetadata();
636 else if (!item_data.last_opened_time.is_null())
637 UpdateLastOpenedTime(item_data.last_opened_time);
641 // Drop the recorded operations.
642 ClearPendingItems();
644 // Run callbacks.
645 RunCallbacks();
647 // Delete the context now if it has been detached.
648 if (is_detached)
649 delete this;
652 void DownloadMetadataManager::ManagerContext::UpdateLastOpenedTime(
653 const base::Time& last_opened_time) {
654 download_metadata_->mutable_download()->set_open_time_msec(
655 last_opened_time.ToJavaTime());
656 WriteMetadata();
659 } // namespace safe_browsing