Chromium Code Reviews| 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 |