 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| Index: chrome/browser/download/download_history.cc | 
| diff --git a/chrome/browser/download/download_history.cc b/chrome/browser/download/download_history.cc | 
| index b4aa58279c12f1edceca6d6030693599df8c27eb..795d6e933e2dc9238c0cd64cfbccae3663423686 100644 | 
| --- a/chrome/browser/download/download_history.cc | 
| +++ b/chrome/browser/download/download_history.cc | 
| @@ -4,146 +4,397 @@ | 
| 
Randy Smith (Not in Mondays)
2012/08/15 20:47:44
Explanatory comment at top of file about basic arc
 
benjhayden
2012/08/17 18:20:45
I had hoped that the code could speak for itself,
 
Randy Smith (Not in Mondays)
2012/08/20 18:57:04
L G T M; thanks.
 | 
| #include "chrome/browser/download/download_history.h" | 
| -#include "base/logging.h" | 
| +#include "base/metrics/histogram.h" | 
| +#include "chrome/browser/cancelable_request.h" | 
| #include "chrome/browser/download/download_crx_util.h" | 
| -#include "chrome/browser/history/history_marshaling.h" | 
| -#include "chrome/browser/history/history_service_factory.h" | 
| -#include "chrome/browser/profiles/profile.h" | 
| +#include "chrome/browser/download/download_persistent_store_info.h" | 
| +#include "content/public/browser/browser_thread.h" | 
| #include "content/public/browser/download_item.h" | 
| -#include "content/public/browser/download_persistent_store_info.h" | 
| +#include "content/public/browser/download_manager.h" | 
| +using content::BrowserThread; | 
| using content::DownloadItem; | 
| -using content::DownloadPersistentStoreInfo; | 
| +using content::DownloadManager; | 
| -DownloadHistory::DownloadHistory(Profile* profile) | 
| - : profile_(profile), | 
| - next_fake_db_handle_(DownloadItem::kUninitializedHandle - 1) { | 
| - DCHECK(profile); | 
| -} | 
| +namespace { | 
| -DownloadHistory::~DownloadHistory() {} | 
| +class DownloadHistoryData : public base::SupportsUserData::Data { | 
| 
Randy Smith (Not in Mondays)
2012/08/15 20:47:44
Comment documenting purpose and use?
 
benjhayden
2012/08/17 18:20:45
Done.
 | 
| + public: | 
| + static const int64 kUninitializedHandle = -1; | 
| 
Randy Smith (Not in Mondays)
2012/08/15 20:47:44
Why here?  I would think this would be a DownloadH
 
benjhayden
2012/08/17 18:20:45
Moved it out into the anonymous namespace.
 
Randy Smith (Not in Mondays)
2012/08/20 18:57:04
I think I was just confused in my earlier comment.
 | 
| -void DownloadHistory::GetNextId( | 
| - const HistoryService::DownloadNextIdCallback& callback) { | 
| - HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | 
| - profile_, Profile::EXPLICIT_ACCESS); | 
| - if (!hs) | 
| - return; | 
| + static DownloadHistoryData* Get(DownloadItem* item) { | 
| + base::SupportsUserData::Data* data = item->GetUserData(kKey); | 
| + return (data == NULL) ? NULL : | 
| + static_cast<DownloadHistoryData*>(data); | 
| + } | 
| - hs->GetNextDownloadId(&history_consumer_, callback); | 
| + explicit DownloadHistoryData(DownloadItem* item) | 
| + : is_adding_(false), | 
| + is_persisted_(false), | 
| + db_handle_(kUninitializedHandle), | 
| + info_(NULL) { | 
| + item->SetUserData(kKey, this); | 
| + } | 
| + | 
| + virtual ~DownloadHistoryData() { | 
| + } | 
| + | 
| + // Whether this item is currently being added to the database. | 
| + bool is_adding() const { return is_adding_; } | 
| + void set_is_adding(bool a) { is_adding_ = a; } | 
| + | 
| + // Whether this item is already persisted in the database. | 
| + bool is_persisted() const { return is_persisted_; } | 
| + void set_is_persisted(bool p) { is_persisted_ = p; } | 
| + | 
| + int64 db_handle() const { return db_handle_; } | 
| + void set_db_handle(int64 h) { db_handle_ = h; } | 
| + | 
| + // This allows OnDownloadUpdated() to see what changed in a DownloadItem if | 
| + // anything, in order to prevent writing to the database unnecessarily. It is | 
| + // nullified when the item is no longer in progress in order to save memory. | 
| + DownloadPersistentStoreInfo* info() { return info_.get(); } | 
| + void set_info(const DownloadPersistentStoreInfo& i) { | 
| + info_.reset(new DownloadPersistentStoreInfo(i)); | 
| + } | 
| + void clear_info() { | 
| + info_.reset(); | 
| + } | 
| + | 
| + private: | 
| + static const char kKey[]; | 
| + | 
| + bool is_adding_; | 
| + bool is_persisted_; | 
| + int64 db_handle_; | 
| + scoped_ptr<DownloadPersistentStoreInfo> info_; | 
| + | 
| + DISALLOW_COPY_AND_ASSIGN(DownloadHistoryData); | 
| +}; | 
| + | 
| +const char DownloadHistoryData::kKey[] = | 
| + "DownloadItem DownloadHistoryData"; | 
| + | 
| +DownloadPersistentStoreInfo GetPersistentStoreInfo(DownloadItem* item) { | 
| + DownloadHistoryData* data = DownloadHistoryData::Get(item); | 
| + int64 db_handle = ((data != NULL) ? | 
| + data->db_handle() : | 
| + DownloadHistoryData::kUninitializedHandle); | 
| + return DownloadPersistentStoreInfo( | 
| + item->GetFullPath(), | 
| + item->GetURL(), | 
| + item->GetReferrerUrl(), | 
| + item->GetStartTime(), | 
| + item->GetEndTime(), | 
| + item->GetReceivedBytes(), | 
| + item->GetTotalBytes(), | 
| + item->GetState(), | 
| + db_handle, | 
| + item->GetOpened()); | 
| } | 
| -void DownloadHistory::Load( | 
| +} // anonymous namespace | 
| + | 
| +HistoryServiceDownloadAdapter::HistoryServiceDownloadAdapter( | 
| + HistoryService* history_service) | 
| + : history_service_(history_service) { | 
| +} | 
| + | 
| +HistoryServiceDownloadAdapter::~HistoryServiceDownloadAdapter() {} | 
| + | 
| +void HistoryServiceDownloadAdapter::QueryDownloads( | 
| const HistoryService::DownloadQueryCallback& callback) { | 
| - HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | 
| - profile_, Profile::EXPLICIT_ACCESS); | 
| - if (!hs) | 
| + history_service_->QueryDownloads(&history_consumer_, callback); | 
| + history_service_->CleanUpInProgressEntries(); | 
| +} | 
| + | 
| +HistoryService::Handle | 
| +HistoryServiceDownloadAdapter::GetVisibleVisitCountToHost( | 
| + const GURL& referrer_url, | 
| + const HistoryService::GetVisibleVisitCountToHostCallback& | 
| + callback) { | 
| + return history_service_->GetVisibleVisitCountToHost( | 
| + referrer_url, &history_consumer_, callback); | 
| +} | 
| + | 
| +void HistoryServiceDownloadAdapter::CreateDownload( | 
| + int32 id, | 
| + const DownloadPersistentStoreInfo& info, | 
| + const HistoryService::DownloadCreateCallback& callback) { | 
| + history_service_->CreateDownload(id, info, &history_consumer_, callback); | 
| +} | 
| + | 
| +void HistoryServiceDownloadAdapter::UpdateDownload( | 
| + const DownloadPersistentStoreInfo& info) { | 
| + history_service_->UpdateDownload(info); | 
| +} | 
| + | 
| +void HistoryServiceDownloadAdapter::RemoveDownloads( | 
| + const std::set<int64>& handles) { | 
| + history_service_->RemoveDownloads(handles); | 
| +} | 
| + | 
| +void HistoryServiceDownloadAdapter::OnDownloadHistoryDestroyed() { | 
| + delete this; | 
| +} | 
| + | 
| +DownloadHistory::DownloadHistory( | 
| + DownloadManager* manager, | 
| + HistoryServiceDownloadAdapter* history) | 
| + : manager_(manager), | 
| + history_(history), | 
| + loading_(false), | 
| + history_size_(0) { | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| + DCHECK(manager_); | 
| + manager_->AddObserver(this); | 
| + history_->QueryDownloads(base::Bind( | 
| + &DownloadHistory::QueryCallback, AsWeakPtr())); | 
| +} | 
| + | 
| +DownloadHistory::~DownloadHistory() { | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| + history_->OnDownloadHistoryDestroyed(); | 
| + if (manager_) | 
| + manager_->RemoveObserver(this); | 
| 
Randy Smith (Not in Mondays)
2012/08/15 20:47:44
DCHECK that the manager has no items on it?
 
benjhayden
2012/08/17 18:20:45
Did the observing_items_ set trick instead.
I'll s
 | 
| +} | 
| + | 
| +void DownloadHistory::QueryCallback( | 
| + DownloadHistory::InfoVector* infos) { | 
| 
Randy Smith (Not in Mondays)
2012/08/15 20:47:44
Who owns the storage in the vector pointed to by I
 
benjhayden
2012/08/17 18:20:45
HistoryService::QueryDownloads() creates a history
 | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| + // We may have caught ManagerGoingDown() before the history loaded. | 
| + if (manager_ == NULL) | 
| return; | 
| + // OnDownloadCreated() is called inside DownloadManager::CreateDownloadItem(), | 
| + // before we have a chance to attach a DownloadHistoryData, so temporarily | 
| + // disable adding new unpersisted items to the history. All methods run on | 
| + // the UI thread and CreateDownloadItem() is synchronous, so it is impossible | 
| + // for an OnDownloadCreated() to come in for another DownloadItem while we are | 
| + // processing the database. | 
| + loading_ = true; | 
| + for (InfoVector::const_iterator it = infos->begin(); | 
| + it != infos->end(); ++it) { | 
| + DownloadItem* download_item = manager_->CreateDownloadItem( | 
| + it->path, | 
| + it->url, | 
| + it->referrer_url, | 
| + it->start_time, | 
| + it->end_time, | 
| + it->received_bytes, | 
| + it->total_bytes, | 
| + it->state, | 
| + it->opened); | 
| + DownloadHistoryData* data = DownloadHistoryData::Get(download_item); | 
| + data->set_is_persisted(true); | 
| + data->set_db_handle(it->db_handle); | 
| + ++history_size_; | 
| + } | 
| + loading_ = false; | 
| +} | 
| - hs->QueryDownloads(&history_consumer_, callback); | 
| +void DownloadHistory::OnDownloadCreated( | 
| + DownloadManager* manager, DownloadItem* item) { | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| - // This is the initial load, so do a cleanup of corrupt in-progress entries. | 
| - hs->CleanUpInProgressEntries(); | 
| + // Observe even temporary downloads in case they are marked not temporary. | 
| + item->AddObserver(this); | 
| + // All downloads should pass through OnDownloadCreated exactly once. | 
| + CHECK(!DownloadHistoryData::Get(item)); | 
| + DownloadHistoryData* data = new DownloadHistoryData(item); | 
| + if (item->GetState() == DownloadItem::IN_PROGRESS) { | 
| + data->set_info(GetPersistentStoreInfo(item)); | 
| + } | 
| + MaybeAddToHistory(item); | 
| } | 
| -void DownloadHistory::CheckVisitedReferrerBefore( | 
| - int32 download_id, | 
| - const GURL& referrer_url, | 
| - const VisitedBeforeDoneCallback& callback) { | 
| - if (referrer_url.is_valid()) { | 
| - HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | 
| - profile_, Profile::EXPLICIT_ACCESS); | 
| - if (hs) { | 
| - HistoryService::Handle handle = | 
| - hs->GetVisibleVisitCountToHost(referrer_url, &history_consumer_, | 
| - base::Bind(&DownloadHistory::OnGotVisitCountToHost, | 
| - base::Unretained(this))); | 
| - visited_before_requests_[handle] = callback; | 
| - return; | 
| +void DownloadHistory::MaybeAddToHistory(DownloadItem* item) { | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| + int32 download_id = item->GetId(); | 
| + DownloadHistoryData* data = DownloadHistoryData::Get(item); | 
| + bool removing = (removing_.find(data->db_handle()) != removing_.end()); | 
| 
Randy Smith (Not in Mondays)
2012/08/15 20:47:44
Should this be a DCHECK?
 
benjhayden
2012/08/17 18:20:45
I could imagine DownloadItem sending one or two fi
 | 
| + if (!loading_ && | 
| + !download_crx_util::IsExtensionDownload(*item) && | 
| + !data->is_persisted() && | 
| + !item->IsTemporary() && | 
| + !removing && | 
| + !data->is_adding()) { | 
| + data->set_is_adding(true); | 
| + if (data->info() == NULL) { | 
| + // Keep the info here regardless of whether the item is in progress so | 
| + // that, when ItemAdded() calls OnDownloadUpdated(), it can choose more | 
| + // intelligently whether to Update the db and/or discard the info. | 
| + data->set_info(GetPersistentStoreInfo(item)); | 
| } | 
| + history_->CreateDownload( | 
| + download_id, | 
| + *data->info(), | 
| + base::Bind(&DownloadHistory::ItemAdded, AsWeakPtr())); | 
| } | 
| - callback.Run(false); | 
| } | 
| -void DownloadHistory::AddEntry( | 
| - DownloadItem* download_item, | 
| - const HistoryService::DownloadCreateCallback& callback) { | 
| - DCHECK(download_item); | 
| - // Do not store the download in the history database for a few special cases: | 
| - // - incognito mode (that is the point of this mode) | 
| - // - extensions (users don't think of extension installation as 'downloading') | 
| - // - temporary download, like in drag-and-drop | 
| - // - history service is not available (e.g. in tests) | 
| - // We have to make sure that these handles don't collide with normal db | 
| - // handles, so we use a negative value. Eventually, they could overlap, but | 
| - // you'd have to do enough downloading that your ISP would likely stab you in | 
| - // the neck first. YMMV. | 
| - HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | 
| - profile_, Profile::EXPLICIT_ACCESS); | 
| - if (download_crx_util::IsExtensionDownload(*download_item) || | 
| - download_item->IsTemporary() || !hs) { | 
| - callback.Run(download_item->GetId(), GetNextFakeDbHandle()); | 
| +void DownloadHistory::ItemAdded(int32 download_id, int64 db_handle) { | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| + | 
| + if (!manager_) | 
| + return; | 
| + | 
| + DownloadItem* item = manager_->GetDownload(download_id); | 
| + if (!item) { | 
| + // We should have caught OnDownloadDestroyed(). If the item should have | 
| 
Randy Smith (Not in Mondays)
2012/08/15 20:47:44
Does this mean we could have a DCHECK() here?
 
benjhayden
2012/08/17 18:20:45
Nope. There's no mechanism that cancels this parti
 
Randy Smith (Not in Mondays)
2012/08/20 18:57:04
Hmmm.  Then I think the comment's a little confusi
 | 
| + // been removed from history, then OnDownloadRemoved() put |download_id| in | 
| + // removed_while_adding_. | 
| + if (removed_while_adding_.find(download_id) != | 
| + removed_while_adding_.end()) { | 
| + removed_while_adding_.erase(download_id); | 
| + if (removing_.empty()) { | 
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 
| + base::Bind(&DownloadHistory::RemoveDownloadsBatch, AsWeakPtr())); | 
| + } | 
| + removing_.insert(db_handle); | 
| + } | 
| return; | 
| } | 
| - int32 id = download_item->GetId(); | 
| - DownloadPersistentStoreInfo history_info = | 
| - download_item->GetPersistentStoreInfo(); | 
| - hs->CreateDownload(id, history_info, &history_consumer_, callback); | 
| + UMA_HISTOGRAM_CUSTOM_COUNTS("Download.HistorySize2", | 
| + history_size_, | 
| + 0/*min*/, | 
| + (1 << 23)/*max*/, | 
| + (1 << 7)/*num_buckets*/); | 
| + ++history_size_; | 
| + | 
| + DownloadHistoryData* data = DownloadHistoryData::Get(item); | 
| + data->set_is_adding(false); | 
| + data->set_is_persisted(true); | 
| + data->set_db_handle(db_handle); | 
| + | 
| + // In case the item changed or became temporary while it was being added. | 
| + // Don't just UpdateObservers() because we're the only observer that can also | 
| + // see is_persisted/db_handle, which is the only thing that we changed. | 
| + OnDownloadUpdated(item); | 
| } | 
| -void DownloadHistory::UpdateEntry(DownloadItem* download_item) { | 
| - // Don't store info in the database if the download was initiated while in | 
| - // incognito mode or if it hasn't been initialized in our database table. | 
| - if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle) | 
| - return; | 
| +void DownloadHistory::OnDownloadUpdated(DownloadItem* item) { | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| - HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | 
| - profile_, Profile::EXPLICIT_ACCESS); | 
| - if (!hs) | 
| + DownloadHistoryData* data = DownloadHistoryData::Get(item); | 
| + if (!data->is_persisted()) { | 
| + MaybeAddToHistory(item); | 
| + return; | 
| + } | 
| + if (item->IsTemporary()) { | 
| + OnDownloadRemoved(item); | 
| return; | 
| - hs->UpdateDownload(download_item->GetPersistentStoreInfo()); | 
| + } | 
| + | 
| + // TODO(asanka): Persist GetTargetFilePath() as well. | 
| + DownloadPersistentStoreInfo current_info(GetPersistentStoreInfo(item)); | 
| + DownloadPersistentStoreInfo* previous_info = data->info(); | 
| + bool do_update = ( | 
| + (previous_info == NULL) || | 
| + (previous_info->path != current_info.path) || | 
| + (previous_info->end_time != current_info.end_time) || | 
| + (previous_info->received_bytes != current_info.received_bytes) || | 
| + (previous_info->total_bytes != current_info.total_bytes) || | 
| + (previous_info->state != current_info.state) || | 
| + (previous_info->opened != current_info.opened)); | 
| + UMA_HISTOGRAM_ENUMERATION("Download.HistoryPropagatedUpdate", do_update, 2); | 
| + if (do_update) { | 
| + history_->UpdateDownload(current_info); | 
| + } | 
| + if (item->GetState() == DownloadItem::IN_PROGRESS) { | 
| + data->set_info(current_info); | 
| + } else { | 
| + data->clear_info(); | 
| + } | 
| } | 
| -void DownloadHistory::UpdateDownloadPath(DownloadItem* download_item, | 
| - const FilePath& new_path) { | 
| - // No update necessary if the download was initiated while in incognito mode. | 
| - if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle) | 
| +// Downloads may be opened after they are completed. | 
| +void DownloadHistory::OnDownloadOpened(DownloadItem* item) { | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| + DownloadHistoryData* data = DownloadHistoryData::Get(item); | 
| + if (!data->is_persisted()) { | 
| + MaybeAddToHistory(item); | 
| return; | 
| + } | 
| + if (item->IsTemporary()) { | 
| + OnDownloadRemoved(item); | 
| + return; | 
| + } | 
| - HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | 
| - profile_, Profile::EXPLICIT_ACCESS); | 
| - if (hs) | 
| - hs->UpdateDownloadPath(new_path, download_item->GetDbHandle()); | 
| + DownloadPersistentStoreInfo current_info(GetPersistentStoreInfo(item)); | 
| + history_->UpdateDownload(current_info); | 
| + if (item->GetState() == DownloadItem::IN_PROGRESS) { | 
| 
Randy Smith (Not in Mondays)
2012/08/15 20:47:44
Can they be opened before they're completed?  I.e.
 
benjhayden
2012/08/17 18:20:45
Wasn't there some chatter about streaming download
 
Randy Smith (Not in Mondays)
2012/08/20 18:57:04
Fair enough.  We're well short of streaming data t
 | 
| + data->set_info(current_info); | 
| + } | 
| } | 
| -void DownloadHistory::RemoveEntry(DownloadItem* download_item) { | 
| - // No update necessary if the download was initiated while in incognito mode. | 
| - if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle) | 
| +void DownloadHistory::OnDownloadRemoved(DownloadItem* item) { | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| + | 
| + DownloadHistoryData* data = DownloadHistoryData::Get(item); | 
| + if ((data == NULL) || | 
| + !data->is_persisted() || | 
| + (data->db_handle() <= DownloadHistoryData::kUninitializedHandle)) { | 
| + if (data->is_adding()) { | 
| + removed_while_adding_.insert(item->GetId()); | 
| + } | 
| return; | 
| + } | 
| + | 
| + // For database efficiency, batch removals together if they happen all at | 
| + // once. | 
| + if (removing_.empty()) { | 
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 
| + base::Bind(&DownloadHistory::RemoveDownloadsBatch, AsWeakPtr())); | 
| + } | 
| + removing_.insert(data->db_handle()); | 
| + data->set_db_handle(DownloadHistoryData::kUninitializedHandle); | 
| + data->set_is_persisted(false); | 
| + --history_size_; | 
| +} | 
| + | 
| +void DownloadHistory::RemoveDownloadsBatch() { | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| + HandleSet remove_handles; | 
| + removing_.swap(remove_handles); | 
| + history_->RemoveDownloads(remove_handles); | 
| +} | 
| - HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | 
| - profile_, Profile::EXPLICIT_ACCESS); | 
| - if (hs) | 
| - hs->RemoveDownload(download_item->GetDbHandle()); | 
| +void DownloadHistory::ManagerGoingDown(DownloadManager* manager) { | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| + DCHECK_EQ(manager_, manager); | 
| + manager_->RemoveObserver(this); | 
| + manager_ = NULL; | 
| } | 
| -void DownloadHistory::RemoveEntriesBetween(const base::Time remove_begin, | 
| - const base::Time remove_end) { | 
| - HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | 
| - profile_, Profile::EXPLICIT_ACCESS); | 
| - if (hs) | 
| - hs->RemoveDownloadsBetween(remove_begin, remove_end); | 
| + | 
| +void DownloadHistory::OnDownloadDestroyed(DownloadItem* item) { | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| + item->RemoveObserver(this); | 
| } | 
| -int64 DownloadHistory::GetNextFakeDbHandle() { | 
| - return next_fake_db_handle_--; | 
| +void DownloadHistory::CheckVisitedReferrerBefore( | 
| + int32 download_id, | 
| + const GURL& referrer_url, | 
| + const VisitedBeforeDoneCallback& callback) { | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| + if (referrer_url.is_valid()) { | 
| + HistoryService::Handle handle = history_->GetVisibleVisitCountToHost( | 
| + referrer_url, | 
| + base::Bind(&DownloadHistory::OnGotVisitCountToHost, | 
| + AsWeakPtr())); | 
| + visited_before_requests_[handle] = callback; | 
| + return; | 
| + } | 
| + callback.Run(false); | 
| } | 
| -void DownloadHistory::OnGotVisitCountToHost(HistoryService::Handle handle, | 
| - bool found_visits, | 
| - int count, | 
| - base::Time first_visit) { | 
| +void DownloadHistory::OnGotVisitCountToHost( | 
| + HistoryService::Handle handle, | 
| + bool found_visits, | 
| + int count, | 
| + base::Time first_visit) { | 
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
| VisitedBeforeRequestsMap::iterator request = | 
| visited_before_requests_.find(handle); | 
| DCHECK(request != visited_before_requests_.end()); |