 Chromium Code Reviews
 Chromium Code Reviews Issue 10665049:
  Make DownloadHistory observe manager, items  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src
    
  
    Issue 10665049:
  Make DownloadHistory observe manager, items  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src| OLD | NEW | 
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 // DownloadHistory manages persisting DownloadItems to the history service by | |
| 6 // observing a single DownloadManager and all its DownloadItems. | |
| 7 // DownloadHistory decides whether and when to add items to, remove items from, | |
| 8 // and update items in the database. DownloadHistory uses DownloadHistoryData to | |
| 9 // store per-DownloadItem data such as its db_handle, whether the item is being | |
| 10 // added and waiting for its db_handle, and the last DownloadPersistentStoreInfo | |
| 11 // that was passed to the database. When the DownloadManager and its delegate | |
| 12 // (ChromeDownloadManagerDelegate) are initialized, DownloadHistory is created | |
| 13 // and queries the HistoryService. When the HistoryService calls back from | |
| 14 // QueryDownloads() to QueryCallback(), DownloadHistory uses | |
| 15 // DownloadManager::CreateDownloadItem() to inform DownloadManager of these | |
| 16 // persisted DownloadItems. CreateDownloadItem() internally calls | |
| 17 // OnDownloadCreated(), which normally adds items to the database, so | |
| 18 // QueryCallback() uses |loading_| to disable adding items to the database. If | |
| 19 // a download is removed via OnDownloadRemoved() while the item is still being | |
| 20 // added to the database, DownloadHistory uses |removed_while_adding_| to | |
| 21 // remember to remove the item when its ItemAdded() callback is called. All | |
| 22 // callbacks are bound with a weak pointer to DownloadHistory to prevent | |
| 23 // use-after-free bugs. | |
| 24 // ChromeDownloadManagerDelegate owns DownloadHistory, and deletes it in | |
| 25 // Shutdown(), which is called by DownloadManagerImpl::Shutdown() after all | |
| 26 // DownloadItems are destroyed. | |
| 27 | |
| 5 #include "chrome/browser/download/download_history.h" | 28 #include "chrome/browser/download/download_history.h" | 
| 6 | 29 | 
| 7 #include "base/logging.h" | 30 #include "base/metrics/histogram.h" | 
| 31 #include "chrome/browser/cancelable_request.h" | |
| 8 #include "chrome/browser/download/download_crx_util.h" | 32 #include "chrome/browser/download/download_crx_util.h" | 
| 9 #include "chrome/browser/history/history_marshaling.h" | 33 #include "chrome/browser/history/download_persistent_store_info.h" | 
| 10 #include "chrome/browser/history/history_service_factory.h" | 34 #include "content/public/browser/browser_thread.h" | 
| 11 #include "chrome/browser/profiles/profile.h" | |
| 12 #include "content/public/browser/download_item.h" | 35 #include "content/public/browser/download_item.h" | 
| 13 #include "content/public/browser/download_persistent_store_info.h" | 36 #include "content/public/browser/download_manager.h" | 
| 14 | 37 | 
| 38 using content::BrowserThread; | |
| 15 using content::DownloadItem; | 39 using content::DownloadItem; | 
| 16 using content::DownloadPersistentStoreInfo; | 40 using content::DownloadManager; | 
| 17 | 41 | 
| 18 DownloadHistory::DownloadHistory(Profile* profile) | 42 namespace { | 
| 19 : profile_(profile), | 43 | 
| 20 next_fake_db_handle_(DownloadItem::kUninitializedHandle - 1) { | 44 // The value of |db_handle| indicating that the associated DownloadItem is not | 
| 21 DCHECK(profile); | 45 // yet persisted. | 
| 22 } | 46 static const int64 kUninitializedHandle = -1; | 
| 23 | 47 | 
| 24 DownloadHistory::~DownloadHistory() {} | 48 // Per-DownloadItem data. This information does not belong inside DownloadItem, | 
| 25 | 49 // and keeping maps in DownloadHistory from DownloadItem to this information is | 
| 26 void DownloadHistory::GetNextId( | 50 // error-prone and complicated. Unfortunately, DownloadHistory::removing_ and | 
| 27 const HistoryService::DownloadNextIdCallback& callback) { | 51 // removed_while_adding_ cannot be moved into this class partly because | 
| 28 HistoryService* hs = HistoryServiceFactory::GetForProfile( | 52 // DownloadHistoryData is destroyed when DownloadItems are destroyed, and we | 
| 29 profile_, Profile::EXPLICIT_ACCESS); | 53 // have no control over when DownloadItems are destroyed. | 
| 30 if (!hs) | 54 class DownloadHistoryData : public base::SupportsUserData::Data { | 
| 31 return; | 55 public: | 
| 32 | 56 static DownloadHistoryData* Get(DownloadItem* item) { | 
| 33 hs->GetNextDownloadId(&history_consumer_, callback); | 57 base::SupportsUserData::Data* data = item->GetUserData(kKey); | 
| 34 } | 58 return (data == NULL) ? NULL : | 
| 35 | 59 static_cast<DownloadHistoryData*>(data); | 
| 36 void DownloadHistory::Load( | 60 } | 
| 61 | |
| 62 explicit DownloadHistoryData(DownloadItem* item) | |
| 63 : is_adding_(false), | |
| 64 db_handle_(kUninitializedHandle), | |
| 65 info_(NULL) { | |
| 66 item->SetUserData(kKey, this); | |
| 67 } | |
| 68 | |
| 69 virtual ~DownloadHistoryData() { | |
| 70 } | |
| 71 | |
| 72 // Whether this item is currently being added to the database. | |
| 73 bool is_adding() const { return is_adding_; } | |
| 74 void set_is_adding(bool a) { is_adding_ = a; } | |
| 75 | |
| 76 // Whether this item is already persisted in the database. | |
| 77 bool is_persisted() const { return db_handle_ > kUninitializedHandle; } | |
| 
Randy Smith (Not in Mondays)
2012/08/27 18:25:11
As noted elsewhere, I'd sorta like download_databa
 
benjhayden
2012/09/10 19:02:56
Done.
 | |
| 78 | |
| 79 int64 db_handle() const { return db_handle_; } | |
| 80 void set_db_handle(int64 h) { db_handle_ = h; } | |
| 81 | |
| 82 // This allows OnDownloadUpdated() to see what changed in a DownloadItem if | |
| 83 // anything, in order to prevent writing to the database unnecessarily. It is | |
| 84 // nullified when the item is no longer in progress in order to save memory. | |
| 85 DownloadPersistentStoreInfo* info() { return info_.get(); } | |
| 86 void set_info(const DownloadPersistentStoreInfo& i) { | |
| 87 info_.reset(new DownloadPersistentStoreInfo(i)); | |
| 88 } | |
| 89 void clear_info() { | |
| 90 info_.reset(); | |
| 91 } | |
| 92 | |
| 93 private: | |
| 94 static const char kKey[]; | |
| 95 | |
| 96 bool is_adding_; | |
| 97 int64 db_handle_; | |
| 98 scoped_ptr<DownloadPersistentStoreInfo> info_; | |
| 99 | |
| 100 DISALLOW_COPY_AND_ASSIGN(DownloadHistoryData); | |
| 101 }; | |
| 102 | |
| 103 const char DownloadHistoryData::kKey[] = | |
| 104 "DownloadItem DownloadHistoryData"; | |
| 105 | |
| 106 DownloadPersistentStoreInfo GetPersistentStoreInfo(DownloadItem* item) { | |
| 107 DownloadHistoryData* dhd = DownloadHistoryData::Get(item); | |
| 108 return DownloadPersistentStoreInfo( | |
| 109 item->GetFullPath(), | |
| 110 item->GetURL(), | |
| 111 item->GetReferrerUrl(), | |
| 112 item->GetStartTime(), | |
| 113 item->GetEndTime(), | |
| 114 item->GetReceivedBytes(), | |
| 115 item->GetTotalBytes(), | |
| 116 item->GetState(), | |
| 117 ((dhd != NULL) ? dhd->db_handle() : kUninitializedHandle), | |
| 118 item->GetOpened()); | |
| 119 } | |
| 120 | |
| 121 typedef std::vector<DownloadPersistentStoreInfo> InfoVector; | |
| 122 | |
| 123 // CancelableRequestConsumer would normally ensure that callbacks from the | |
| 
Randy Smith (Not in Mondays)
2012/08/27 18:25:11
How about "The HistoryService normally uses Cancel
 
benjhayden
2012/09/10 19:02:56
Done.
 | |
| 124 // HistoryService are run on the same thread that issued the request, but | |
| 125 // DownloadHistory has opted for less magic, so it uses Ensure*OnUI to | |
| 
Randy Smith (Not in Mondays)
2012/08/27 18:25:11
"... less magic by relying on WeakPtrs instead ...
 
benjhayden
2012/09/10 19:02:56
Done.
 | |
| 126 // explicitly bounce to the UI thread. These methods are static to avoid | |
| 127 // creating WeakPtrs on the wrong thread. | |
| 
Randy Smith (Not in Mondays)
2012/08/27 18:25:11
I don't understand the weak pointer reference in t
 
benjhayden
2012/09/10 19:02:56
If these functions were methods on DownloadHistory
 | |
| 128 void EnsureQueryOnUI( | |
| 129 const base::Callback<void(scoped_ptr<InfoVector>)>& callback, | |
| 130 scoped_ptr<InfoVector> infos) { | |
| 131 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
| 132 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | |
| 133 callback, base::Passed(infos.Pass()))); | |
| 134 return; | |
| 135 } | |
| 136 callback.Run(infos.Pass()); | |
| 137 } | |
| 138 | |
| 139 void EnsureItemAddedOnUI(const base::Callback<void(int64)>& callback, | |
| 140 int64 handle) { | |
| 141 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
| 142 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | |
| 143 callback, handle)); | |
| 144 return; | |
| 145 } | |
| 146 callback.Run(handle); | |
| 147 } | |
| 148 | |
| 149 void EnsureVisitedOnUI( | |
| 150 const base::Callback<void(bool, int, base::Time)>& callback, | |
| 151 bool success, int count, base::Time first_visit) { | |
| 152 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
| 153 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | |
| 154 callback, success, count, first_visit)); | |
| 155 return; | |
| 156 } | |
| 157 callback.Run(success, count, first_visit); | |
| 158 } | |
| 159 | |
| 160 } // anonymous namespace | |
| 161 | |
| 162 HistoryServiceDownloadAdapter::HistoryServiceDownloadAdapter( | |
| 163 HistoryService* history_service) | |
| 164 : history_service_(history_service) { | |
| 165 } | |
| 166 | |
| 167 HistoryServiceDownloadAdapter::~HistoryServiceDownloadAdapter() {} | |
| 168 | |
| 169 void HistoryServiceDownloadAdapter::QueryDownloads( | |
| 37 const HistoryService::DownloadQueryCallback& callback) { | 170 const HistoryService::DownloadQueryCallback& callback) { | 
| 38 HistoryService* hs = HistoryServiceFactory::GetForProfile( | 171 history_service_->QueryDownloads(callback); | 
| 39 profile_, Profile::EXPLICIT_ACCESS); | 172 history_service_->CleanUpInProgressEntries(); | 
| 40 if (!hs) | 173 } | 
| 41 return; | 174 | 
| 42 | 175 void HistoryServiceDownloadAdapter::GetVisibleVisitCountToHost( | 
| 43 hs->QueryDownloads(&history_consumer_, callback); | 176 const GURL& referrer_url, | 
| 44 | 177 const HistoryService::GetVisibleVisitCountToHostSimpleCallback& callback) { | 
| 45 // This is the initial load, so do a cleanup of corrupt in-progress entries. | 178 history_service_->GetVisibleVisitCountToHostSimple( | 
| 46 hs->CleanUpInProgressEntries(); | 179 referrer_url, callback); | 
| 180 } | |
| 181 | |
| 182 void HistoryServiceDownloadAdapter::CreateDownload( | |
| 183 const DownloadPersistentStoreInfo& info, | |
| 184 const HistoryService::DownloadCreateCallback& callback) { | |
| 185 history_service_->CreateDownload(info, callback); | |
| 186 } | |
| 187 | |
| 188 void HistoryServiceDownloadAdapter::UpdateDownload( | |
| 189 const DownloadPersistentStoreInfo& info) { | |
| 190 history_service_->UpdateDownload(info); | |
| 191 } | |
| 192 | |
| 193 void HistoryServiceDownloadAdapter::RemoveDownloads( | |
| 194 const std::set<int64>& handles) { | |
| 195 history_service_->RemoveDownloads(handles); | |
| 196 } | |
| 197 | |
| 198 DownloadHistory::DownloadHistory( | |
| 199 DownloadManager* manager, | |
| 200 scoped_ptr<HistoryServiceDownloadAdapter> history) | |
| 201 : manager_(manager), | |
| 202 history_(history.Pass()), | |
| 203 loading_(false), | |
| 204 history_size_(0), | |
| 205 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
| 206 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 207 DCHECK(manager_); | |
| 208 manager_->AddObserver(this); | |
| 209 history_->QueryDownloads(base::Bind(&EnsureQueryOnUI, base::Bind( | |
| 210 &DownloadHistory::QueryCallback, weak_ptr_factory_.GetWeakPtr()))); | |
| 211 } | |
| 212 | |
| 213 DownloadHistory::~DownloadHistory() { | |
| 214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 215 for (ItemSet::const_iterator iter = observing_items_.begin(); | |
| 216 iter != observing_items_.end(); ++iter) { | |
| 217 (*iter)->RemoveObserver(this); | |
| 218 } | |
| 219 observing_items_.clear(); | |
| 220 if (manager_) | |
| 221 manager_->RemoveObserver(this); | |
| 222 } | |
| 223 | |
| 224 void DownloadHistory::QueryCallback(scoped_ptr<InfoVector> infos) { | |
| 225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 226 // ManagerGoingDown() may have happened before the history loaded. | |
| 227 if (!manager_) | |
| 228 return; | |
| 229 // OnDownloadCreated() is called inside DownloadManager::CreateDownloadItem(), | |
| 230 // so temporarily disable adding new unpersisted items to the history. All | |
| 231 // methods run on the UI thread and CreateDownloadItem() is synchronous, so it | |
| 232 // is impossible for an OnDownloadCreated() to come in for another | |
| 233 // DownloadItem while QueryCallback() is processing the database. | |
| 
Randy Smith (Not in Mondays)
2012/08/27 18:25:11
This is actually not strictly speaking true, or at
 
benjhayden
2012/09/10 19:02:56
Done.
 | |
| 234 loading_ = true; | |
| 235 for (InfoVector::const_iterator it = infos->begin(); | |
| 236 it != infos->end(); ++it) { | |
| 237 DownloadItem* download_item = manager_->CreateDownloadItem( | |
| 238 it->path, | |
| 239 it->url, | |
| 240 it->referrer_url, | |
| 241 it->start_time, | |
| 242 it->end_time, | |
| 243 it->received_bytes, | |
| 244 it->total_bytes, | |
| 245 it->state, | |
| 246 it->opened); | |
| 247 DownloadHistoryData* dhd = DownloadHistoryData::Get(download_item); | |
| 248 DCHECK(it->db_handle > kUninitializedHandle); | |
| 249 dhd->set_db_handle(it->db_handle); | |
| 250 ++history_size_; | |
| 251 } | |
| 252 manager_->CheckForHistoryFilesRemoval(); | |
| 253 loading_ = false; | |
| 254 } | |
| 255 | |
| 256 void DownloadHistory::OnDownloadCreated( | |
| 257 DownloadManager* manager, DownloadItem* item) { | |
| 258 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 259 | |
| 260 // Observe even temporary downloads in case they are marked not temporary. | |
| 261 item->AddObserver(this); | |
| 262 observing_items_.insert(item); | |
| 263 // All downloads should pass through OnDownloadCreated exactly once. | |
| 264 CHECK(!DownloadHistoryData::Get(item)); | |
| 265 DownloadHistoryData* dhd = new DownloadHistoryData(item); | |
| 266 if (item->GetState() == DownloadItem::IN_PROGRESS) { | |
| 267 dhd->set_info(GetPersistentStoreInfo(item)); | |
| 268 } | |
| 269 MaybeAddToHistory(item); | |
| 270 } | |
| 271 | |
| 272 void DownloadHistory::MaybeAddToHistory(DownloadItem* item) { | |
| 273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 274 int32 download_id = item->GetId(); | |
| 275 DownloadHistoryData* dhd = DownloadHistoryData::Get(item); | |
| 276 bool removing = (removing_.find(dhd->db_handle()) != removing_.end()); | |
| 277 // TODO(benjhayden): Remove IsTemporary(). | |
| 278 if (loading_ || | |
| 279 download_crx_util::IsExtensionDownload(*item) || | |
| 280 dhd->is_persisted() || | |
| 281 item->IsTemporary() || | |
| 282 removing || | |
| 283 dhd->is_adding()) | |
| 284 return; | |
| 285 dhd->set_is_adding(true); | |
| 286 if (dhd->info() == NULL) { | |
| 287 // Keep the info here regardless of whether the item is in progress so | |
| 288 // that, when ItemAdded() calls OnDownloadUpdated(), it can choose more | |
| 289 // intelligently whether to Update the db and/or discard the info. | |
| 290 dhd->set_info(GetPersistentStoreInfo(item)); | |
| 291 } | |
| 292 history_->CreateDownload(*dhd->info(), base::Bind( | |
| 293 &EnsureItemAddedOnUI, base::Bind( | |
| 294 &DownloadHistory::ItemAdded, weak_ptr_factory_.GetWeakPtr(), | |
| 295 download_id))); | |
| 296 } | |
| 297 | |
| 298 void DownloadHistory::ItemAdded(int32 download_id, int64 db_handle) { | |
| 299 if (!manager_) | |
| 300 return; | |
| 301 | |
| 302 if (removed_while_adding_.find(download_id) != | |
| 303 removed_while_adding_.end()) { | |
| 304 removed_while_adding_.erase(download_id); | |
| 305 if (removing_.empty()) { | |
| 306 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 307 base::Bind(&DownloadHistory::RemoveDownloadsBatch, | |
| 308 weak_ptr_factory_.GetWeakPtr())); | |
| 309 } | |
| 310 removing_.insert(db_handle); | |
| 311 return; | |
| 312 } | |
| 313 | |
| 314 DownloadItem* item = manager_->GetDownload(download_id); | |
| 315 if (!item) { | |
| 316 // This item will have called OnDownloadDestroyed(). If the item should | |
| 317 // have been removed from history, then t would have also called | |
| 
Randy Smith (Not in Mondays)
2012/08/27 18:25:11
nit: t -> it
 
benjhayden
2012/09/10 19:02:56
Done.
 | |
| 318 // OnDownloadRemoved(), which would have put |download_id| in | |
| 319 // removed_while_adding_, handled above. | |
| 320 return; | |
| 321 } | |
| 322 | |
| 323 UMA_HISTOGRAM_CUSTOM_COUNTS("Download.HistorySize2", | |
| 324 history_size_, | |
| 325 0/*min*/, | |
| 326 (1 << 23)/*max*/, | |
| 327 (1 << 7)/*num_buckets*/); | |
| 328 ++history_size_; | |
| 329 | |
| 330 DownloadHistoryData* dhd = DownloadHistoryData::Get(item); | |
| 331 dhd->set_is_adding(false); | |
| 332 DCHECK(db_handle > kUninitializedHandle); | |
| 333 dhd->set_db_handle(db_handle); | |
| 334 | |
| 335 // In case the item changed or became temporary while it was being added. | |
| 336 // Don't just update all of the item's observers because we're the only | |
| 337 // observer that can also see db_handle, which is the only thing that | |
| 338 // ItemAdded changed. | |
| 339 OnDownloadUpdated(item); | |
| 340 } | |
| 341 | |
| 342 void DownloadHistory::OnDownloadUpdated(DownloadItem* item) { | |
| 343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 344 | |
| 345 DownloadHistoryData* dhd = DownloadHistoryData::Get(item); | |
| 346 if (!dhd->is_persisted()) { | |
| 347 MaybeAddToHistory(item); | |
| 348 return; | |
| 349 } | |
| 350 if (item->IsTemporary()) { | |
| 351 OnDownloadRemoved(item); | |
| 352 return; | |
| 353 } | |
| 354 | |
| 355 // TODO(asanka): Persist GetTargetFilePath() as well. | |
| 356 DownloadPersistentStoreInfo current_info(GetPersistentStoreInfo(item)); | |
| 357 DownloadPersistentStoreInfo* previous_info = dhd->info(); | |
| 358 bool do_update = ( | |
| 359 (previous_info == NULL) || | |
| 360 (previous_info->path != current_info.path) || | |
| 361 (previous_info->end_time != current_info.end_time) || | |
| 362 (previous_info->received_bytes != current_info.received_bytes) || | |
| 363 (previous_info->total_bytes != current_info.total_bytes) || | |
| 364 (previous_info->state != current_info.state) || | |
| 365 (previous_info->opened != current_info.opened)); | |
| 366 UMA_HISTOGRAM_ENUMERATION("Download.HistoryPropagatedUpdate", do_update, 2); | |
| 367 if (do_update) { | |
| 368 history_->UpdateDownload(current_info); | |
| 369 } | |
| 370 if (item->GetState() == DownloadItem::IN_PROGRESS) { | |
| 371 dhd->set_info(current_info); | |
| 372 } else { | |
| 373 dhd->clear_info(); | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 // Downloads may be opened after they are completed. | |
| 378 void DownloadHistory::OnDownloadOpened(DownloadItem* item) { | |
| 379 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 380 DownloadHistoryData* dhd = DownloadHistoryData::Get(item); | |
| 381 if (!dhd->is_persisted()) { | |
| 382 MaybeAddToHistory(item); | |
| 383 return; | |
| 384 } | |
| 385 if (item->IsTemporary()) { | |
| 386 OnDownloadRemoved(item); | |
| 387 return; | |
| 388 } | |
| 389 | |
| 390 DownloadPersistentStoreInfo current_info(GetPersistentStoreInfo(item)); | |
| 391 history_->UpdateDownload(current_info); | |
| 392 if (item->GetState() == DownloadItem::IN_PROGRESS) { | |
| 393 dhd->set_info(current_info); | |
| 394 } | |
| 395 } | |
| 396 | |
| 397 void DownloadHistory::OnDownloadRemoved(DownloadItem* item) { | |
| 398 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 399 | |
| 400 DownloadHistoryData* dhd = DownloadHistoryData::Get(item); | |
| 401 if (!dhd->is_persisted()) { | |
| 402 if (dhd->is_adding()) { | |
| 403 removed_while_adding_.insert(item->GetId()); | |
| 404 } | |
| 405 return; | |
| 406 } | |
| 407 | |
| 408 // For database efficiency, batch removals together if they happen all at | |
| 409 // once. | |
| 410 if (removing_.empty()) { | |
| 411 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 412 base::Bind(&DownloadHistory::RemoveDownloadsBatch, | |
| 413 weak_ptr_factory_.GetWeakPtr())); | |
| 414 } | |
| 415 removing_.insert(dhd->db_handle()); | |
| 416 dhd->set_db_handle(kUninitializedHandle); | |
| 417 --history_size_; | |
| 418 } | |
| 419 | |
| 420 void DownloadHistory::RemoveDownloadsBatch() { | |
| 421 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 422 HandleSet remove_handles; | |
| 423 removing_.swap(remove_handles); | |
| 424 history_->RemoveDownloads(remove_handles); | |
| 425 } | |
| 426 | |
| 427 void DownloadHistory::ManagerGoingDown(DownloadManager* manager) { | |
| 428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 429 DCHECK_EQ(manager_, manager); | |
| 430 manager_->RemoveObserver(this); | |
| 431 manager_ = NULL; | |
| 432 } | |
| 433 | |
| 434 void DownloadHistory::OnDownloadDestroyed(DownloadItem* item) { | |
| 435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 436 item->RemoveObserver(this); | |
| 437 observing_items_.erase(item); | |
| 47 } | 438 } | 
| 48 | 439 | 
| 49 void DownloadHistory::CheckVisitedReferrerBefore( | 440 void DownloadHistory::CheckVisitedReferrerBefore( | 
| 50 int32 download_id, | 441 int32 download_id, | 
| 51 const GURL& referrer_url, | 442 const GURL& referrer_url, | 
| 52 const VisitedBeforeDoneCallback& callback) { | 443 const VisitedBeforeDoneCallback& callback) { | 
| 53 if (referrer_url.is_valid()) { | 444 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| 54 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | 445 if (!referrer_url.is_valid()) { | 
| 55 profile_, Profile::EXPLICIT_ACCESS); | 446 callback.Run(false); | 
| 56 if (hs) { | 447 return; | 
| 57 HistoryService::Handle handle = | 448 } | 
| 58 hs->GetVisibleVisitCountToHost(referrer_url, &history_consumer_, | 449 history_->GetVisibleVisitCountToHost( | 
| 59 base::Bind(&DownloadHistory::OnGotVisitCountToHost, | 450 referrer_url, | 
| 60 base::Unretained(this))); | 451 base::Bind(&EnsureVisitedOnUI, base::Bind( | 
| 61 visited_before_requests_[handle] = callback; | 452 &DownloadHistory::OnGotVisitCountToHost, | 
| 62 return; | 453 weak_ptr_factory_.GetWeakPtr(), callback))); | 
| 63 } | 454 } | 
| 64 } | 455 | 
| 65 callback.Run(false); | 456 void DownloadHistory::OnGotVisitCountToHost( | 
| 66 } | 457 const VisitedBeforeDoneCallback& callback, | 
| 67 | 458 bool found_visits, | 
| 68 void DownloadHistory::AddEntry( | 459 int count, | 
| 69 DownloadItem* download_item, | 460 base::Time first_visit) { | 
| 70 const HistoryService::DownloadCreateCallback& callback) { | |
| 71 DCHECK(download_item); | |
| 72 // Do not store the download in the history database for a few special cases: | |
| 73 // - incognito mode (that is the point of this mode) | |
| 74 // - extensions (users don't think of extension installation as 'downloading') | |
| 75 // - temporary download, like in drag-and-drop | |
| 76 // - history service is not available (e.g. in tests) | |
| 77 // We have to make sure that these handles don't collide with normal db | |
| 78 // handles, so we use a negative value. Eventually, they could overlap, but | |
| 79 // you'd have to do enough downloading that your ISP would likely stab you in | |
| 80 // the neck first. YMMV. | |
| 81 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | |
| 82 profile_, Profile::EXPLICIT_ACCESS); | |
| 83 if (download_crx_util::IsExtensionDownload(*download_item) || | |
| 84 download_item->IsTemporary() || !hs) { | |
| 85 callback.Run(download_item->GetId(), GetNextFakeDbHandle()); | |
| 86 return; | |
| 87 } | |
| 88 | |
| 89 int32 id = download_item->GetId(); | |
| 90 DownloadPersistentStoreInfo history_info = | |
| 91 download_item->GetPersistentStoreInfo(); | |
| 92 hs->CreateDownload(id, history_info, &history_consumer_, callback); | |
| 93 } | |
| 94 | |
| 95 void DownloadHistory::UpdateEntry(DownloadItem* download_item) { | |
| 96 // Don't store info in the database if the download was initiated while in | |
| 97 // incognito mode or if it hasn't been initialized in our database table. | |
| 98 if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle) | |
| 99 return; | |
| 100 | |
| 101 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | |
| 102 profile_, Profile::EXPLICIT_ACCESS); | |
| 103 if (!hs) | |
| 104 return; | |
| 105 hs->UpdateDownload(download_item->GetPersistentStoreInfo()); | |
| 106 } | |
| 107 | |
| 108 void DownloadHistory::UpdateDownloadPath(DownloadItem* download_item, | |
| 109 const FilePath& new_path) { | |
| 110 // No update necessary if the download was initiated while in incognito mode. | |
| 111 if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle) | |
| 112 return; | |
| 113 | |
| 114 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | |
| 115 profile_, Profile::EXPLICIT_ACCESS); | |
| 116 if (hs) | |
| 117 hs->UpdateDownloadPath(new_path, download_item->GetDbHandle()); | |
| 118 } | |
| 119 | |
| 120 void DownloadHistory::RemoveEntry(DownloadItem* download_item) { | |
| 121 // No update necessary if the download was initiated while in incognito mode. | |
| 122 if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle) | |
| 123 return; | |
| 124 | |
| 125 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | |
| 126 profile_, Profile::EXPLICIT_ACCESS); | |
| 127 if (hs) | |
| 128 hs->RemoveDownload(download_item->GetDbHandle()); | |
| 129 } | |
| 130 | |
| 131 void DownloadHistory::RemoveEntriesBetween(const base::Time remove_begin, | |
| 132 const base::Time remove_end) { | |
| 133 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | |
| 134 profile_, Profile::EXPLICIT_ACCESS); | |
| 135 if (hs) | |
| 136 hs->RemoveDownloadsBetween(remove_begin, remove_end); | |
| 137 } | |
| 138 | |
| 139 int64 DownloadHistory::GetNextFakeDbHandle() { | |
| 140 return next_fake_db_handle_--; | |
| 141 } | |
| 142 | |
| 143 void DownloadHistory::OnGotVisitCountToHost(HistoryService::Handle handle, | |
| 144 bool found_visits, | |
| 145 int count, | |
| 146 base::Time first_visit) { | |
| 147 VisitedBeforeRequestsMap::iterator request = | |
| 148 visited_before_requests_.find(handle); | |
| 149 DCHECK(request != visited_before_requests_.end()); | |
| 150 VisitedBeforeDoneCallback callback = request->second; | |
| 151 visited_before_requests_.erase(request); | |
| 152 callback.Run(found_visits && count && | 461 callback.Run(found_visits && count && | 
| 153 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight())); | 462 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight())); | 
| 154 } | 463 } | 
| OLD | NEW |