| Index: chrome/browser/history/top_sites_impl.cc
|
| diff --git a/chrome/browser/history/top_sites_impl.cc b/chrome/browser/history/top_sites_impl.cc
|
| index 18f2e2117425fcb65f763e260e61798e824a8095..9eb3a00694f7a060f98cdb30e5dfadc134aca212 100644
|
| --- a/chrome/browser/history/top_sites_impl.cc
|
| +++ b/chrome/browser/history/top_sites_impl.cc
|
| @@ -55,18 +55,33 @@ namespace {
|
|
|
| void RunOrPostGetMostVisitedURLsCallback(
|
| base::TaskRunner* task_runner,
|
| + bool include_forced_urls,
|
| const TopSitesImpl::GetMostVisitedURLsCallback& callback,
|
| - const MostVisitedURLList& urls) {
|
| + const MostVisitedURLList& all_urls,
|
| + const MostVisitedURLList& nonforced_urls) {
|
| + const MostVisitedURLList* urls =
|
| + include_forced_urls ? &all_urls : &nonforced_urls;
|
| if (task_runner->RunsTasksOnCurrentThread())
|
| - callback.Run(urls);
|
| + callback.Run(*urls);
|
| else
|
| - task_runner->PostTask(FROM_HERE, base::Bind(callback, urls));
|
| + task_runner->PostTask(FROM_HERE, base::Bind(callback, *urls));
|
| +}
|
| +
|
| +// Compares two MostVisitedURL having a non-null |last_forced_time|.
|
| +bool ForcedURLComparator(const MostVisitedURL& first,
|
| + const MostVisitedURL& second) {
|
| + DCHECK(!first.last_forced_time.is_null() &&
|
| + !second.last_forced_time.is_null());
|
| + return first.last_forced_time < second.last_forced_time;
|
| }
|
|
|
| } // namespace
|
|
|
| -// How many top sites to store in the cache.
|
| -static const size_t kTopSitesNumber = 20;
|
| +// How many non-forced top sites to store in the cache.
|
| +static const size_t kNonForcedTopSitesNumber = 20;
|
| +
|
| +// How many forced top sites to store in the cache.
|
| +static const size_t kForcedTopSitesNumber = 20;
|
|
|
| // Max number of temporary images we'll cache. See comment above
|
| // temp_images_ for details.
|
| @@ -132,7 +147,7 @@ bool TopSitesImpl::SetPageThumbnail(const GURL& url,
|
|
|
| bool add_temp_thumbnail = false;
|
| if (!IsKnownURL(url)) {
|
| - if (!IsFull()) {
|
| + if (!IsNonForcedFull()) {
|
| add_temp_thumbnail = true;
|
| } else {
|
| return false; // This URL is not known to us.
|
| @@ -171,7 +186,7 @@ bool TopSitesImpl::SetPageThumbnailToJPEGBytes(
|
|
|
| bool add_temp_thumbnail = false;
|
| if (!IsKnownURL(url)) {
|
| - if (!IsFull()) {
|
| + if (!IsNonForcedFull()) {
|
| add_temp_thumbnail = true;
|
| } else {
|
| return false; // This URL is not known to us.
|
| @@ -194,7 +209,8 @@ bool TopSitesImpl::SetPageThumbnailToJPEGBytes(
|
|
|
| // WARNING: this function may be invoked on any thread.
|
| void TopSitesImpl::GetMostVisitedURLs(
|
| - const GetMostVisitedURLsCallback& callback) {
|
| + const GetMostVisitedURLsCallback& callback,
|
| + bool include_forced_urls) {
|
| MostVisitedURLList filtered_urls;
|
| {
|
| base::AutoLock lock(lock_);
|
| @@ -204,10 +220,17 @@ void TopSitesImpl::GetMostVisitedURLs(
|
| pending_callbacks_.push_back(
|
| base::Bind(&RunOrPostGetMostVisitedURLsCallback,
|
| base::MessageLoopProxy::current(),
|
| + include_forced_urls,
|
| callback));
|
| return;
|
| }
|
| - filtered_urls = thread_safe_cache_->top_sites();
|
| + if (include_forced_urls) {
|
| + filtered_urls = thread_safe_cache_->top_sites();
|
| + } else {
|
| + filtered_urls.assign(thread_safe_cache_->top_sites().begin() +
|
| + thread_safe_cache_->GetNumForcedURLs(),
|
| + thread_safe_cache_->top_sites().end());
|
| + }
|
| }
|
| callback.Run(filtered_urls);
|
| }
|
| @@ -366,29 +389,49 @@ void TopSitesImpl::Shutdown() {
|
| void TopSitesImpl::DiffMostVisited(const MostVisitedURLList& old_list,
|
| const MostVisitedURLList& new_list,
|
| TopSitesDelta* delta) {
|
| +
|
| // Add all the old URLs for quick lookup. This maps URLs to the corresponding
|
| // index in the input.
|
| std::map<GURL, size_t> all_old_urls;
|
| - for (size_t i = 0; i < old_list.size(); i++)
|
| + size_t num_old_forced = 0;
|
| + for (size_t i = 0; i < old_list.size(); i++) {
|
| + if (!old_list[i].last_forced_time.is_null())
|
| + num_old_forced++;
|
| + DCHECK(old_list[i].last_forced_time.is_null() || i < num_old_forced)
|
| + << "Forced URLs must all appear before non-forced URLs.";
|
| all_old_urls[old_list[i].url] = i;
|
| + }
|
|
|
| // Check all the URLs in the new set to see which ones are new or just moved.
|
| // When we find a match in the old set, we'll reset its index to our special
|
| // marker. This allows us to quickly identify the deleted ones in a later
|
| // pass.
|
| const size_t kAlreadyFoundMarker = static_cast<size_t>(-1);
|
| + int rank = -1; // Forced URLs have a rank of -1.
|
| for (size_t i = 0; i < new_list.size(); i++) {
|
| + // Increase the rank if we're going through forced URLs. This works because
|
| + // non-forced URLs all come after forced URLs.
|
| + if (new_list[i].last_forced_time.is_null())
|
| + rank++;
|
| + DCHECK(new_list[i].last_forced_time.is_null() == (rank != -1))
|
| + << "Forced URLs must all appear before non-forced URLs.";
|
| std::map<GURL, size_t>::iterator found = all_old_urls.find(new_list[i].url);
|
| if (found == all_old_urls.end()) {
|
| MostVisitedURLWithRank added;
|
| added.url = new_list[i];
|
| - added.rank = i;
|
| + added.rank = rank;
|
| delta->added.push_back(added);
|
| } else {
|
| - if (found->second != i) {
|
| + DCHECK(found->second != kAlreadyFoundMarker)
|
| + << "Same URL appears twice in the new list.";
|
| + int old_rank = found->second >= num_old_forced ?
|
| + found->second - num_old_forced : -1;
|
| + if (old_rank != rank ||
|
| + old_list[found->second].last_forced_time !=
|
| + new_list[i].last_forced_time) {
|
| MostVisitedURLWithRank moved;
|
| moved.url = new_list[i];
|
| - moved.rank = i;
|
| + moved.rank = rank;
|
| delta->moved.push_back(moved);
|
| }
|
| found->second = kAlreadyFoundMarker;
|
| @@ -431,8 +474,12 @@ const std::string& TopSitesImpl::GetCanonicalURLString(const GURL& url) const {
|
| return cache_->GetCanonicalURL(url).spec();
|
| }
|
|
|
| -bool TopSitesImpl::IsFull() {
|
| - return loaded_ && cache_->top_sites().size() >= kTopSitesNumber;
|
| +bool TopSitesImpl::IsNonForcedFull() {
|
| + return loaded_ && cache_->GetNumNonForcedURLs() >= kNonForcedTopSitesNumber;
|
| +}
|
| +
|
| +bool TopSitesImpl::IsForcedFull() {
|
| + return loaded_ && cache_->GetNumForcedURLs() >= kForcedTopSitesNumber;
|
| }
|
|
|
| TopSitesImpl::~TopSitesImpl() {
|
| @@ -480,9 +527,10 @@ bool TopSitesImpl::SetPageThumbnailEncoded(
|
| return false;
|
|
|
| size_t index = cache_->GetURLIndex(url);
|
| + int url_rank = index - cache_->GetNumForcedURLs();
|
| const MostVisitedURL& most_visited = cache_->top_sites()[index];
|
| backend_->SetPageThumbnail(most_visited,
|
| - index,
|
| + url_rank < 0 ? -1 : url_rank,
|
| *(cache_->GetImage(most_visited.url)));
|
| return true;
|
| }
|
| @@ -559,11 +607,12 @@ bool TopSitesImpl::loaded() const {
|
| return loaded_;
|
| }
|
|
|
| -bool TopSitesImpl::AddPrepopulatedPages(MostVisitedURLList* urls) {
|
| +bool TopSitesImpl::AddPrepopulatedPages(MostVisitedURLList* urls,
|
| + size_t num_forced_urls) {
|
| bool added = false;
|
| MostVisitedURLList prepopulate_urls = GetPrepopulatePages();
|
| for (size_t i = 0; i < prepopulate_urls.size(); ++i) {
|
| - if (urls->size() < kTopSitesNumber &&
|
| + if (urls->size() - num_forced_urls < kNonForcedTopSitesNumber &&
|
| IndexOf(*urls, prepopulate_urls[i].url) == -1) {
|
| urls->push_back(prepopulate_urls[i]);
|
| added = true;
|
| @@ -572,6 +621,44 @@ bool TopSitesImpl::AddPrepopulatedPages(MostVisitedURLList* urls) {
|
| return added;
|
| }
|
|
|
| +size_t TopSitesImpl::MergeCachedForcedURLs(MostVisitedURLList* new_list) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + // Add all the new URLs for quick lookup. Take that opportunity to count the
|
| + // number of forced URLs in |new_list|.
|
| + std::set<GURL> all_new_urls;
|
| + size_t num_forced = 0;
|
| + for (size_t i = 0; i < new_list->size(); ++i) {
|
| + all_new_urls.insert((*new_list)[i].url);
|
| + if (!(*new_list)[i].last_forced_time.is_null())
|
| + ++num_forced;
|
| + }
|
| +
|
| + // Keep the forced URLs from |cache_| that are not found in |new_list|.
|
| + MostVisitedURLList filtered_forced_urls;
|
| + for (size_t i = 0; i < cache_->GetNumForcedURLs(); ++i) {
|
| + if (all_new_urls.find(cache_->top_sites()[i].url) == all_new_urls.end())
|
| + filtered_forced_urls.push_back(cache_->top_sites()[i]);
|
| + }
|
| + num_forced += filtered_forced_urls.size();
|
| +
|
| + // Prepend forced URLs and sort in order of ascending |last_forced_time|.
|
| + new_list->insert(new_list->begin(), filtered_forced_urls.begin(),
|
| + filtered_forced_urls.end());
|
| + std::inplace_merge(
|
| + new_list->begin(), new_list->begin() + filtered_forced_urls.size(),
|
| + new_list->begin() + num_forced, ForcedURLComparator);
|
| +
|
| + // Drop older forced URLs if the list overflows. Since forced URLs are always
|
| + // sort in increasing order of |last_forced_time|, drop the first ones.
|
| + if (num_forced > kForcedTopSitesNumber) {
|
| + new_list->erase(new_list->begin(),
|
| + new_list->begin() + (num_forced - kForcedTopSitesNumber));
|
| + num_forced = kForcedTopSitesNumber;
|
| + }
|
| +
|
| + return num_forced;
|
| +}
|
| +
|
| void TopSitesImpl::ApplyBlacklist(const MostVisitedURLList& urls,
|
| MostVisitedURLList* out) {
|
| // Log the number of times ApplyBlacklist is called so we can compute the
|
| @@ -581,9 +668,23 @@ void TopSitesImpl::ApplyBlacklist(const MostVisitedURLList& urls,
|
| UMA_HISTOGRAM_BOOLEAN("TopSites.NumberOfApplyBlacklist", true);
|
| UMA_HISTOGRAM_COUNTS_100("TopSites.NumberOfBlacklistedItems",
|
| (blacklist ? blacklist->size() : 0));
|
| - for (size_t i = 0; i < urls.size() && i < kTopSitesNumber; ++i) {
|
| - if (!IsBlacklisted(urls[i].url))
|
| + size_t num_non_forced_urls = 0;
|
| + size_t num_forced_urls = 0;
|
| + for (size_t i = 0; i < urls.size(); ++i) {
|
| + if (!IsBlacklisted(urls[i].url)) {
|
| + if (urls[i].last_forced_time.is_null()) {
|
| + // Non-forced URL.
|
| + if (num_non_forced_urls >= kNonForcedTopSitesNumber)
|
| + continue;
|
| + num_non_forced_urls++;
|
| + } else {
|
| + // Forced URL.
|
| + if (num_forced_urls >= kForcedTopSitesNumber)
|
| + continue;
|
| + num_forced_urls++;
|
| + }
|
| out->push_back(urls[i]);
|
| + }
|
| }
|
| }
|
|
|
| @@ -638,7 +739,7 @@ void TopSitesImpl::Observe(int type,
|
| content::Source<NavigationController>(source).ptr();
|
| Profile* profile = Profile::FromBrowserContext(
|
| controller->GetWebContents()->GetBrowserContext());
|
| - if (profile == profile_ && !IsFull()) {
|
| + if (profile == profile_ && !IsNonForcedFull()) {
|
| content::LoadCommittedDetails* load_details =
|
| content::Details<content::LoadCommittedDetails>(details).ptr();
|
| if (!load_details)
|
| @@ -657,7 +758,8 @@ void TopSitesImpl::SetTopSites(const MostVisitedURLList& new_top_sites) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
|
| MostVisitedURLList top_sites(new_top_sites);
|
| - AddPrepopulatedPages(&top_sites);
|
| + size_t num_forced_urls = MergeCachedForcedURLs(&top_sites);
|
| + AddPrepopulatedPages(&top_sites, num_forced_urls);
|
|
|
| TopSitesDelta delta;
|
| DiffMostVisited(cache_->top_sites(), top_sites, &delta);
|
| @@ -691,7 +793,7 @@ void TopSitesImpl::SetTopSites(const MostVisitedURLList& new_top_sites) {
|
| }
|
| }
|
|
|
| - if (top_sites.size() >= kTopSitesNumber)
|
| + if (top_sites.size() - num_forced_urls >= kNonForcedTopSitesNumber)
|
| temp_images_.clear();
|
|
|
| ResetThreadSafeCache();
|
| @@ -708,13 +810,14 @@ int TopSitesImpl::num_results_to_request_from_history() const {
|
|
|
| const DictionaryValue* blacklist =
|
| profile_->GetPrefs()->GetDictionary(prefs::kNtpMostVisitedURLsBlacklist);
|
| - return kTopSitesNumber + (blacklist ? blacklist->size() : 0);
|
| + return kNonForcedTopSitesNumber + (blacklist ? blacklist->size() : 0);
|
| }
|
|
|
| void TopSitesImpl::MoveStateToLoaded() {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
|
| - MostVisitedURLList filtered_urls;
|
| + MostVisitedURLList filtered_urls_all;
|
| + MostVisitedURLList filtered_urls_nonforced;
|
| PendingCallbacks pending_callbacks;
|
| {
|
| base::AutoLock lock(lock_);
|
| @@ -726,13 +829,18 @@ void TopSitesImpl::MoveStateToLoaded() {
|
| // Now that we're loaded we can service the queued up callbacks. Copy them
|
| // here and service them outside the lock.
|
| if (!pending_callbacks_.empty()) {
|
| - filtered_urls = thread_safe_cache_->top_sites();
|
| + // We always filter out forced URLs because callers of GetMostVisitedURLs
|
| + // are not interested in them.
|
| + filtered_urls_all = thread_safe_cache_->top_sites();
|
| + filtered_urls_nonforced.assign(thread_safe_cache_->top_sites().begin() +
|
| + thread_safe_cache_->GetNumForcedURLs(),
|
| + thread_safe_cache_->top_sites().end());
|
| pending_callbacks.swap(pending_callbacks_);
|
| }
|
| }
|
|
|
| for (size_t i = 0; i < pending_callbacks.size(); i++)
|
| - pending_callbacks[i].Run(filtered_urls);
|
| + pending_callbacks[i].Run(filtered_urls_all, filtered_urls_nonforced);
|
|
|
| content::NotificationService::current()->Notify(
|
| chrome::NOTIFICATION_TOP_SITES_LOADED,
|
|
|