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 #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 "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 }; | 134 }; |
135 | 135 |
136 } // namespace | 136 } // namespace |
137 | 137 |
138 TopSites::TopSites(Profile* profile) | 138 TopSites::TopSites(Profile* profile) |
139 : backend_(NULL), | 139 : backend_(NULL), |
140 cache_(new TopSitesCache()), | 140 cache_(new TopSitesCache()), |
141 thread_safe_cache_(new TopSitesCache()), | 141 thread_safe_cache_(new TopSitesCache()), |
142 profile_(profile), | 142 profile_(profile), |
143 last_num_urls_changed_(0), | 143 last_num_urls_changed_(0), |
144 pinned_urls_(NULL), | |
145 history_state_(HISTORY_LOADING), | 144 history_state_(HISTORY_LOADING), |
146 top_sites_state_(TOP_SITES_LOADING), | 145 top_sites_state_(TOP_SITES_LOADING), |
147 loaded_(false) { | 146 loaded_(false) { |
148 if (!profile_) | 147 if (!profile_) |
149 return; | 148 return; |
150 | 149 |
151 if (content::NotificationService::current()) { | 150 if (content::NotificationService::current()) { |
152 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, | 151 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, |
153 content::Source<Profile>(profile_)); | 152 content::Source<Profile>(profile_)); |
154 // Listen for any nav commits. We'll ignore those not related to this | 153 // Listen for any nav commits. We'll ignore those not related to this |
155 // profile when we get the notification. | 154 // profile when we get the notification. |
156 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, | 155 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, |
157 content::NotificationService::AllSources()); | 156 content::NotificationService::AllSources()); |
158 } | 157 } |
159 | |
160 // We create update objects here to be sure that dictionaries are created | |
161 // in the user preferences. | |
162 DictionaryPrefUpdate(profile_->GetPrefs(), | |
163 prefs::kNtpMostVisitedPinnedURLs).Get(); | |
164 | |
165 // Now the dictionaries are guaranteed to exist and we can cache pointers | |
166 // to them. | |
167 pinned_urls_ = | |
168 profile_->GetPrefs()->GetDictionary(prefs::kNtpMostVisitedPinnedURLs); | |
169 DCHECK(pinned_urls_ != NULL); | |
170 } | 158 } |
171 | 159 |
172 void TopSites::Init(const FilePath& db_name) { | 160 void TopSites::Init(const FilePath& db_name) { |
173 // Create the backend here, rather than in the constructor, so that | 161 // Create the backend here, rather than in the constructor, so that |
174 // unit tests that do not need the backend can run without a problem. | 162 // unit tests that do not need the backend can run without a problem. |
175 backend_ = new TopSitesBackend; | 163 backend_ = new TopSitesBackend; |
176 backend_->Init(db_name); | 164 backend_->Init(db_name); |
177 backend_->GetMostVisitedThumbnails( | 165 backend_->GetMostVisitedThumbnails( |
178 &top_sites_consumer_, | 166 &top_sites_consumer_, |
179 base::Bind(&TopSites::OnGotMostVisitedThumbnails, | 167 base::Bind(&TopSites::OnGotMostVisitedThumbnails, |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
307 // This can happen if history was unloaded then loaded again. | 295 // This can happen if history was unloaded then loaded again. |
308 return; | 296 return; |
309 } | 297 } |
310 | 298 |
311 history_state_ = HISTORY_MIGRATING; | 299 history_state_ = HISTORY_MIGRATING; |
312 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)->ScheduleDBTask( | 300 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)->ScheduleDBTask( |
313 new LoadThumbnailsFromHistoryTask( | 301 new LoadThumbnailsFromHistoryTask( |
314 this, | 302 this, |
315 num_results_to_request_from_history()), | 303 num_results_to_request_from_history()), |
316 &history_consumer_); | 304 &history_consumer_); |
317 MigratePinnedURLs(); | |
318 } | 305 } |
319 | 306 |
320 void TopSites::FinishHistoryMigration(const ThumbnailMigration& data) { | 307 void TopSites::FinishHistoryMigration(const ThumbnailMigration& data) { |
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 308 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
322 DCHECK_EQ(history_state_, HISTORY_MIGRATING); | 309 DCHECK_EQ(history_state_, HISTORY_MIGRATING); |
323 | 310 |
324 history_state_ = HISTORY_LOADED; | 311 history_state_ = HISTORY_LOADED; |
325 | 312 |
326 SetTopSites(data.most_visited); | 313 SetTopSites(data.most_visited); |
327 | 314 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
381 | 368 |
382 bool TopSites::HasBlacklistedItems() const { | 369 bool TopSites::HasBlacklistedItems() const { |
383 const DictionaryValue* blacklist = | 370 const DictionaryValue* blacklist = |
384 profile_->GetPrefs()->GetDictionary(prefs::kNtpMostVisitedURLsBlacklist); | 371 profile_->GetPrefs()->GetDictionary(prefs::kNtpMostVisitedURLsBlacklist); |
385 return blacklist && !blacklist->empty(); | 372 return blacklist && !blacklist->empty(); |
386 } | 373 } |
387 | 374 |
388 void TopSites::AddBlacklistedURL(const GURL& url) { | 375 void TopSites::AddBlacklistedURL(const GURL& url) { |
389 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 376 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
390 | 377 |
391 RemovePinnedURL(url); | |
392 Value* dummy = Value::CreateNullValue(); | 378 Value* dummy = Value::CreateNullValue(); |
393 { | 379 { |
394 DictionaryPrefUpdate update(profile_->GetPrefs(), | 380 DictionaryPrefUpdate update(profile_->GetPrefs(), |
395 prefs::kNtpMostVisitedURLsBlacklist); | 381 prefs::kNtpMostVisitedURLsBlacklist); |
396 DictionaryValue* blacklist = update.Get(); | 382 DictionaryValue* blacklist = update.Get(); |
397 blacklist->SetWithoutPathExpansion(GetURLHash(url), dummy); | 383 blacklist->SetWithoutPathExpansion(GetURLHash(url), dummy); |
398 } | 384 } |
399 | 385 |
400 ResetThreadSafeCache(); | 386 ResetThreadSafeCache(); |
401 NotifyTopSitesChanged(); | 387 NotifyTopSitesChanged(); |
(...skipping 23 matching lines...) Expand all Loading... |
425 { | 411 { |
426 DictionaryPrefUpdate update(profile_->GetPrefs(), | 412 DictionaryPrefUpdate update(profile_->GetPrefs(), |
427 prefs::kNtpMostVisitedURLsBlacklist); | 413 prefs::kNtpMostVisitedURLsBlacklist); |
428 DictionaryValue* blacklist = update.Get(); | 414 DictionaryValue* blacklist = update.Get(); |
429 blacklist->Clear(); | 415 blacklist->Clear(); |
430 } | 416 } |
431 ResetThreadSafeCache(); | 417 ResetThreadSafeCache(); |
432 NotifyTopSitesChanged(); | 418 NotifyTopSitesChanged(); |
433 } | 419 } |
434 | 420 |
435 void TopSites::AddPinnedURL(const GURL& url, size_t pinned_index) { | |
436 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
437 | |
438 GURL old; | |
439 if (GetPinnedURLAtIndex(pinned_index, &old)) | |
440 RemovePinnedURL(old); | |
441 | |
442 if (IsURLPinned(url)) | |
443 RemovePinnedURL(url); | |
444 | |
445 Value* index = Value::CreateIntegerValue(pinned_index); | |
446 | |
447 { | |
448 DictionaryPrefUpdate update(profile_->GetPrefs(), | |
449 prefs::kNtpMostVisitedPinnedURLs); | |
450 DictionaryValue* pinned_urls = update.Get(); | |
451 pinned_urls->SetWithoutPathExpansion(GetURLString(url), index); | |
452 } | |
453 | |
454 ResetThreadSafeCache(); | |
455 NotifyTopSitesChanged(); | |
456 } | |
457 | |
458 bool TopSites::IsURLPinned(const GURL& url) { | |
459 int tmp; | |
460 return pinned_urls_->GetIntegerWithoutPathExpansion(GetURLString(url), &tmp); | |
461 } | |
462 | |
463 void TopSites::RemovePinnedURL(const GURL& url) { | |
464 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
465 | |
466 { | |
467 DictionaryPrefUpdate update(profile_->GetPrefs(), | |
468 prefs::kNtpMostVisitedPinnedURLs); | |
469 DictionaryValue* pinned_urls = update.Get(); | |
470 pinned_urls->RemoveWithoutPathExpansion(GetURLString(url), NULL); | |
471 } | |
472 | |
473 ResetThreadSafeCache(); | |
474 NotifyTopSitesChanged(); | |
475 } | |
476 | |
477 bool TopSites::GetPinnedURLAtIndex(size_t index, GURL* url) { | |
478 for (DictionaryValue::key_iterator it = pinned_urls_->begin_keys(); | |
479 it != pinned_urls_->end_keys(); ++it) { | |
480 int current_index; | |
481 if (pinned_urls_->GetIntegerWithoutPathExpansion(*it, ¤t_index)) { | |
482 if (static_cast<size_t>(current_index) == index) { | |
483 *url = GURL(*it); | |
484 return true; | |
485 } | |
486 } | |
487 } | |
488 return false; | |
489 } | |
490 | |
491 void TopSites::Shutdown() { | 421 void TopSites::Shutdown() { |
492 profile_ = NULL; | 422 profile_ = NULL; |
493 // Cancel all requests so that the service doesn't callback to us after we've | 423 // Cancel all requests so that the service doesn't callback to us after we've |
494 // invoked Shutdown (this could happen if we have a pending request and | 424 // invoked Shutdown (this could happen if we have a pending request and |
495 // Shutdown is invoked). | 425 // Shutdown is invoked). |
496 history_consumer_.CancelAllRequests(); | 426 history_consumer_.CancelAllRequests(); |
497 top_sites_consumer_.CancelAllRequests(); | 427 top_sites_consumer_.CancelAllRequests(); |
498 backend_->Shutdown(); | 428 backend_->Shutdown(); |
499 } | 429 } |
500 | 430 |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
691 for (size_t i = 0; i < prepopulate_urls.size(); ++i) { | 621 for (size_t i = 0; i < prepopulate_urls.size(); ++i) { |
692 if (urls->size() < kTopSitesNumber && | 622 if (urls->size() < kTopSitesNumber && |
693 IndexOf(*urls, prepopulate_urls[i].url) == -1) { | 623 IndexOf(*urls, prepopulate_urls[i].url) == -1) { |
694 urls->push_back(prepopulate_urls[i]); | 624 urls->push_back(prepopulate_urls[i]); |
695 added = true; | 625 added = true; |
696 } | 626 } |
697 } | 627 } |
698 return added; | 628 return added; |
699 } | 629 } |
700 | 630 |
701 void TopSites::MigratePinnedURLs() { | 631 void TopSites::ApplyBlacklist(const MostVisitedURLList& urls, |
702 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 632 MostVisitedURLList* out) { |
703 | 633 for (size_t i = 0; i < urls.size() && i < kTopSitesNumber; ++i) { |
704 std::map<GURL, size_t> tmp_map; | |
705 for (DictionaryValue::key_iterator it = pinned_urls_->begin_keys(); | |
706 it != pinned_urls_->end_keys(); ++it) { | |
707 Value* value; | |
708 if (!pinned_urls_->GetWithoutPathExpansion(*it, &value)) | |
709 continue; | |
710 | |
711 if (value->IsType(DictionaryValue::TYPE_DICTIONARY)) { | |
712 DictionaryValue* dict = static_cast<DictionaryValue*>(value); | |
713 std::string url_string; | |
714 int index; | |
715 if (dict->GetString("url", &url_string) && | |
716 dict->GetInteger("index", &index)) | |
717 tmp_map[GURL(url_string)] = index; | |
718 } | |
719 } | |
720 | |
721 { | |
722 DictionaryPrefUpdate update(profile_->GetPrefs(), | |
723 prefs::kNtpMostVisitedPinnedURLs); | |
724 DictionaryValue* pinned_urls = update.Get(); | |
725 pinned_urls->Clear(); | |
726 } | |
727 | |
728 for (std::map<GURL, size_t>::iterator it = tmp_map.begin(); | |
729 it != tmp_map.end(); ++it) | |
730 AddPinnedURL(it->first, it->second); | |
731 } | |
732 | |
733 void TopSites::ApplyBlacklistAndPinnedURLs(const MostVisitedURLList& urls, | |
734 MostVisitedURLList* out) { | |
735 MostVisitedURLList urls_copy; | |
736 for (size_t i = 0; i < urls.size(); i++) { | |
737 if (!IsBlacklisted(urls[i].url)) | 634 if (!IsBlacklisted(urls[i].url)) |
738 urls_copy.push_back(urls[i]); | 635 out->push_back(urls[i]); |
739 } | |
740 | |
741 for (size_t pinned_index = 0; pinned_index < kTopSitesNumber; | |
742 pinned_index++) { | |
743 GURL url; | |
744 bool found = GetPinnedURLAtIndex(pinned_index, &url); | |
745 if (!found) | |
746 continue; | |
747 | |
748 DCHECK(!url.is_empty()); | |
749 int cur_index = IndexOf(urls_copy, url); | |
750 MostVisitedURL tmp; | |
751 if (cur_index < 0) { | |
752 // Pinned URL not in urls. | |
753 tmp.url = url; | |
754 } else { | |
755 tmp = urls_copy[cur_index]; | |
756 urls_copy.erase(urls_copy.begin() + cur_index); | |
757 } | |
758 if (pinned_index > out->size()) | |
759 out->resize(pinned_index); // Add empty URLs as fillers. | |
760 out->insert(out->begin() + pinned_index, tmp); | |
761 } | |
762 | |
763 // Add non-pinned URLs in the empty spots. | |
764 size_t current_url = 0; // Index into the remaining URLs in urls_copy. | |
765 for (size_t i = 0; i < kTopSitesNumber && current_url < urls_copy.size(); | |
766 i++) { | |
767 if (i == out->size()) { | |
768 out->push_back(urls_copy[current_url]); | |
769 current_url++; | |
770 } else if (i < out->size()) { | |
771 if ((*out)[i].url.is_empty()) { | |
772 // Replace the filler | |
773 (*out)[i] = urls_copy[current_url]; | |
774 current_url++; | |
775 } | |
776 } else { | |
777 NOTREACHED(); | |
778 } | |
779 } | 636 } |
780 } | 637 } |
781 | 638 |
782 std::string TopSites::GetURLString(const GURL& url) { | 639 std::string TopSites::GetURLString(const GURL& url) { |
783 return cache_->GetCanonicalURL(url).spec(); | 640 return cache_->GetCanonicalURL(url).spec(); |
784 } | 641 } |
785 | 642 |
786 std::string TopSites::GetURLHash(const GURL& url) { | 643 std::string TopSites::GetURLHash(const GURL& url) { |
787 // We don't use canonical URLs here to be able to blacklist only one of | 644 // We don't use canonical URLs here to be able to blacklist only one of |
788 // the two 'duplicate' sites, e.g. 'gmail.com' and 'mail.google.com'. | 645 // the two 'duplicate' sites, e.g. 'gmail.com' and 'mail.google.com'. |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
830 if (cache_->IsKnownURL(*i)) | 687 if (cache_->IsKnownURL(*i)) |
831 indices_to_delete.insert(cache_->GetURLIndex(*i)); | 688 indices_to_delete.insert(cache_->GetURLIndex(*i)); |
832 } | 689 } |
833 | 690 |
834 if (indices_to_delete.empty()) | 691 if (indices_to_delete.empty()) |
835 return; | 692 return; |
836 | 693 |
837 MostVisitedURLList new_top_sites(cache_->top_sites()); | 694 MostVisitedURLList new_top_sites(cache_->top_sites()); |
838 for (std::set<size_t>::reverse_iterator i = indices_to_delete.rbegin(); | 695 for (std::set<size_t>::reverse_iterator i = indices_to_delete.rbegin(); |
839 i != indices_to_delete.rend(); i++) { | 696 i != indices_to_delete.rend(); i++) { |
840 size_t index = *i; | 697 new_top_sites.erase(new_top_sites.begin() + *i); |
841 RemovePinnedURL(new_top_sites[index].url); | |
842 new_top_sites.erase(new_top_sites.begin() + index); | |
843 } | 698 } |
844 SetTopSites(new_top_sites); | 699 SetTopSites(new_top_sites); |
845 } | 700 } |
846 StartQueryForMostVisited(); | 701 StartQueryForMostVisited(); |
847 } else if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED) { | 702 } else if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED) { |
848 NavigationController* controller = | 703 NavigationController* controller = |
849 content::Source<NavigationController>(source).ptr(); | 704 content::Source<NavigationController>(source).ptr(); |
850 Profile* profile = Profile::FromBrowserContext( | 705 Profile* profile = Profile::FromBrowserContext( |
851 controller->GetWebContents()->GetBrowserContext()); | 706 controller->GetWebContents()->GetBrowserContext()); |
852 if (profile == profile_ && !IsFull()) { | 707 if (profile == profile_ && !IsFull()) { |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
947 | 802 |
948 content::NotificationService::current()->Notify( | 803 content::NotificationService::current()->Notify( |
949 chrome::NOTIFICATION_TOP_SITES_LOADED, | 804 chrome::NOTIFICATION_TOP_SITES_LOADED, |
950 content::Source<Profile>(profile_), | 805 content::Source<Profile>(profile_), |
951 content::Details<TopSites>(this)); | 806 content::Details<TopSites>(this)); |
952 } | 807 } |
953 | 808 |
954 void TopSites::ResetThreadSafeCache() { | 809 void TopSites::ResetThreadSafeCache() { |
955 base::AutoLock lock(lock_); | 810 base::AutoLock lock(lock_); |
956 MostVisitedURLList cached; | 811 MostVisitedURLList cached; |
957 ApplyBlacklistAndPinnedURLs(cache_->top_sites(), &cached); | 812 ApplyBlacklist(cache_->top_sites(), &cached); |
958 thread_safe_cache_->SetTopSites(cached); | 813 thread_safe_cache_->SetTopSites(cached); |
959 } | 814 } |
960 | 815 |
961 void TopSites::ResetThreadSafeImageCache() { | 816 void TopSites::ResetThreadSafeImageCache() { |
962 base::AutoLock lock(lock_); | 817 base::AutoLock lock(lock_); |
963 thread_safe_cache_->SetThumbnails(cache_->images()); | 818 thread_safe_cache_->SetThumbnails(cache_->images()); |
964 } | 819 } |
965 | 820 |
966 void TopSites::NotifyTopSitesChanged() { | 821 void TopSites::NotifyTopSitesChanged() { |
967 content::NotificationService::current()->Notify( | 822 content::NotificationService::current()->Notify( |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1038 SetTopSites(pages); | 893 SetTopSites(pages); |
1039 | 894 |
1040 // Used only in testing. | 895 // Used only in testing. |
1041 content::NotificationService::current()->Notify( | 896 content::NotificationService::current()->Notify( |
1042 chrome::NOTIFICATION_TOP_SITES_UPDATED, | 897 chrome::NOTIFICATION_TOP_SITES_UPDATED, |
1043 content::Source<TopSites>(this), | 898 content::Source<TopSites>(this), |
1044 content::Details<CancelableRequestProvider::Handle>(&handle)); | 899 content::Details<CancelableRequestProvider::Handle>(&handle)); |
1045 } | 900 } |
1046 | 901 |
1047 } // namespace history | 902 } // namespace history |
OLD | NEW |