OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 #include "chrome/browser/history/top_sites.h" | 5 #include "chrome/browser/history/top_sites.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <set> | 8 #include <set> |
9 | 9 |
10 #include "app/l10n_util.h" | 10 #include "app/l10n_util.h" |
(...skipping 21 matching lines...) Expand all Loading... |
32 #include "gfx/codec/jpeg_codec.h" | 32 #include "gfx/codec/jpeg_codec.h" |
33 #include "grit/chromium_strings.h" | 33 #include "grit/chromium_strings.h" |
34 #include "grit/generated_resources.h" | 34 #include "grit/generated_resources.h" |
35 #include "grit/locale_settings.h" | 35 #include "grit/locale_settings.h" |
36 #include "third_party/skia/include/core/SkBitmap.h" | 36 #include "third_party/skia/include/core/SkBitmap.h" |
37 | 37 |
38 namespace history { | 38 namespace history { |
39 | 39 |
40 // How many top sites to store in the cache. | 40 // How many top sites to store in the cache. |
41 static const size_t kTopSitesNumber = 20; | 41 static const size_t kTopSitesNumber = 20; |
| 42 |
| 43 // Max number of temporary images we'll cache. See comment above |
| 44 // temp_images_ for details. |
| 45 static const size_t kMaxTempTopImages = 8; |
| 46 |
42 static const size_t kTopSitesShown = 8; | 47 static const size_t kTopSitesShown = 8; |
43 static const int kDaysOfHistory = 90; | 48 static const int kDaysOfHistory = 90; |
44 // Time from startup to first HistoryService query. | 49 // Time from startup to first HistoryService query. |
45 static const int64 kUpdateIntervalSecs = 15; | 50 static const int64 kUpdateIntervalSecs = 15; |
46 // Intervals between requests to HistoryService. | 51 // Intervals between requests to HistoryService. |
47 static const int64 kMinUpdateIntervalMinutes = 1; | 52 static const int64 kMinUpdateIntervalMinutes = 1; |
48 static const int64 kMaxUpdateIntervalMinutes = 60; | 53 static const int64 kMaxUpdateIntervalMinutes = 60; |
49 | 54 |
50 // IDs of the sites we force into top sites. | 55 // IDs of the sites we force into top sites. |
51 static const int kPrepopulatePageIDs[] = | 56 static const int kPrepopulatePageIDs[] = |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
193 } | 198 } |
194 | 199 |
195 if (!HistoryService::CanAddURL(url)) | 200 if (!HistoryService::CanAddURL(url)) |
196 return false; // It's not a real webpage. | 201 return false; // It's not a real webpage. |
197 | 202 |
198 scoped_refptr<RefCountedBytes> thumbnail_data; | 203 scoped_refptr<RefCountedBytes> thumbnail_data; |
199 if (!EncodeBitmap(thumbnail, &thumbnail_data)) | 204 if (!EncodeBitmap(thumbnail, &thumbnail_data)) |
200 return false; | 205 return false; |
201 | 206 |
202 if (add_temp_thumbnail) { | 207 if (add_temp_thumbnail) { |
| 208 // Always remove the existing entry and then add it back. That way if we end |
| 209 // up with too many temp thumbnails we'll prune the oldest first. |
| 210 RemoveTemporaryThumbnailByURL(url); |
203 AddTemporaryThumbnail(url, thumbnail_data, score); | 211 AddTemporaryThumbnail(url, thumbnail_data, score); |
204 return true; | 212 return true; |
205 } | 213 } |
206 | 214 |
207 return SetPageThumbnailEncoded(url, thumbnail_data, score); | 215 return SetPageThumbnailEncoded(url, thumbnail_data, score); |
208 } | 216 } |
209 | 217 |
210 void TopSites::GetMostVisitedURLs(CancelableRequestConsumer* consumer, | 218 void TopSites::GetMostVisitedURLs(CancelableRequestConsumer* consumer, |
211 GetTopSitesCallback* callback) { | 219 GetTopSitesCallback* callback) { |
212 // WARNING: this may be invoked on any thread. | 220 // WARNING: this may be invoked on any thread. |
(...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
480 index, | 488 index, |
481 *(cache_->GetImage(most_visited.url))); | 489 *(cache_->GetImage(most_visited.url))); |
482 return true; | 490 return true; |
483 } | 491 } |
484 | 492 |
485 // static | 493 // static |
486 bool TopSites::EncodeBitmap(const SkBitmap& bitmap, | 494 bool TopSites::EncodeBitmap(const SkBitmap& bitmap, |
487 scoped_refptr<RefCountedBytes>* bytes) { | 495 scoped_refptr<RefCountedBytes>* bytes) { |
488 *bytes = new RefCountedBytes(); | 496 *bytes = new RefCountedBytes(); |
489 SkAutoLockPixels bitmap_lock(bitmap); | 497 SkAutoLockPixels bitmap_lock(bitmap); |
490 return gfx::JPEGCodec::Encode( | 498 std::vector<unsigned char> data; |
491 reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)), | 499 if (!gfx::JPEGCodec::Encode( |
492 gfx::JPEGCodec::FORMAT_BGRA, bitmap.width(), | 500 reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)), |
493 bitmap.height(), | 501 gfx::JPEGCodec::FORMAT_BGRA, bitmap.width(), |
494 static_cast<int>(bitmap.rowBytes()), 90, | 502 bitmap.height(), |
495 &((*bytes)->data)); | 503 static_cast<int>(bitmap.rowBytes()), 90, |
| 504 &data)) { |
| 505 return false; |
| 506 } |
| 507 // As we're going to cache this data, make sure the vector is only as big as |
| 508 // it needs to be. |
| 509 (*bytes)->data = data; |
| 510 return true; |
| 511 } |
| 512 |
| 513 void TopSites::RemoveTemporaryThumbnailByURL(const GURL& url) { |
| 514 for (TempImages::iterator i = temp_images_.begin(); i != temp_images_.end(); |
| 515 ++i) { |
| 516 if (i->first == url) { |
| 517 temp_images_.erase(i); |
| 518 return; |
| 519 } |
| 520 } |
496 } | 521 } |
497 | 522 |
498 void TopSites::AddTemporaryThumbnail(const GURL& url, | 523 void TopSites::AddTemporaryThumbnail(const GURL& url, |
499 const RefCountedBytes* thumbnail, | 524 const RefCountedBytes* thumbnail, |
500 const ThumbnailScore& score) { | 525 const ThumbnailScore& score) { |
501 Images& img = temp_thumbnails_map_[url]; | 526 if (temp_images_.size() == kMaxTempTopImages) |
502 img.thumbnail = const_cast<RefCountedBytes*>(thumbnail); | 527 temp_images_.erase(temp_images_.begin()); |
503 img.thumbnail_score = score; | 528 |
| 529 TempImage image; |
| 530 image.first = url; |
| 531 image.second.thumbnail = const_cast<RefCountedBytes*>(thumbnail); |
| 532 image.second.thumbnail_score = score; |
| 533 temp_images_.push_back(image); |
504 } | 534 } |
505 | 535 |
506 void TopSites::StartQueryForMostVisited() { | 536 void TopSites::StartQueryForMostVisited() { |
507 if (!profile_) | 537 if (!profile_) |
508 return; | 538 return; |
509 | 539 |
510 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | 540 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); |
511 // |hs| may be null during unit tests. | 541 // |hs| may be null during unit tests. |
512 if (hs) { | 542 if (hs) { |
513 hs->QueryMostVisitedURLs( | 543 hs->QueryMostVisitedURLs( |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
696 } | 726 } |
697 StartQueryForMostVisited(); | 727 StartQueryForMostVisited(); |
698 } else if (type == NotificationType::NAV_ENTRY_COMMITTED) { | 728 } else if (type == NotificationType::NAV_ENTRY_COMMITTED) { |
699 if (cache_->top_sites().size() < kTopSitesNumber) { | 729 if (cache_->top_sites().size() < kTopSitesNumber) { |
700 NavigationController::LoadCommittedDetails* load_details = | 730 NavigationController::LoadCommittedDetails* load_details = |
701 Details<NavigationController::LoadCommittedDetails>(details).ptr(); | 731 Details<NavigationController::LoadCommittedDetails>(details).ptr(); |
702 if (!load_details) | 732 if (!load_details) |
703 return; | 733 return; |
704 const GURL& url = load_details->entry->url(); | 734 const GURL& url = load_details->entry->url(); |
705 if (!cache_->IsKnownURL(url) && HistoryService::CanAddURL(url)) { | 735 if (!cache_->IsKnownURL(url) && HistoryService::CanAddURL(url)) { |
706 // Ideally we would just invoke StartQueryForMostVisited, but at the | 736 // To avoid slamming history we throttle requests when the url updates. |
707 // time this is invoked history hasn't been updated, which means if we | 737 // To do otherwise negatively impacts perf tests. |
708 // invoked StartQueryForMostVisited now we could get stale data. | 738 RestartQueryForTopSitesTimer(GetUpdateDelay()); |
709 RestartQueryForTopSitesTimer(base::TimeDelta::FromMilliseconds(1)); | |
710 } | 739 } |
711 } | 740 } |
712 } | 741 } |
713 } | 742 } |
714 | 743 |
715 void TopSites::SetTopSites(const MostVisitedURLList& new_top_sites) { | 744 void TopSites::SetTopSites(const MostVisitedURLList& new_top_sites) { |
716 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 745 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
717 | 746 |
718 MostVisitedURLList top_sites(new_top_sites); | 747 MostVisitedURLList top_sites(new_top_sites); |
719 AddPrepopulatedPages(&top_sites); | 748 AddPrepopulatedPages(&top_sites); |
720 | 749 |
721 TopSitesDelta delta; | 750 TopSitesDelta delta; |
722 DiffMostVisited(cache_->top_sites(), top_sites, &delta); | 751 DiffMostVisited(cache_->top_sites(), top_sites, &delta); |
723 if (!delta.deleted.empty() || !delta.added.empty() || !delta.moved.empty()) | 752 if (!delta.deleted.empty() || !delta.added.empty() || !delta.moved.empty()) |
724 backend_->UpdateTopSites(delta); | 753 backend_->UpdateTopSites(delta); |
725 | 754 |
726 last_num_urls_changed_ = delta.added.size() + delta.moved.size(); | 755 last_num_urls_changed_ = delta.added.size() + delta.moved.size(); |
727 | 756 |
728 // We always do the following steps (setting top sites in cache, and resetting | 757 // We always do the following steps (setting top sites in cache, and resetting |
729 // thread safe cache ...) as this method is invoked during startup at which | 758 // thread safe cache ...) as this method is invoked during startup at which |
730 // point the caches haven't been updated yet. | 759 // point the caches haven't been updated yet. |
731 cache_->SetTopSites(top_sites); | 760 cache_->SetTopSites(top_sites); |
732 | 761 |
733 // See if we have any tmp thumbnails for the new sites. | 762 // See if we have any tmp thumbnails for the new sites. |
734 if (!temp_thumbnails_map_.empty()) { | 763 if (!temp_images_.empty()) { |
735 for (size_t i = 0; i < top_sites.size(); ++i) { | 764 for (size_t i = 0; i < top_sites.size(); ++i) { |
736 const MostVisitedURL& mv = top_sites[i]; | 765 const MostVisitedURL& mv = top_sites[i]; |
737 GURL canonical_url = cache_->GetCanonicalURL(mv.url); | 766 GURL canonical_url = cache_->GetCanonicalURL(mv.url); |
738 for (std::map<GURL, Images>::iterator it = temp_thumbnails_map_.begin(); | 767 // At the time we get the thumbnail redirects aren't known, so we have to |
739 it != temp_thumbnails_map_.end(); ++it) { | 768 // iterate through all the images. |
740 // Must map all temp URLs to canonical ones. | 769 for (TempImages::iterator it = temp_images_.begin(); |
741 // temp_thumbnails_map_ contains non-canonical URLs, because | 770 it != temp_images_.end(); ++it) { |
742 // when we add a temp thumbnail, redirect chain is not known. | |
743 // This is slow, but temp_thumbnails_map_ should have very few URLs. | |
744 if (canonical_url == cache_->GetCanonicalURL(it->first)) { | 771 if (canonical_url == cache_->GetCanonicalURL(it->first)) { |
745 SetPageThumbnailEncoded(mv.url, | 772 SetPageThumbnailEncoded(mv.url, |
746 it->second.thumbnail, | 773 it->second.thumbnail, |
747 it->second.thumbnail_score); | 774 it->second.thumbnail_score); |
748 temp_thumbnails_map_.erase(it); | 775 temp_images_.erase(it); |
749 break; | 776 break; |
750 } | 777 } |
751 } | 778 } |
752 } | 779 } |
753 } | 780 } |
754 | 781 |
755 if (top_sites.size() >= kTopSitesNumber) | 782 if (top_sites.size() >= kTopSitesNumber) |
756 temp_thumbnails_map_.clear(); | 783 temp_images_.clear(); |
757 | 784 |
758 ResetThreadSafeCache(); | 785 ResetThreadSafeCache(); |
759 ResetThreadSafeImageCache(); | 786 ResetThreadSafeImageCache(); |
760 | 787 |
761 // Restart the timer that queries history for top sites. This is done to | 788 // Restart the timer that queries history for top sites. This is done to |
762 // ensure we stay in sync with history. | 789 // ensure we stay in sync with history. |
763 RestartQueryForTopSitesTimer(GetUpdateDelay()); | 790 RestartQueryForTopSitesTimer(GetUpdateDelay()); |
764 } | 791 } |
765 | 792 |
766 int TopSites::num_results_to_request_from_history() const { | 793 int TopSites::num_results_to_request_from_history() const { |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
803 thread_safe_cache_->SetTopSites(cached); | 830 thread_safe_cache_->SetTopSites(cached); |
804 } | 831 } |
805 | 832 |
806 void TopSites::ResetThreadSafeImageCache() { | 833 void TopSites::ResetThreadSafeImageCache() { |
807 AutoLock lock(lock_); | 834 AutoLock lock(lock_); |
808 thread_safe_cache_->SetThumbnails(cache_->images()); | 835 thread_safe_cache_->SetThumbnails(cache_->images()); |
809 thread_safe_cache_->RemoveUnreferencedThumbnails(); | 836 thread_safe_cache_->RemoveUnreferencedThumbnails(); |
810 } | 837 } |
811 | 838 |
812 void TopSites::RestartQueryForTopSitesTimer(base::TimeDelta delta) { | 839 void TopSites::RestartQueryForTopSitesTimer(base::TimeDelta delta) { |
| 840 if (timer_.IsRunning() && ((timer_start_time_ + timer_.GetCurrentDelay()) < |
| 841 (base::TimeTicks::Now() + delta))) { |
| 842 return; |
| 843 } |
| 844 |
| 845 timer_start_time_ = base::TimeTicks::Now(); |
813 timer_.Stop(); | 846 timer_.Stop(); |
814 timer_.Start(delta, this, &TopSites::StartQueryForMostVisited); | 847 timer_.Start(delta, this, &TopSites::StartQueryForMostVisited); |
815 } | 848 } |
816 | 849 |
817 void TopSites::OnHistoryMigrationWrittenToDisk(TopSitesBackend::Handle handle) { | 850 void TopSites::OnHistoryMigrationWrittenToDisk(TopSitesBackend::Handle handle) { |
818 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 851 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
819 | 852 |
820 if (!profile_) | 853 if (!profile_) |
821 return; | 854 return; |
822 | 855 |
(...skipping 28 matching lines...) Expand all Loading... |
851 base::TimeDelta::FromSeconds(kUpdateIntervalSecs)); | 884 base::TimeDelta::FromSeconds(kUpdateIntervalSecs)); |
852 } else { | 885 } else { |
853 // The top sites file didn't exist or is the wrong version. We need to wait | 886 // The top sites file didn't exist or is the wrong version. We need to wait |
854 // for history to finish loading to know if we really needed to migrate. | 887 // for history to finish loading to know if we really needed to migrate. |
855 if (history_state_ == HISTORY_LOADED) { | 888 if (history_state_ == HISTORY_LOADED) { |
856 top_sites_state_ = TOP_SITES_LOADED; | 889 top_sites_state_ = TOP_SITES_LOADED; |
857 SetTopSites(MostVisitedURLList()); | 890 SetTopSites(MostVisitedURLList()); |
858 MoveStateToLoaded(); | 891 MoveStateToLoaded(); |
859 } else { | 892 } else { |
860 top_sites_state_ = TOP_SITES_LOADED_WAITING_FOR_HISTORY; | 893 top_sites_state_ = TOP_SITES_LOADED_WAITING_FOR_HISTORY; |
861 // Ask for history just in case it hasn't been load yet. When history | 894 // Ask for history just in case it hasn't been loaded yet. When history |
862 // finishes loading we'll do migration and/or move to loaded. | 895 // finishes loading we'll do migration and/or move to loaded. |
863 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | 896 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); |
864 } | 897 } |
865 } | 898 } |
866 } | 899 } |
867 | 900 |
868 void TopSites::OnTopSitesAvailableFromHistory( | 901 void TopSites::OnTopSitesAvailableFromHistory( |
869 CancelableRequestProvider::Handle handle, | 902 CancelableRequestProvider::Handle handle, |
870 MostVisitedURLList pages) { | 903 MostVisitedURLList pages) { |
871 SetTopSites(pages); | 904 SetTopSites(pages); |
872 } | 905 } |
873 | 906 |
874 } // namespace history | 907 } // namespace history |
OLD | NEW |