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 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()); |
| 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; |
| 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; |
| 87 } |
| 88 } else { |
| 89 result = FILE_TOO_BIG; |
| 90 } |
| 91 } else { |
| 92 result = GET_INFO_FAILURE; |
| 93 } |
| 94 } else if (metadata_file.error_details() != File::FILE_ERROR_NOT_FOUND) { |
| 95 result = OPEN_FAILURE; |
| 96 } else { |
| 97 result = NOT_FOUND; |
| 98 } |
| 99 if (result != READ_SUCCESS) |
| 100 metadata->Clear(); |
| 101 UMA_HISTOGRAM_ENUMERATION( |
| 102 "SBIRS.DownloadMetadataReadResult", result, NUM_READ_RESULTS); |
| 103 } |
| 104 |
| 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; |
| 117 } |
| 118 UMA_HISTOGRAM_ENUMERATION( |
| 119 "SBIRS.DownloadMetadataWriteResult", result, NUM_WRITE_RESULTS); |
| 120 } |
| 121 |
| 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.DownloadMetadataDeleteSuccess", success); |
| 126 } |
| 127 |
| 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()); |
| 136 } |
| 137 |
| 138 } // namespace |
| 139 |
| 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 cosnumers 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); |
| 157 |
| 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(); |
| 163 |
| 164 // Notifies the context that |download| has been added to its manager. |
| 165 void OnDownloadCreated(content::DownloadItem* download); |
| 166 |
| 167 // Sets |request| as the relevant metadata to persist for |download|. |
| 168 void SetRequest(content::DownloadItem* download, |
| 169 const ClientDownloadRequest& request); |
| 170 |
| 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(); |
| 174 |
| 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); |
| 179 |
| 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; |
| 185 |
| 186 private: |
| 187 enum State { |
| 188 // The context is waiting for the metadata file to be loaded. |
| 189 WAITING_FOR_LOAD, |
| 190 |
| 191 // The context is waiting for the metadata file to be loaded and its |
| 192 // corresponding DownloadManager has gone away. |
| 193 DETACHED_WAIT, |
| 194 |
| 195 // The context has loaded the metadata file. |
| 196 LOAD_COMPLETE, |
| 197 }; |
| 198 |
| 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; |
| 206 }; |
| 207 |
| 208 // A mapping of download IDs to their corresponding data. |
| 209 typedef std::map<uint32_t, ItemData> ItemDataMap; |
| 210 |
| 211 virtual ~ManagerContext(); |
| 212 |
| 213 // Clears the |pending_items_| mapping, removing observers in the process. |
| 214 void ClearPendingItems(); |
| 215 |
| 216 // Runs all |get_details_callbacks_| with the current metadata. |
| 217 void RunCallbacks(); |
| 218 |
| 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; |
| 222 |
| 223 // Posts a task in the worker pool to read the metadata from disk. |
| 224 void ReadMetadata(); |
| 225 |
| 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); |
| 229 |
| 230 // Updates the last opened time in the metadata and writes it to disk. |
| 231 void UpdateLastOpenedTime(const base::Time& last_opened_time); |
| 232 |
| 233 // Posts a task in the worker pool to write the metadata to disk. |
| 234 void WriteMetadata(); |
| 235 |
| 236 // A task runner to which read tasks are posted. |
| 237 scoped_refptr<base::SequencedTaskRunner> read_runner_; |
| 238 |
| 239 // A task runner to which write tasks are posted. |
| 240 scoped_refptr<base::SequencedTaskRunner> write_runner_; |
| 241 |
| 242 // The path to the metadata file for this context. |
| 243 base::FilePath metadata_path_; |
| 244 |
| 245 // When true, the context is waiting for a pending read operation to complete. |
| 246 // While this is the case, all added download items are observed and events |
| 247 // are temporarily recorded in |pending_items_|. Once the read completes, |
| 248 // pending operations for the item correponding to the metadata file are |
| 249 // 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. |
| 252 State state_; |
| 253 |
| 254 // The current metadata for the context. May be supplied either by reading |
| 255 // from the file or by having been set via |SetRequest|. |
| 256 scoped_ptr<DownloadMetadata> download_metadata_; |
| 257 |
| 258 // The operation data that accumulates for added download items while the |
| 259 // metadata file is being read. |
| 260 ItemDataMap pending_items_; |
| 261 |
| 262 // The download item corresponding to the download_metadata_. When non-null, |
| 263 // the context observes events on this item only. |
| 264 content::DownloadItem* item_; |
| 265 |
| 266 // Pending callbacks in response to GetDownloadDetails. The callbacks are run |
| 267 // in order when a pending read operation completes. |
| 268 std::list<GetDownloadDetailsCallback> get_details_callbacks_; |
| 269 |
| 270 base::WeakPtrFactory<ManagerContext> weak_factory_; |
| 271 |
| 272 DISALLOW_COPY_AND_ASSIGN(ManagerContext); |
| 273 }; |
| 274 |
| 275 DownloadMetadataManager::DownloadMetadataManager( |
| 276 const scoped_refptr<base::SequencedWorkerPool>& worker_pool) { |
| 277 base::SequencedWorkerPool::SequenceToken token( |
| 278 worker_pool->GetSequenceToken()); |
| 279 // Do not block shutdown on reads since nothing will come of it. |
| 280 read_runner_ = worker_pool->GetSequencedTaskRunnerWithShutdownBehavior( |
| 281 token, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); |
| 282 // Block shutdown on writes in the hopes of persisting important data. |
| 283 write_runner_ = worker_pool->GetSequencedTaskRunnerWithShutdownBehavior( |
| 284 token, base::SequencedWorkerPool::BLOCK_SHUTDOWN); |
| 285 } |
| 286 |
| 287 DownloadMetadataManager::DownloadMetadataManager( |
| 288 const scoped_refptr<base::SequencedTaskRunner>& task_runner) |
| 289 : read_runner_(task_runner), |
| 290 write_runner_(task_runner) { |
| 291 } |
| 292 |
| 293 DownloadMetadataManager::~DownloadMetadataManager() { |
| 294 // All download managers must have gone down prior to this. |
| 295 DCHECK(contexts_.empty()); |
| 296 } |
| 297 |
| 298 void DownloadMetadataManager::AddDownloadManager( |
| 299 content::DownloadManager* download_manager) { |
| 300 DCHECK_EQ(contexts_.count(download_manager), 0U); |
| 301 download_manager->AddObserver(this); |
| 302 contexts_[download_manager] = |
| 303 new ManagerContext(read_runner_, write_runner_, download_manager); |
| 304 } |
| 305 |
| 306 void DownloadMetadataManager::SetRequest(content::DownloadItem* item, |
| 307 const ClientDownloadRequest* request) { |
| 308 content::DownloadManager* download_manager = |
| 309 GetDownloadManagerForBrowserContext(item->GetBrowserContext()); |
| 310 DCHECK_EQ(contexts_.count(download_manager), 1U); |
| 311 if (request) |
| 312 contexts_[download_manager]->SetRequest(item, *request); |
| 313 else |
| 314 contexts_[download_manager]->RemoveMetadata(); |
| 315 } |
| 316 |
| 317 void DownloadMetadataManager::GetDownloadDetails( |
| 318 content::BrowserContext* browser_context, |
| 319 const GetDownloadDetailsCallback& callback) { |
| 320 DCHECK(browser_context); |
| 321 // The DownloadManager for |browser_context| may not have been created yet. In |
| 322 // this case, asking for it would cause history to load in the background and |
| 323 // wouldn't really help much. Instead, scan the contexts to see if one belongs |
| 324 // to |browser_context|. If one is not found, read the metadata and return it. |
| 325 scoped_ptr<ClientIncidentReport_DownloadDetails> download_details; |
| 326 for (const auto& value : contexts_) { |
| 327 if (value.first->GetBrowserContext() == browser_context) { |
| 328 value.second->GetDownloadDetails(callback); |
| 329 return; |
| 330 } |
| 331 } |
| 332 |
| 333 // Fire off a task to load the details and return them to the caller. |
| 334 DownloadMetadata* metadata = new DownloadMetadata(); |
| 335 read_runner_->PostTaskAndReply( |
| 336 FROM_HERE, |
| 337 base::Bind(&ReadMetadataOnWorkerPool, |
| 338 GetMetadataPath(browser_context), |
| 339 metadata), |
| 340 base::Bind( |
| 341 &ReturnResults, callback, base::Passed(make_scoped_ptr(metadata)))); |
| 342 } |
| 343 |
| 344 content::DownloadManager* |
| 345 DownloadMetadataManager::GetDownloadManagerForBrowserContext( |
| 346 content::BrowserContext* context) { |
| 347 return content::BrowserContext::GetDownloadManager(context); |
| 348 } |
| 349 |
| 350 void DownloadMetadataManager::OnDownloadCreated( |
| 351 content::DownloadManager* download_manager, |
| 352 content::DownloadItem* item) { |
| 353 DCHECK_EQ(contexts_.count(download_manager), 1U); |
| 354 contexts_[download_manager]->OnDownloadCreated(item); |
| 355 } |
| 356 |
| 357 void DownloadMetadataManager::ManagerGoingDown( |
| 358 content::DownloadManager* download_manager) { |
| 359 DCHECK_EQ(contexts_.count(download_manager), 1U); |
| 360 auto iter = contexts_.find(download_manager); |
| 361 iter->second->Detach(); |
| 362 contexts_.erase(iter); |
| 363 } |
| 364 |
| 365 DownloadMetadataManager::ManagerContext::ManagerContext( |
| 366 const scoped_refptr<base::SequencedTaskRunner>& read_runner, |
| 367 const scoped_refptr<base::SequencedTaskRunner>& write_runner, |
| 368 content::DownloadManager* download_manager) |
| 369 : read_runner_(read_runner), |
| 370 write_runner_(write_runner), |
| 371 metadata_path_(GetMetadataPath(download_manager->GetBrowserContext())), |
| 372 state_(WAITING_FOR_LOAD), |
| 373 item_(), |
| 374 weak_factory_(this) { |
| 375 // Start the asynchronous task to read the persistent metadata. |
| 376 ReadMetadata(); |
| 377 } |
| 378 |
| 379 void DownloadMetadataManager::ManagerContext::Detach() { |
| 380 // Delete the instance immediately if there's no work to process after a |
| 381 // pending read completes. |
| 382 if (state_ == LOAD_COMPLETE || |
| 383 (get_details_callbacks_.empty() && pending_items_.empty())) { |
| 384 delete this; |
| 385 } else { |
| 386 // delete the instance in OnMetadataReady. |
| 387 state_ = DETACHED_WAIT; |
| 388 } |
| 389 } |
| 390 |
| 391 void DownloadMetadataManager::ManagerContext::OnDownloadCreated( |
| 392 content::DownloadItem* download) { |
| 393 const uint32_t id = download->GetId(); |
| 394 if (state_ != LOAD_COMPLETE) { |
| 395 // Observe all downloads and record operations while waiting for metadata to |
| 396 // load. |
| 397 download->AddObserver(this); |
| 398 pending_items_[id].item = download; |
| 399 } else if (id == GetDownloadId()) { |
| 400 // Observe this one item if it is the important one. |
| 401 DCHECK_EQ(item_, static_cast<content::DownloadItem*>(nullptr)); |
| 402 item_ = download; |
| 403 download->AddObserver(this); |
| 404 } |
| 405 } |
| 406 |
| 407 void DownloadMetadataManager::ManagerContext::SetRequest( |
| 408 content::DownloadItem* download, |
| 409 const ClientDownloadRequest& request) { |
| 410 if (state_ != LOAD_COMPLETE) { |
| 411 // Abandon the read task since |download| is the new top dog. |
| 412 weak_factory_.InvalidateWeakPtrs(); |
| 413 state_ = LOAD_COMPLETE; |
| 414 // Stop observing all items and drop any recorded operations. |
| 415 ClearPendingItems(); |
| 416 } |
| 417 // Observe this item from here on out. |
| 418 if (item_ != download) { |
| 419 if (item_) |
| 420 item_->RemoveObserver(this); |
| 421 download->AddObserver(this); |
| 422 item_ = download; |
| 423 } |
| 424 // Take the request. |
| 425 download_metadata_.reset(new DownloadMetadata); |
| 426 download_metadata_->set_download_id(download->GetId()); |
| 427 download_metadata_->mutable_download()->mutable_download()->CopyFrom(request); |
| 428 base::Time end_time = download->GetEndTime(); |
| 429 // The item may have yet to be marked as complete, so consider the current |
| 430 // time as being its download time. |
| 431 if (end_time.is_null()) |
| 432 end_time = base::Time::Now(); |
| 433 download_metadata_->mutable_download()->set_download_time_msec( |
| 434 end_time.ToJavaTime()); |
| 435 // Persist it. |
| 436 WriteMetadata(); |
| 437 // Run callbacks (only present in case of a transition to LOAD_COMPLETE). |
| 438 RunCallbacks(); |
| 439 } |
| 440 |
| 441 void DownloadMetadataManager::ManagerContext::RemoveMetadata() { |
| 442 if (state_ != LOAD_COMPLETE) { |
| 443 // Abandon the read task since the file is to be removed. |
| 444 weak_factory_.InvalidateWeakPtrs(); |
| 445 state_ = LOAD_COMPLETE; |
| 446 // Stop observing all items and drop any recorded operations. |
| 447 ClearPendingItems(); |
| 448 } |
| 449 // Remove any metadata. |
| 450 download_metadata_.reset(); |
| 451 write_runner_->PostTask( |
| 452 FROM_HERE, base::Bind(&DeleteMetadataOnWorkerPool, metadata_path_)); |
| 453 // Run callbacks (only present in case of a transition to LOAD_COMPLETE). |
| 454 RunCallbacks(); |
| 455 } |
| 456 |
| 457 void DownloadMetadataManager::ManagerContext::GetDownloadDetails( |
| 458 const GetDownloadDetailsCallback& callback) { |
| 459 if (state_ != LOAD_COMPLETE) { |
| 460 get_details_callbacks_.push_back(callback); |
| 461 } else { |
| 462 if (download_metadata_) { |
| 463 callback.Run(make_scoped_ptr(new ClientIncidentReport_DownloadDetails( |
| 464 download_metadata_->download())).Pass()); |
| 465 } else { |
| 466 callback.Run(scoped_ptr<ClientIncidentReport_DownloadDetails>()); |
| 467 } |
| 468 } |
| 469 } |
| 470 |
| 471 void DownloadMetadataManager::ManagerContext::OnDownloadOpened( |
| 472 content::DownloadItem* download) { |
| 473 const base::Time now = base::Time::Now(); |
| 474 if (state_ != LOAD_COMPLETE) |
| 475 pending_items_[download->GetId()].last_opened_time = now; |
| 476 else |
| 477 UpdateLastOpenedTime(now); |
| 478 } |
| 479 |
| 480 void DownloadMetadataManager::ManagerContext::OnDownloadRemoved( |
| 481 content::DownloadItem* download) { |
| 482 if (state_ != LOAD_COMPLETE) |
| 483 pending_items_[download->GetId()].removed = true; |
| 484 else |
| 485 RemoveMetadata(); |
| 486 } |
| 487 |
| 488 void DownloadMetadataManager::ManagerContext::OnDownloadDestroyed( |
| 489 content::DownloadItem* download) { |
| 490 if (state_ != LOAD_COMPLETE) { |
| 491 // Erase the data for this item if nothing of import happened. Otherwise |
| 492 // clear its item pointer since it is now invalid. |
| 493 auto iter = pending_items_.find(download->GetId()); |
| 494 DCHECK(iter != pending_items_.end()); |
| 495 if (!iter->second.removed && iter->second.last_opened_time.is_null()) |
| 496 pending_items_.erase(iter); |
| 497 else |
| 498 iter->second.item = nullptr; |
| 499 } else if (item_) { |
| 500 // This item is no longer being observed. |
| 501 DCHECK_EQ(item_, download); |
| 502 item_ = nullptr; |
| 503 } |
| 504 } |
| 505 |
| 506 DownloadMetadataManager::ManagerContext::~ManagerContext() { |
| 507 // All downloads must have been destroyed prior to deleting the context and |
| 508 // all recorded operations and callbacks must have been handled. |
| 509 DCHECK(pending_items_.empty()); |
| 510 DCHECK_EQ(item_, static_cast<content::DownloadItem*>(nullptr)); |
| 511 DCHECK(get_details_callbacks_.empty()); |
| 512 } |
| 513 |
| 514 void DownloadMetadataManager::ManagerContext::ClearPendingItems() { |
| 515 for (const auto& value : pending_items_) { |
| 516 if (value.second.item) |
| 517 value.second.item->RemoveObserver(this); |
| 518 } |
| 519 pending_items_.clear(); |
| 520 } |
| 521 |
| 522 void DownloadMetadataManager::ManagerContext::RunCallbacks() { |
| 523 while (!get_details_callbacks_.empty()) { |
| 524 const auto& callback = get_details_callbacks_.front(); |
| 525 if (download_metadata_) { |
| 526 callback.Run(make_scoped_ptr(new ClientIncidentReport_DownloadDetails( |
| 527 download_metadata_->download())).Pass()); |
| 528 } else { |
| 529 callback.Run(scoped_ptr<ClientIncidentReport_DownloadDetails>()); |
| 530 } |
| 531 get_details_callbacks_.pop_front(); |
| 532 } |
| 533 } |
| 534 |
| 535 uint32_t DownloadMetadataManager::ManagerContext::GetDownloadId() const { |
| 536 if (state_ != LOAD_COMPLETE || !download_metadata_) |
| 537 return content::DownloadItem::kInvalidId; |
| 538 return download_metadata_->download_id(); |
| 539 } |
| 540 |
| 541 void DownloadMetadataManager::ManagerContext::ReadMetadata() { |
| 542 DCHECK_NE(state_, LOAD_COMPLETE); |
| 543 |
| 544 DownloadMetadata* metadata = new DownloadMetadata(); |
| 545 // Do not block shutdown on this read since nothing will come of it. |
| 546 read_runner_->PostTaskAndReply( |
| 547 FROM_HERE, |
| 548 base::Bind(&ReadMetadataOnWorkerPool, metadata_path_, metadata), |
| 549 base::Bind(&DownloadMetadataManager::ManagerContext::OnMetadataReady, |
| 550 weak_factory_.GetWeakPtr(), |
| 551 base::Passed(make_scoped_ptr(metadata)))); |
| 552 } |
| 553 |
| 554 void DownloadMetadataManager::ManagerContext::OnMetadataReady( |
| 555 scoped_ptr<DownloadMetadata> download_metadata) { |
| 556 DCHECK_NE(state_, LOAD_COMPLETE); |
| 557 |
| 558 const bool is_detached = (state_ == DETACHED_WAIT); |
| 559 |
| 560 // Note that any available data has been read. |
| 561 state_ = LOAD_COMPLETE; |
| 562 if (download_metadata->has_download_id()) |
| 563 download_metadata_ = download_metadata.Pass(); |
| 564 else |
| 565 download_metadata_.reset(); |
| 566 |
| 567 // Process all operations that had been held while waiting for the metadata. |
| 568 content::DownloadItem* download = nullptr; |
| 569 { |
| 570 const auto& iter = pending_items_.find(GetDownloadId()); |
| 571 if (iter != pending_items_.end()) { |
| 572 const ItemData& item_data = iter->second; |
| 573 download = item_data.item; // non-null if not destroyed. |
| 574 if (item_data.removed) |
| 575 RemoveMetadata(); |
| 576 else if (!item_data.last_opened_time.is_null()) |
| 577 UpdateLastOpenedTime(item_data.last_opened_time); |
| 578 } |
| 579 } |
| 580 |
| 581 // Stop observing all items. |
| 582 ClearPendingItems(); |
| 583 |
| 584 // If the download was known, observe it from here on out. |
| 585 if (download) { |
| 586 download->AddObserver(this); |
| 587 item_ = download; |
| 588 } |
| 589 |
| 590 // Run callbacks. |
| 591 RunCallbacks(); |
| 592 |
| 593 // Delete the context now if it has been detached. |
| 594 if (is_detached) |
| 595 delete this; |
| 596 } |
| 597 |
| 598 void DownloadMetadataManager::ManagerContext::UpdateLastOpenedTime( |
| 599 const base::Time& last_opened_time) { |
| 600 download_metadata_->mutable_download()->set_open_time_msec( |
| 601 last_opened_time.ToJavaTime()); |
| 602 WriteMetadata(); |
| 603 } |
| 604 |
| 605 void DownloadMetadataManager::ManagerContext::WriteMetadata() { |
| 606 write_runner_->PostTask( |
| 607 FROM_HERE, |
| 608 base::Bind(&WriteMetadataOnWorkerPool, |
| 609 metadata_path_, |
| 610 base::Owned(new DownloadMetadata(*download_metadata_)))); |
| 611 } |
| 612 |
| 613 } // namespace safe_browsing |
OLD | NEW |