ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / browser / safe_browsing / incident_reporting / download_metadata_manager.cc
blob6c6a9a45ff9d5dd9c78410c7e2246f679d63aea2
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/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"
22 namespace safe_browsing {
24 namespace {
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
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
47 // The name of the metadata file in the profile directory.
48 const base::FilePath::CharType kDownloadMetadataBasename[] =
49 FILE_PATH_LITERAL("DownloadMetadata");
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);
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());
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;
88 } else {
89 result = FILE_TOO_BIG;
91 } else {
92 result = GET_INFO_FAILURE;
94 } else if (metadata_file.error_details() != File::FILE_ERROR_NOT_FOUND) {
95 result = OPEN_FAILURE;
96 } else {
97 result = NOT_FOUND;
99 if (result != READ_SUCCESS)
100 metadata->Clear();
101 UMA_HISTOGRAM_ENUMERATION(
102 "SBIRS.DownloadMetadata.ReadResult", result, NUM_READ_RESULTS);
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;
118 UMA_HISTOGRAM_ENUMERATION(
119 "SBIRS.DownloadMetadata.WriteResult", result, NUM_WRITE_RESULTS);
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.DownloadMetadata.DeleteSuccess", success);
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());
138 } // namespace
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 consumers with persisted details of a download.
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);
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();
164 // Notifies the context that |download| has been added to its manager.
165 void OnDownloadCreated(content::DownloadItem* download);
167 // Sets |request| as the relevant metadata to persist for |download|.
168 void SetRequest(content::DownloadItem* download,
169 const ClientDownloadRequest& request);
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();
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);
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;
186 private:
187 enum State {
188 // The context is waiting for the metadata file to be loaded.
189 WAITING_FOR_LOAD,
191 // The context is waiting for the metadata file to be loaded and its
192 // corresponding DownloadManager has gone away.
193 DETACHED_WAIT,
195 // The context has loaded the metadata file.
196 LOAD_COMPLETE,
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;
208 // A mapping of download IDs to their corresponding data.
209 typedef std::map<uint32_t, ItemData> ItemDataMap;
211 ~ManagerContext() override;
213 // Clears the |pending_items_| mapping, removing observers in the process.
214 void ClearPendingItems();
216 // Runs all |get_details_callbacks_| with the current metadata.
217 void RunCallbacks();
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;
223 // Posts a task in the worker pool to read the metadata from disk.
224 void ReadMetadata();
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);
230 // Updates the last opened time in the metadata and writes it to disk.
231 void UpdateLastOpenedTime(const base::Time& last_opened_time);
233 // Posts a task in the worker pool to write the metadata to disk.
234 void WriteMetadata();
236 // A task runner to which read tasks are posted.
237 scoped_refptr<base::SequencedTaskRunner> read_runner_;
239 // A task runner to which write tasks are posted.
240 scoped_refptr<base::SequencedTaskRunner> write_runner_;
242 // The path to the metadata file for this context.
243 base::FilePath metadata_path_;
245 // When not LOAD_COMPLETE, the context is waiting for a pending read operation
246 // to complete. While this is the case, all added download items are observed
247 // and events are temporarily recorded in |pending_items_|. Once the read
248 // completes, pending operations for the item correponding to the metadata
249 // file are 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. The context is moved to the
252 // DETACHED_WAIT state if the corresponding DownloadManager goes away while a
253 // read operation is outstanding. When the read subsequently completes, the
254 // context is destroyed after the processing described above is performed.
255 State state_;
257 // The current metadata for the context. May be supplied either by reading
258 // from the file or by having been set via |SetRequest|.
259 scoped_ptr<DownloadMetadata> download_metadata_;
261 // The operation data that accumulates for added download items while the
262 // metadata file is being read.
263 ItemDataMap pending_items_;
265 // The download item corresponding to the download_metadata_. When non-null,
266 // the context observes events on this item only.
267 content::DownloadItem* item_;
269 // Pending callbacks in response to GetDownloadDetails. The callbacks are run
270 // in order when a pending read operation completes.
271 std::list<GetDownloadDetailsCallback> get_details_callbacks_;
273 base::WeakPtrFactory<ManagerContext> weak_factory_;
275 DISALLOW_COPY_AND_ASSIGN(ManagerContext);
278 DownloadMetadataManager::DownloadMetadataManager(
279 const scoped_refptr<base::SequencedWorkerPool>& worker_pool) {
280 base::SequencedWorkerPool::SequenceToken token(
281 worker_pool->GetSequenceToken());
282 // Do not block shutdown on reads since nothing will come of it.
283 read_runner_ = worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
284 token, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
285 // Block shutdown on writes only if they've already begun.
286 write_runner_ = worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
287 token, base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
290 DownloadMetadataManager::DownloadMetadataManager(
291 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
292 : read_runner_(task_runner), write_runner_(task_runner) {
295 DownloadMetadataManager::~DownloadMetadataManager() {
296 // Destruction may have taken place before managers have gone down.
297 for (const auto& value : contexts_) {
298 value.first->RemoveObserver(this);
299 value.second->Detach();
301 contexts_.clear();
304 void DownloadMetadataManager::AddDownloadManager(
305 content::DownloadManager* download_manager) {
306 DCHECK_EQ(contexts_.count(download_manager), 0U);
307 download_manager->AddObserver(this);
308 contexts_[download_manager] =
309 new ManagerContext(read_runner_, write_runner_, download_manager);
312 void DownloadMetadataManager::SetRequest(content::DownloadItem* item,
313 const ClientDownloadRequest* request) {
314 content::DownloadManager* download_manager =
315 GetDownloadManagerForBrowserContext(item->GetBrowserContext());
316 DCHECK_EQ(contexts_.count(download_manager), 1U);
317 if (request)
318 contexts_[download_manager]->SetRequest(item, *request);
319 else
320 contexts_[download_manager]->RemoveMetadata();
323 void DownloadMetadataManager::GetDownloadDetails(
324 content::BrowserContext* browser_context,
325 const GetDownloadDetailsCallback& callback) {
326 DCHECK(browser_context);
327 // The DownloadManager for |browser_context| may not have been created yet. In
328 // this case, asking for it would cause history to load in the background and
329 // wouldn't really help much. Instead, scan the contexts to see if one belongs
330 // to |browser_context|. If one is not found, read the metadata and return it.
331 scoped_ptr<ClientIncidentReport_DownloadDetails> download_details;
332 for (const auto& value : contexts_) {
333 if (value.first->GetBrowserContext() == browser_context) {
334 value.second->GetDownloadDetails(callback);
335 return;
339 // Fire off a task to load the details and return them to the caller.
340 DownloadMetadata* metadata = new DownloadMetadata();
341 read_runner_->PostTaskAndReply(
342 FROM_HERE,
343 base::Bind(&ReadMetadataOnWorkerPool,
344 GetMetadataPath(browser_context),
345 metadata),
346 base::Bind(
347 &ReturnResults, callback, base::Passed(make_scoped_ptr(metadata))));
350 content::DownloadManager*
351 DownloadMetadataManager::GetDownloadManagerForBrowserContext(
352 content::BrowserContext* context) {
353 return content::BrowserContext::GetDownloadManager(context);
356 void DownloadMetadataManager::OnDownloadCreated(
357 content::DownloadManager* download_manager,
358 content::DownloadItem* item) {
359 DCHECK_EQ(contexts_.count(download_manager), 1U);
360 contexts_[download_manager]->OnDownloadCreated(item);
363 void DownloadMetadataManager::ManagerGoingDown(
364 content::DownloadManager* download_manager) {
365 DCHECK_EQ(contexts_.count(download_manager), 1U);
366 auto iter = contexts_.find(download_manager);
367 iter->second->Detach();
368 contexts_.erase(iter);
371 DownloadMetadataManager::ManagerContext::ManagerContext(
372 const scoped_refptr<base::SequencedTaskRunner>& read_runner,
373 const scoped_refptr<base::SequencedTaskRunner>& write_runner,
374 content::DownloadManager* download_manager)
375 : read_runner_(read_runner),
376 write_runner_(write_runner),
377 metadata_path_(GetMetadataPath(download_manager->GetBrowserContext())),
378 state_(WAITING_FOR_LOAD),
379 item_(),
380 weak_factory_(this) {
381 // Start the asynchronous task to read the persistent metadata.
382 ReadMetadata();
385 void DownloadMetadataManager::ManagerContext::Detach() {
386 // Delete the instance immediately if there's no work to process after a
387 // pending read completes.
388 if (get_details_callbacks_.empty() && pending_items_.empty()) {
389 delete this;
390 } else {
391 // delete the instance in OnMetadataReady.
392 state_ = DETACHED_WAIT;
396 void DownloadMetadataManager::ManagerContext::OnDownloadCreated(
397 content::DownloadItem* download) {
398 const uint32_t id = download->GetId();
399 if (state_ != LOAD_COMPLETE) {
400 // Observe all downloads and record operations while waiting for metadata to
401 // load.
402 download->AddObserver(this);
403 pending_items_[id].item = download;
404 } else if (id == GetDownloadId()) {
405 // Observe this one item if it is the important one.
406 DCHECK_EQ(item_, static_cast<content::DownloadItem*>(nullptr));
407 item_ = download;
408 download->AddObserver(this);
412 void DownloadMetadataManager::ManagerContext::SetRequest(
413 content::DownloadItem* download,
414 const ClientDownloadRequest& request) {
415 if (state_ != LOAD_COMPLETE) {
416 // Abandon the read task since |download| is the new top dog.
417 weak_factory_.InvalidateWeakPtrs();
418 state_ = LOAD_COMPLETE;
419 // Stop observing all items and drop any recorded operations.
420 ClearPendingItems();
422 // Observe this item from here on out.
423 if (item_ != download) {
424 if (item_)
425 item_->RemoveObserver(this);
426 download->AddObserver(this);
427 item_ = download;
429 // Take the request.
430 download_metadata_.reset(new DownloadMetadata);
431 download_metadata_->set_download_id(download->GetId());
432 download_metadata_->mutable_download()->mutable_download()->CopyFrom(request);
433 base::Time end_time = download->GetEndTime();
434 // The item may have yet to be marked as complete, so consider the current
435 // time as being its download time.
436 if (end_time.is_null())
437 end_time = base::Time::Now();
438 download_metadata_->mutable_download()->set_download_time_msec(
439 end_time.ToJavaTime());
440 // Persist it.
441 WriteMetadata();
442 // Run callbacks (only present in case of a transition to LOAD_COMPLETE).
443 RunCallbacks();
446 void DownloadMetadataManager::ManagerContext::RemoveMetadata() {
447 if (state_ != LOAD_COMPLETE) {
448 // Abandon the read task since the file is to be removed.
449 weak_factory_.InvalidateWeakPtrs();
450 state_ = LOAD_COMPLETE;
451 // Stop observing all items and drop any recorded operations.
452 ClearPendingItems();
454 // Remove any metadata.
455 download_metadata_.reset();
456 // Stop observing this item.
457 if (item_) {
458 item_->RemoveObserver(this);
459 item_= nullptr;
461 write_runner_->PostTask(
462 FROM_HERE, base::Bind(&DeleteMetadataOnWorkerPool, metadata_path_));
463 // Run callbacks (only present in case of a transition to LOAD_COMPLETE).
464 RunCallbacks();
467 void DownloadMetadataManager::ManagerContext::GetDownloadDetails(
468 const GetDownloadDetailsCallback& callback) {
469 if (state_ != LOAD_COMPLETE) {
470 get_details_callbacks_.push_back(callback);
471 } else {
472 if (download_metadata_) {
473 callback.Run(make_scoped_ptr(new ClientIncidentReport_DownloadDetails(
474 download_metadata_->download())).Pass());
475 } else {
476 callback.Run(scoped_ptr<ClientIncidentReport_DownloadDetails>());
481 void DownloadMetadataManager::ManagerContext::OnDownloadOpened(
482 content::DownloadItem* download) {
483 const base::Time now = base::Time::Now();
484 if (state_ != LOAD_COMPLETE)
485 pending_items_[download->GetId()].last_opened_time = now;
486 else
487 UpdateLastOpenedTime(now);
490 void DownloadMetadataManager::ManagerContext::OnDownloadRemoved(
491 content::DownloadItem* download) {
492 if (state_ != LOAD_COMPLETE)
493 pending_items_[download->GetId()].removed = true;
494 else
495 RemoveMetadata();
498 void DownloadMetadataManager::ManagerContext::OnDownloadDestroyed(
499 content::DownloadItem* download) {
500 if (state_ != LOAD_COMPLETE) {
501 // Erase the data for this item if nothing of import happened. Otherwise
502 // clear its item pointer since it is now invalid.
503 auto iter = pending_items_.find(download->GetId());
504 DCHECK(iter != pending_items_.end());
505 if (!iter->second.removed && iter->second.last_opened_time.is_null())
506 pending_items_.erase(iter);
507 else
508 iter->second.item = nullptr;
509 } else if (item_) {
510 // This item is no longer being observed.
511 DCHECK_EQ(item_, download);
512 item_ = nullptr;
516 DownloadMetadataManager::ManagerContext::~ManagerContext() {
517 // A context should not be deleted while waiting for a load to complete.
518 DCHECK(pending_items_.empty());
519 DCHECK(get_details_callbacks_.empty());
521 // The context may have detached while still observing the item of interest
522 // since a DownloadManager announces that it's going down before it destroyes
523 // its items.
524 if (item_) {
525 item_->RemoveObserver(this);
526 item_ = nullptr;
530 void DownloadMetadataManager::ManagerContext::ClearPendingItems() {
531 for (const auto& value : pending_items_) {
532 if (value.second.item)
533 value.second.item->RemoveObserver(this);
535 pending_items_.clear();
538 void DownloadMetadataManager::ManagerContext::RunCallbacks() {
539 while (!get_details_callbacks_.empty()) {
540 const auto& callback = get_details_callbacks_.front();
541 if (download_metadata_) {
542 callback.Run(make_scoped_ptr(new ClientIncidentReport_DownloadDetails(
543 download_metadata_->download())).Pass());
544 } else {
545 callback.Run(scoped_ptr<ClientIncidentReport_DownloadDetails>());
547 get_details_callbacks_.pop_front();
551 uint32_t DownloadMetadataManager::ManagerContext::GetDownloadId() const {
552 if (state_ != LOAD_COMPLETE || !download_metadata_)
553 return content::DownloadItem::kInvalidId;
554 return download_metadata_->download_id();
557 void DownloadMetadataManager::ManagerContext::ReadMetadata() {
558 DCHECK_NE(state_, LOAD_COMPLETE);
560 DownloadMetadata* metadata = new DownloadMetadata();
561 // Do not block shutdown on this read since nothing will come of it.
562 read_runner_->PostTaskAndReply(
563 FROM_HERE,
564 base::Bind(&ReadMetadataOnWorkerPool, metadata_path_, metadata),
565 base::Bind(&DownloadMetadataManager::ManagerContext::OnMetadataReady,
566 weak_factory_.GetWeakPtr(),
567 base::Passed(make_scoped_ptr(metadata))));
570 void DownloadMetadataManager::ManagerContext::OnMetadataReady(
571 scoped_ptr<DownloadMetadata> download_metadata) {
572 DCHECK_NE(state_, LOAD_COMPLETE);
574 const bool is_detached = (state_ == DETACHED_WAIT);
576 // Note that any available data has been read.
577 state_ = LOAD_COMPLETE;
578 if (download_metadata->has_download_id())
579 download_metadata_ = download_metadata.Pass();
580 else
581 download_metadata_.reset();
583 // Process all operations that had been held while waiting for the metadata.
584 content::DownloadItem* download = nullptr;
586 const auto& iter = pending_items_.find(GetDownloadId());
587 if (iter != pending_items_.end()) {
588 const ItemData& item_data = iter->second;
589 download = item_data.item; // non-null if not destroyed.
590 if (item_data.removed) {
591 RemoveMetadata();
592 download = nullptr;
593 } else if (!item_data.last_opened_time.is_null()) {
594 UpdateLastOpenedTime(item_data.last_opened_time);
599 // Stop observing all items.
600 ClearPendingItems();
602 // If the download was known and not removed, observe it from here on out.
603 if (download) {
604 download->AddObserver(this);
605 item_ = download;
608 // Run callbacks.
609 RunCallbacks();
611 // Delete the context now if it has been detached.
612 if (is_detached)
613 delete this;
616 void DownloadMetadataManager::ManagerContext::UpdateLastOpenedTime(
617 const base::Time& last_opened_time) {
618 download_metadata_->mutable_download()->set_open_time_msec(
619 last_opened_time.ToJavaTime());
620 WriteMetadata();
623 void DownloadMetadataManager::ManagerContext::WriteMetadata() {
624 write_runner_->PostTask(
625 FROM_HERE,
626 base::Bind(&WriteMetadataOnWorkerPool,
627 metadata_path_,
628 base::Owned(new DownloadMetadata(*download_metadata_))));
631 } // namespace safe_browsing