OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 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/win/jumplist.h" | 5 #include "chrome/browser/win/jumplist.h" |
6 | 6 |
7 #include "base/base_paths.h" | 7 #include "base/base_paths.h" |
8 #include "base/bind.h" | 8 #include "base/bind.h" |
9 #include "base/bind_helpers.h" | 9 #include "base/bind_helpers.h" |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 #include "ui/gfx/image/image_family.h" | 49 #include "ui/gfx/image/image_family.h" |
50 #include "ui/gfx/image/image_skia.h" | 50 #include "ui/gfx/image/image_skia.h" |
51 #include "ui/gfx/image/image_skia_rep.h" | 51 #include "ui/gfx/image/image_skia_rep.h" |
52 #include "url/gurl.h" | 52 #include "url/gurl.h" |
53 | 53 |
54 using content::BrowserThread; | 54 using content::BrowserThread; |
55 using JumpListData = JumpList::JumpListData; | 55 using JumpListData = JumpList::JumpListData; |
56 | 56 |
57 namespace { | 57 namespace { |
58 | 58 |
| 59 // The default maximum number of items to display in JumpList is 10. |
| 60 // https://msdn.microsoft.com/library/windows/desktop/dd378398.aspx |
| 61 // The "Most visited" and "Recently closed" category titles always take 2 slots. |
| 62 // For the remaining 8 slots, we allocate 5 slots to "most-visited" items and 3 |
| 63 // slots to"recently-closed" items, respectively. |
| 64 constexpr size_t kMostVisitedItems = 5; |
| 65 constexpr size_t kRecentlyClosedItems = 3; |
| 66 |
59 // The number of updates to skip to alleviate the machine when a previous update | 67 // The number of updates to skip to alleviate the machine when a previous update |
60 // was too slow. | 68 // was too slow. |
61 constexpr int kUpdatesToSkipUnderHeavyLoad = 10; | 69 constexpr int kUpdatesToSkipUnderHeavyLoad = 10; |
62 | 70 |
63 // The delay before updating the JumpList to prevent update storms. | 71 // The delay before updating the JumpList to prevent update storms. |
64 constexpr base::TimeDelta kDelayForJumplistUpdate = | 72 constexpr base::TimeDelta kDelayForJumplistUpdate = |
65 base::TimeDelta::FromMilliseconds(3500); | 73 base::TimeDelta::FromMilliseconds(3500); |
66 | 74 |
67 // The maximum allowed time for JumpListUpdater::BeginUpdate. Updates taking | 75 // The maximum allowed time for JumpListUpdater::BeginUpdate. Updates taking |
68 // longer than this are discarded to prevent bogging down slow machines. | 76 // longer than this are discarded to prevent bogging down slow machines. |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
215 TabRestoreServiceFactory::GetForProfile(profile_); | 223 TabRestoreServiceFactory::GetForProfile(profile_); |
216 if (!tab_restore_service) | 224 if (!tab_restore_service) |
217 return; | 225 return; |
218 | 226 |
219 app_id_ = | 227 app_id_ = |
220 shell_integration::win::GetChromiumModelIdForProfile(profile_->GetPath()); | 228 shell_integration::win::GetChromiumModelIdForProfile(profile_->GetPath()); |
221 | 229 |
222 scoped_refptr<history::TopSites> top_sites = | 230 scoped_refptr<history::TopSites> top_sites = |
223 TopSitesFactory::GetForProfile(profile_); | 231 TopSitesFactory::GetForProfile(profile_); |
224 if (top_sites) { | 232 if (top_sites) { |
225 // TopSites updates itself after a delay. This is especially noticable when | |
226 // your profile is empty. Ask TopSites to update itself when jumplist is | |
227 // initialized. | |
228 top_sites->SyncWithHistory(); | |
229 // Register as TopSitesObserver so that we can update ourselves when the | 233 // Register as TopSitesObserver so that we can update ourselves when the |
230 // TopSites changes. | 234 // TopSites changes. TopSites updates itself after a delay. This is |
| 235 // especially noticable when your profile is empty. |
231 top_sites->AddObserver(this); | 236 top_sites->AddObserver(this); |
232 } | 237 } |
233 tab_restore_service->AddObserver(this); | 238 tab_restore_service->AddObserver(this); |
234 pref_change_registrar_.reset(new PrefChangeRegistrar); | 239 pref_change_registrar_.reset(new PrefChangeRegistrar); |
235 pref_change_registrar_->Init(profile_->GetPrefs()); | 240 pref_change_registrar_->Init(profile_->GetPrefs()); |
236 pref_change_registrar_->Add( | 241 pref_change_registrar_->Add( |
237 prefs::kIncognitoModeAvailability, | 242 prefs::kIncognitoModeAvailability, |
238 base::Bind(&JumpList::OnIncognitoAvailabilityChanged, this)); | 243 base::Bind(&JumpList::OnIncognitoAvailabilityChanged, this)); |
239 } | 244 } |
240 | 245 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
277 | 282 |
278 void JumpList::ShutdownOnUIThread() { | 283 void JumpList::ShutdownOnUIThread() { |
279 DCHECK(CalledOnValidThread()); | 284 DCHECK(CalledOnValidThread()); |
280 Terminate(); | 285 Terminate(); |
281 } | 286 } |
282 | 287 |
283 void JumpList::OnMostVisitedURLsAvailable( | 288 void JumpList::OnMostVisitedURLsAvailable( |
284 const history::MostVisitedURLList& urls) { | 289 const history::MostVisitedURLList& urls) { |
285 DCHECK(CalledOnValidThread()); | 290 DCHECK(CalledOnValidThread()); |
286 | 291 |
287 // At most 9 JumpList items can be displayed for the "Most Visited" | |
288 // category. | |
289 const int kMostVistedCount = 9; | |
290 { | 292 { |
291 JumpListData* data = &jumplist_data_->data; | 293 JumpListData* data = &jumplist_data_->data; |
292 base::AutoLock auto_lock(data->list_lock_); | 294 base::AutoLock auto_lock(data->list_lock_); |
293 data->most_visited_pages_.clear(); | 295 data->most_visited_pages_.clear(); |
294 | 296 |
295 for (size_t i = 0; i < urls.size() && i < kMostVistedCount; i++) { | 297 for (size_t i = 0; i < urls.size() && i < kMostVisitedItems; i++) { |
296 const history::MostVisitedURL& url = urls[i]; | 298 const history::MostVisitedURL& url = urls[i]; |
297 scoped_refptr<ShellLinkItem> link = CreateShellLink(); | 299 scoped_refptr<ShellLinkItem> link = CreateShellLink(); |
298 std::string url_string = url.url.spec(); | 300 std::string url_string = url.url.spec(); |
299 base::string16 url_string_wide = base::UTF8ToUTF16(url_string); | 301 base::string16 url_string_wide = base::UTF8ToUTF16(url_string); |
300 link->GetCommandLine()->AppendArgNative(url_string_wide); | 302 link->GetCommandLine()->AppendArgNative(url_string_wide); |
301 link->GetCommandLine()->AppendSwitchASCII( | 303 link->GetCommandLine()->AppendSwitchASCII( |
302 switches::kWinJumplistAction, jumplist::kMostVisitedCategory); | 304 switches::kWinJumplistAction, jumplist::kMostVisitedCategory); |
303 link->set_title(!url.title.empty() ? url.title : url_string_wide); | 305 link->set_title(!url.title.empty() ? url.title : url_string_wide); |
304 link->set_url(url_string); | 306 link->set_url(url_string); |
305 data->most_visited_pages_.push_back(link); | 307 data->most_visited_pages_.push_back(link); |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
523 if (timer_most_visited_.IsRunning()) { | 525 if (timer_most_visited_.IsRunning()) { |
524 timer_most_visited_.Reset(); | 526 timer_most_visited_.Reset(); |
525 } else { | 527 } else { |
526 timer_most_visited_.Start( | 528 timer_most_visited_.Start( |
527 FROM_HERE, kDelayForJumplistUpdate, | 529 FROM_HERE, kDelayForJumplistUpdate, |
528 base::Bind(&JumpList::DeferredTopSitesChanged, base::Unretained(this))); | 530 base::Bind(&JumpList::DeferredTopSitesChanged, base::Unretained(this))); |
529 } | 531 } |
530 } | 532 } |
531 | 533 |
532 void JumpList::DeferredTopSitesChanged() { | 534 void JumpList::DeferredTopSitesChanged() { |
| 535 DCHECK(CalledOnValidThread()); |
| 536 |
533 if (updates_to_skip_ > 0) { | 537 if (updates_to_skip_ > 0) { |
534 --updates_to_skip_; | 538 --updates_to_skip_; |
535 return; | 539 return; |
536 } | 540 } |
537 | 541 |
| 542 // Opening the first tab in one session triggers a TopSite history sync. |
| 543 // Delay this sync till the first tab is closed to allow the "recently closed" |
| 544 // category from last session to stay longer. |
| 545 if (!has_tab_closed_) |
| 546 return; |
| 547 |
538 scoped_refptr<history::TopSites> top_sites = | 548 scoped_refptr<history::TopSites> top_sites = |
539 TopSitesFactory::GetForProfile(profile_); | 549 TopSitesFactory::GetForProfile(profile_); |
540 if (top_sites) { | 550 if (top_sites) { |
541 top_sites->GetMostVisitedURLs( | 551 top_sites->GetMostVisitedURLs( |
542 base::Bind(&JumpList::OnMostVisitedURLsAvailable, | 552 base::Bind(&JumpList::OnMostVisitedURLsAvailable, |
543 weak_ptr_factory_.GetWeakPtr()), | 553 weak_ptr_factory_.GetWeakPtr()), |
544 false); | 554 false); |
545 } | 555 } |
546 } | 556 } |
547 | 557 |
548 void JumpList::DeferredTabRestoreServiceChanged() { | 558 void JumpList::DeferredTabRestoreServiceChanged() { |
| 559 DCHECK(CalledOnValidThread()); |
| 560 |
549 if (updates_to_skip_ > 0) { | 561 if (updates_to_skip_ > 0) { |
550 --updates_to_skip_; | 562 --updates_to_skip_; |
551 return; | 563 return; |
552 } | 564 } |
553 | 565 |
| 566 // Force a TopSite history sync when closing a first tab in one session. |
| 567 if (!has_tab_closed_) { |
| 568 has_tab_closed_ = true; |
| 569 scoped_refptr<history::TopSites> top_sites = |
| 570 TopSitesFactory::GetForProfile(profile_); |
| 571 if (top_sites) |
| 572 top_sites->SyncWithHistory(); |
| 573 } |
| 574 |
554 // Create a list of ShellLinkItems from the "Recently Closed" pages. | 575 // Create a list of ShellLinkItems from the "Recently Closed" pages. |
555 // As noted above, we create a ShellLinkItem objects with the following | 576 // As noted above, we create a ShellLinkItem objects with the following |
556 // parameters. | 577 // parameters. |
557 // * arguments | 578 // * arguments |
558 // The last URL of the tab object. | 579 // The last URL of the tab object. |
559 // * title | 580 // * title |
560 // The title of the last URL. | 581 // The title of the last URL. |
561 // * icon | 582 // * icon |
562 // An empty string. This value is to be updated in OnFaviconDataAvailable(). | 583 // An empty string. This value is to be updated in OnFaviconDataAvailable(). |
563 const int kRecentlyClosedCount = 3; | 584 |
564 sessions::TabRestoreService* tab_restore_service = | 585 sessions::TabRestoreService* tab_restore_service = |
565 TabRestoreServiceFactory::GetForProfile(profile_); | 586 TabRestoreServiceFactory::GetForProfile(profile_); |
566 | 587 |
567 { | 588 { |
568 JumpListData* data = &jumplist_data_->data; | 589 JumpListData* data = &jumplist_data_->data; |
569 base::AutoLock auto_lock(data->list_lock_); | 590 base::AutoLock auto_lock(data->list_lock_); |
570 data->recently_closed_pages_.clear(); | 591 data->recently_closed_pages_.clear(); |
571 | 592 |
572 for (const auto& entry : tab_restore_service->entries()) { | 593 for (const auto& entry : tab_restore_service->entries()) { |
573 if (data->recently_closed_pages_.size() >= kRecentlyClosedCount) | 594 if (data->recently_closed_pages_.size() >= kRecentlyClosedItems) |
574 break; | 595 break; |
575 switch (entry->type) { | 596 switch (entry->type) { |
576 case sessions::TabRestoreService::TAB: | 597 case sessions::TabRestoreService::TAB: |
577 AddTab(static_cast<const sessions::TabRestoreService::Tab&>(*entry), | 598 AddTab(static_cast<const sessions::TabRestoreService::Tab&>(*entry), |
578 kRecentlyClosedCount, data); | 599 kRecentlyClosedItems, data); |
579 break; | 600 break; |
580 case sessions::TabRestoreService::WINDOW: | 601 case sessions::TabRestoreService::WINDOW: |
581 AddWindow( | 602 AddWindow( |
582 static_cast<const sessions::TabRestoreService::Window&>(*entry), | 603 static_cast<const sessions::TabRestoreService::Window&>(*entry), |
583 kRecentlyClosedCount, data); | 604 kRecentlyClosedItems, data); |
584 break; | 605 break; |
585 } | 606 } |
586 } | 607 } |
587 | 608 |
588 data->recently_closed_pages_have_updates_ = true; | 609 data->recently_closed_pages_have_updates_ = true; |
589 } | 610 } |
590 | 611 |
591 // Send a query that retrieves the first favicon. | 612 // Send a query that retrieves the first favicon. |
592 StartLoadingFavicon(); | 613 StartLoadingFavicon(); |
593 } | 614 } |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
704 | 725 |
705 // Discard this JumpList update if JumpListUpdater::BeginUpdate takes longer | 726 // Discard this JumpList update if JumpListUpdater::BeginUpdate takes longer |
706 // than the maximum allowed time, as it's very likely the following update | 727 // than the maximum allowed time, as it's very likely the following update |
707 // steps will also take a long time. As we've not updated the icons on the | 728 // steps will also take a long time. As we've not updated the icons on the |
708 // disk, discarding this update wont't affect the current JumpList used by OS. | 729 // disk, discarding this update wont't affect the current JumpList used by OS. |
709 if (begin_update_timer.Elapsed() >= kTimeOutForJumplistBeginUpdate) { | 730 if (begin_update_timer.Elapsed() >= kTimeOutForJumplistBeginUpdate) { |
710 updates_to_skip_ = kUpdatesToSkipUnderHeavyLoad; | 731 updates_to_skip_ = kUpdatesToSkipUnderHeavyLoad; |
711 return false; | 732 return false; |
712 } | 733 } |
713 | 734 |
714 // The default maximum number of items to display in JumpList is 10. | |
715 // https://msdn.microsoft.com/library/windows/desktop/dd378398.aspx | |
716 // The "Most visited" category title always takes 1 of the JumpList slots if | |
717 // |most_visited_pages| isn't empty. | |
718 // The "Recently closed" category title will also take 1 if | |
719 // |recently_closed_pages| isn't empty. | |
720 // For the remaining slots, we allocate 5/8 (i.e., 5 slots if both categories | |
721 // present) to "most-visited" items and 3/8 (i.e., 3 slots if both categories | |
722 // present) to "recently-closed" items, respectively. | |
723 // Nevertheless, if there are not so many items in |recently_closed_pages|, | |
724 // we give the remaining slots to "most-visited" items. | |
725 | |
726 const int kMostVisited = 50; | |
727 const int kRecentlyClosed = 30; | |
728 const int kTotal = kMostVisited + kRecentlyClosed; | |
729 | |
730 // Adjust the available jumplist slots to account for the category titles. | |
731 size_t user_max_items_adjusted = jumplist_updater.user_max_items(); | |
732 if (!most_visited_pages.empty()) | |
733 --user_max_items_adjusted; | |
734 if (!recently_closed_pages.empty()) | |
735 --user_max_items_adjusted; | |
736 | |
737 size_t most_visited_items = | |
738 MulDiv(user_max_items_adjusted, kMostVisited, kTotal); | |
739 size_t recently_closed_items = user_max_items_adjusted - most_visited_items; | |
740 if (recently_closed_pages.size() < recently_closed_items) { | |
741 most_visited_items += recently_closed_items - recently_closed_pages.size(); | |
742 recently_closed_items = recently_closed_pages.size(); | |
743 } | |
744 | |
745 // Record the desired number of icons to create in this JumpList update. | 735 // Record the desired number of icons to create in this JumpList update. |
746 int icons_to_create = 0; | 736 int icons_to_create = 0; |
747 | 737 |
748 // Update the icons for "Most Visisted" category of the JumpList if needed. | 738 // Update the icons for "Most Visisted" category of the JumpList if needed. |
749 if (most_visited_pages_have_updates) { | 739 if (most_visited_pages_have_updates) { |
750 base::FilePath icon_dir_most_visited = GenerateJumplistIconDirName( | 740 base::FilePath icon_dir_most_visited = GenerateJumplistIconDirName( |
751 profile_dir, FILE_PATH_LITERAL("MostVisited")); | 741 profile_dir, FILE_PATH_LITERAL("MostVisited")); |
752 | 742 |
753 UpdateIconFiles(icon_dir_most_visited, most_visited_pages, | 743 UpdateIconFiles(icon_dir_most_visited, most_visited_pages, |
754 most_visited_items, JumpListCategory::kMostVisited); | 744 kMostVisitedItems, JumpListCategory::kMostVisited); |
755 | 745 |
756 icons_to_create += std::min(most_visited_pages.size(), most_visited_items); | 746 icons_to_create += std::min(most_visited_pages.size(), kMostVisitedItems); |
757 } | 747 } |
758 | 748 |
759 // Update the icons for "Recently Closed" category of the JumpList if needed. | 749 // Update the icons for "Recently Closed" category of the JumpList if needed. |
760 if (recently_closed_pages_have_updates) { | 750 if (recently_closed_pages_have_updates) { |
761 base::FilePath icon_dir_recent_closed = GenerateJumplistIconDirName( | 751 base::FilePath icon_dir_recent_closed = GenerateJumplistIconDirName( |
762 profile_dir, FILE_PATH_LITERAL("RecentClosed")); | 752 profile_dir, FILE_PATH_LITERAL("RecentClosed")); |
763 | 753 |
764 UpdateIconFiles(icon_dir_recent_closed, recently_closed_pages, | 754 UpdateIconFiles(icon_dir_recent_closed, recently_closed_pages, |
765 recently_closed_items, JumpListCategory::kRecentlyClosed); | 755 kRecentlyClosedItems, JumpListCategory::kRecentlyClosed); |
766 | 756 |
767 icons_to_create += | 757 icons_to_create += |
768 std::min(recently_closed_pages.size(), recently_closed_items); | 758 std::min(recently_closed_pages.size(), kRecentlyClosedItems); |
769 } | 759 } |
770 | 760 |
771 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/40407. | 761 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/40407. |
772 UMA_HISTOGRAM_COUNTS_100("WinJumplist.CreateIconFilesCount", icons_to_create); | 762 UMA_HISTOGRAM_COUNTS_100("WinJumplist.CreateIconFilesCount", icons_to_create); |
773 | 763 |
774 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/40407. | 764 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/40407. |
775 SCOPED_UMA_HISTOGRAM_TIMER("WinJumplist.UpdateJumpListDuration"); | 765 SCOPED_UMA_HISTOGRAM_TIMER("WinJumplist.UpdateJumpListDuration"); |
776 | 766 |
777 base::ElapsedTimer add_custom_category_timer; | 767 base::ElapsedTimer add_custom_category_timer; |
778 | 768 |
779 // Update the "Most Visited" category of the JumpList if it exists. | 769 // Update the "Most Visited" category of the JumpList if it exists. |
780 // This update request is applied into the JumpList when we commit this | 770 // This update request is applied into the JumpList when we commit this |
781 // transaction. | 771 // transaction. |
782 if (!jumplist_updater.AddCustomCategory( | 772 if (!jumplist_updater.AddCustomCategory( |
783 l10n_util::GetStringUTF16(IDS_NEW_TAB_MOST_VISITED), | 773 l10n_util::GetStringUTF16(IDS_NEW_TAB_MOST_VISITED), |
784 most_visited_pages, most_visited_items)) { | 774 most_visited_pages, kMostVisitedItems)) { |
785 return false; | 775 return false; |
786 } | 776 } |
787 | 777 |
788 // Update the "Recently Closed" category of the JumpList. | 778 // Update the "Recently Closed" category of the JumpList. |
789 if (!jumplist_updater.AddCustomCategory( | 779 if (!jumplist_updater.AddCustomCategory( |
790 l10n_util::GetStringUTF16(IDS_RECENTLY_CLOSED), recently_closed_pages, | 780 l10n_util::GetStringUTF16(IDS_RECENTLY_CLOSED), recently_closed_pages, |
791 recently_closed_items)) { | 781 kRecentlyClosedItems)) { |
792 return false; | 782 return false; |
793 } | 783 } |
794 | 784 |
795 // If JumpListUpdater::AddCustomCategory or JumpListUpdater::CommitUpdate | 785 // If JumpListUpdater::AddCustomCategory or JumpListUpdater::CommitUpdate |
796 // takes longer than the maximum allowed time, skip the next | 786 // takes longer than the maximum allowed time, skip the next |
797 // |kUpdatesToSkipUnderHeavyLoad| updates. This update should be finished | 787 // |kUpdatesToSkipUnderHeavyLoad| updates. This update should be finished |
798 // because we've already updated the icons on the disk. If discarding this | 788 // because we've already updated the icons on the disk. If discarding this |
799 // update from here, some items in the current JumpList may not have icons | 789 // update from here, some items in the current JumpList may not have icons |
800 // as they've been delete from the disk. In this case, the background color of | 790 // as they've been delete from the disk. In this case, the background color of |
801 // the JumpList panel is used instead, which doesn't look nice. | 791 // the JumpList panel is used instead, which doesn't look nice. |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
859 app_id, profile_dir, local_most_visited_pages, | 849 app_id, profile_dir, local_most_visited_pages, |
860 local_recently_closed_pages, most_visited_pages_have_updates, | 850 local_recently_closed_pages, most_visited_pages_have_updates, |
861 recently_closed_pages_have_updates, incognito_availability)) { | 851 recently_closed_pages_have_updates, incognito_availability)) { |
862 base::AutoLock auto_lock(data->list_lock_); | 852 base::AutoLock auto_lock(data->list_lock_); |
863 if (most_visited_pages_have_updates) | 853 if (most_visited_pages_have_updates) |
864 data->most_visited_pages_have_updates_ = true; | 854 data->most_visited_pages_have_updates_ = true; |
865 if (recently_closed_pages_have_updates) | 855 if (recently_closed_pages_have_updates) |
866 data->recently_closed_pages_have_updates_ = true; | 856 data->recently_closed_pages_have_updates_ = true; |
867 } | 857 } |
868 } | 858 } |
OLD | NEW |