Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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/jumplist_win.h" | 5 #include "chrome/browser/jumplist_win.h" |
| 6 | 6 |
| 7 #include <windows.h> | 7 #include <windows.h> |
| 8 #include <shobjidl.h> | 8 #include <shobjidl.h> |
| 9 #include <propkey.h> | 9 #include <propkey.h> |
| 10 #include <propvarutil.h> | 10 #include <propvarutil.h> |
| 11 | 11 |
| 12 #include <string> | 12 #include <string> |
| 13 #include <vector> | 13 #include <vector> |
| 14 | 14 |
| 15 #include "base/callback.h" | 15 #include "base/callback.h" |
| 16 #include "base/command_line.h" | 16 #include "base/command_line.h" |
| 17 #include "base/file_util.h" | 17 #include "base/file_util.h" |
| 18 #include "base/path_service.h" | 18 #include "base/path_service.h" |
| 19 #include "base/string_util.h" | 19 #include "base/string_util.h" |
| 20 #include "base/threading/thread.h" | 20 #include "base/threading/thread.h" |
| 21 #include "base/utf_string_conversions.h" | 21 #include "base/utf_string_conversions.h" |
| 22 #include "base/win/scoped_comptr.h" | 22 #include "base/win/scoped_comptr.h" |
| 23 #include "base/win/windows_version.h" | 23 #include "base/win/windows_version.h" |
| 24 #include "chrome/browser/favicon/favicon_service.h" | 24 #include "chrome/browser/favicon/favicon_service.h" |
| 25 #include "chrome/browser/history/history.h" | 25 #include "chrome/browser/history/history.h" |
| 26 #include "chrome/browser/history/page_usage_data.h" | 26 #include "chrome/browser/history/page_usage_data.h" |
| 27 #include "chrome/browser/history/top_sites.h" | |
| 27 #include "chrome/browser/profiles/profile.h" | 28 #include "chrome/browser/profiles/profile.h" |
| 28 #include "chrome/browser/sessions/session_types.h" | 29 #include "chrome/browser/sessions/session_types.h" |
| 29 #include "chrome/browser/sessions/tab_restore_service.h" | 30 #include "chrome/browser/sessions/tab_restore_service.h" |
| 30 #include "chrome/browser/sessions/tab_restore_service_factory.h" | 31 #include "chrome/browser/sessions/tab_restore_service_factory.h" |
| 31 #include "chrome/browser/shell_integration.h" | 32 #include "chrome/browser/shell_integration.h" |
| 32 #include "chrome/common/chrome_constants.h" | 33 #include "chrome/common/chrome_constants.h" |
| 34 #include "chrome/common/chrome_notification_types.h" | |
| 33 #include "chrome/common/chrome_switches.h" | 35 #include "chrome/common/chrome_switches.h" |
| 34 #include "chrome/common/url_constants.h" | 36 #include "chrome/common/url_constants.h" |
| 35 #include "content/browser/browser_thread.h" | 37 #include "content/browser/browser_thread.h" |
| 38 #include "content/common/notification_service.h" | |
| 36 #include "googleurl/src/gurl.h" | 39 #include "googleurl/src/gurl.h" |
| 37 #include "grit/chromium_strings.h" | 40 #include "grit/chromium_strings.h" |
| 38 #include "grit/generated_resources.h" | 41 #include "grit/generated_resources.h" |
| 39 #include "third_party/skia/include/core/SkBitmap.h" | 42 #include "third_party/skia/include/core/SkBitmap.h" |
| 40 #include "ui/base/l10n/l10n_util.h" | 43 #include "ui/base/l10n/l10n_util.h" |
| 41 #include "ui/gfx/codec/png_codec.h" | 44 #include "ui/gfx/codec/png_codec.h" |
| 42 #include "ui/gfx/icon_util.h" | 45 #include "ui/gfx/icon_util.h" |
| 43 | 46 |
| 44 | 47 |
| 45 namespace { | 48 namespace { |
| (...skipping 423 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 469 return false; | 472 return false; |
| 470 | 473 |
| 471 // Commit this transaction and send the updated JumpList to Windows. | 474 // Commit this transaction and send the updated JumpList to Windows. |
| 472 result = destination_list->CommitList(); | 475 result = destination_list->CommitList(); |
| 473 if (FAILED(result)) | 476 if (FAILED(result)) |
| 474 return false; | 477 return false; |
| 475 | 478 |
| 476 return true; | 479 return true; |
| 477 } | 480 } |
| 478 | 481 |
| 479 // Represents a task which updates an application JumpList. | |
| 480 // This task encapsulates all I/O tasks and OS-specific tasks required for | |
| 481 // updating a JumpList from Chromium, such as: | |
| 482 // * Deleting the directory containing temporary icon files; | |
| 483 // * Creating temporary icon files used by the JumpList; | |
| 484 // * Creating an ICustomDestinationList instance; | |
| 485 // * Adding items in the ICustomDestinationList instance. | |
| 486 // To spawn this task, | |
| 487 // 1. Prepare objects required by this task: | |
| 488 // * a std::wstring that contains a temporary icons; | |
| 489 // * a ShellLinkItemList that contains the items of the "Most Visited" | |
| 490 // category, and; | |
| 491 // * a ShellLinkItemList that contains the items of the "Recently Closed" | |
| 492 // category. | |
| 493 // 2. Create a JumpListUpdateTask instance, and; | |
| 494 // 3. Post this task to the file thread. | |
| 495 class JumpListUpdateTask : public Task { | |
| 496 public: | |
| 497 JumpListUpdateTask(const wchar_t* app_id, | |
| 498 const FilePath& icon_dir, | |
| 499 const ShellLinkItemList& most_visited_pages, | |
| 500 const ShellLinkItemList& recently_closed_pages) | |
| 501 : app_id_(app_id), | |
| 502 icon_dir_(icon_dir), | |
| 503 most_visited_pages_(most_visited_pages), | |
| 504 recently_closed_pages_(recently_closed_pages) { | |
| 505 } | |
| 506 | |
| 507 private: | |
| 508 // Represents an entry point of this task. | |
| 509 // When we post this task to a file thread, the thread calls this function. | |
| 510 void Run(); | |
| 511 | |
| 512 // App id to associate with the jump list. | |
| 513 std::wstring app_id_; | |
| 514 | |
| 515 // The directory which contains JumpList icons. | |
| 516 FilePath icon_dir_; | |
| 517 | |
| 518 // Items in the "Most Visited" category of the application JumpList. | |
| 519 ShellLinkItemList most_visited_pages_; | |
| 520 | |
| 521 // Items in the "Recently Closed" category of the application JumpList. | |
| 522 ShellLinkItemList recently_closed_pages_; | |
| 523 }; | |
| 524 | |
| 525 void JumpListUpdateTask::Run() { | |
| 526 // Delete the directory which contains old icon files, rename the current | |
| 527 // icon directory, and create a new directory which contains new JumpList | |
| 528 // icon files. | |
| 529 FilePath icon_dir_old(icon_dir_.value() + L"Old"); | |
| 530 if (file_util::PathExists(icon_dir_old)) | |
| 531 file_util::Delete(icon_dir_old, true); | |
| 532 file_util::Move(icon_dir_, icon_dir_old); | |
| 533 file_util::CreateDirectory(icon_dir_); | |
| 534 | |
| 535 // Create temporary icon files for shortcuts in the "Most Visited" category. | |
| 536 for (ShellLinkItemList::const_iterator item = most_visited_pages_.begin(); | |
| 537 item != most_visited_pages_.end(); ++item) { | |
| 538 SkBitmap icon_bitmap; | |
| 539 if ((*item)->data().get() && | |
| 540 gfx::PNGCodec::Decode((*item)->data()->front(), | |
| 541 (*item)->data()->size(), | |
| 542 &icon_bitmap)) { | |
| 543 FilePath icon_path; | |
| 544 if (CreateIconFile(icon_bitmap, icon_dir_, &icon_path)) | |
| 545 (*item)->SetIcon(icon_path.value(), 0, true); | |
| 546 } | |
| 547 } | |
| 548 | |
| 549 // Create temporary icon files for shortcuts in the "Recently Closed" | |
| 550 // category. | |
| 551 for (ShellLinkItemList::const_iterator item = recently_closed_pages_.begin(); | |
| 552 item != recently_closed_pages_.end(); ++item) { | |
| 553 SkBitmap icon_bitmap; | |
| 554 if ((*item)->data().get() && | |
| 555 gfx::PNGCodec::Decode((*item)->data()->front(), | |
| 556 (*item)->data()->size(), | |
| 557 &icon_bitmap)) { | |
| 558 FilePath icon_path; | |
| 559 if (CreateIconFile(icon_bitmap, icon_dir_, &icon_path)) | |
| 560 (*item)->SetIcon(icon_path.value(), 0, true); | |
| 561 } | |
| 562 } | |
| 563 | |
| 564 // We finished collecting all resources needed for updating an appliation | |
| 565 // JumpList. So, create a new JumpList and replace the current JumpList | |
| 566 // with it. | |
| 567 UpdateJumpList(app_id_.c_str(), most_visited_pages_, recently_closed_pages_); | |
| 568 | |
| 569 // Delete all items in these lists now since we don't need the ShellLinkItem | |
| 570 // objects in these lists. | |
| 571 most_visited_pages_.clear(); | |
| 572 recently_closed_pages_.clear(); | |
| 573 } | |
| 574 | |
| 575 } // namespace | 482 } // namespace |
| 576 | 483 |
| 577 JumpList::JumpList() : profile_(NULL) { | 484 JumpList::JumpList() |
| 485 : profile_(NULL), | |
| 486 handle_(NULL) { | |
| 578 } | 487 } |
| 579 | 488 |
| 580 JumpList::~JumpList() { | 489 JumpList::~JumpList() { |
| 581 RemoveObserver(); | 490 Terminate(); |
| 582 } | 491 } |
| 583 | 492 |
| 584 // static | 493 // static |
| 585 bool JumpList::Enabled() { | 494 bool JumpList::Enabled() { |
| 586 return (base::win::GetVersion() >= base::win::VERSION_WIN7 && | 495 return (base::win::GetVersion() >= base::win::VERSION_WIN7 && |
| 587 !CommandLine::ForCurrentProcess()->HasSwitch( | 496 !CommandLine::ForCurrentProcess()->HasSwitch( |
| 588 switches::kDisableCustomJumpList)); | 497 switches::kDisableCustomJumpList)); |
| 589 } | 498 } |
| 590 | 499 |
| 591 bool JumpList::AddObserver(Profile* profile) { | 500 bool JumpList::AddObserver(Profile* profile) { |
| 592 // To update JumpList when a tab is added or removed, we add this object to | 501 // To update JumpList when a tab is added or removed, we add this object to |
| 593 // the observer list of the TabRestoreService class. | 502 // the observer list of the TabRestoreService class. |
| 594 // When we add this object to the observer list, we save the pointer to this | 503 // When we add this object to the observer list, we save the pointer to this |
| 595 // TabRestoreService object. This pointer is used when we remove this object | 504 // TabRestoreService object. This pointer is used when we remove this object |
| 596 // from the observer list. | 505 // from the observer list. |
| 597 if (base::win::GetVersion() < base::win::VERSION_WIN7 || !profile) | 506 if (base::win::GetVersion() < base::win::VERSION_WIN7 || !profile) |
| 598 return false; | 507 return false; |
| 599 | 508 |
| 600 TabRestoreService* tab_restore_service = | 509 TabRestoreService* tab_restore_service = |
| 601 TabRestoreServiceFactory::GetForProfile(profile); | 510 TabRestoreServiceFactory::GetForProfile(profile); |
| 602 if (!tab_restore_service) | 511 if (!tab_restore_service) |
| 603 return false; | 512 return false; |
| 604 | 513 |
| 605 app_id_ = ShellIntegration::GetChromiumAppId(profile->GetPath()); | 514 app_id_ = ShellIntegration::GetChromiumAppId(profile->GetPath()); |
| 606 icon_dir_ = profile->GetPath().Append(chrome::kJumpListIconDirname); | 515 icon_dir_ = profile->GetPath().Append(chrome::kJumpListIconDirname); |
| 607 profile_ = profile; | 516 profile_ = profile; |
| 517 history::TopSites* top_sites = profile_->GetTopSites(); | |
| 518 if (top_sites) { | |
| 519 // TopSites updates itself after a delay. This is especially noticable when | |
| 520 // your profile is empty. Ask TopSites to update itself when we're about to | |
| 521 // show the new tab page. | |
|
MAD
2011/08/19 14:46:03
I don't think we are about to show the new tab pag
| |
| 522 top_sites->SyncWithHistory(); | |
| 523 // Register for notification when TopSites changes so that we can update | |
| 524 // ourself. | |
| 525 registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED, | |
| 526 Source<history::TopSites>(top_sites)); | |
| 527 // Register for notification when profile is destroyed to ensure that all | |
| 528 // observers are detatched at that time. | |
| 529 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, | |
| 530 Source<Profile>(profile_)); | |
|
MAD
2011/08/19 14:46:03
Bad alignment, should be aligned to this, right af
| |
| 531 } | |
| 608 tab_restore_service->AddObserver(this); | 532 tab_restore_service->AddObserver(this); |
| 609 return true; | 533 return true; |
| 610 } | 534 } |
| 611 | 535 |
| 536 void JumpList::Observe(int type, | |
| 537 const NotificationSource& source, | |
| 538 const NotificationDetails& details) { | |
| 539 switch (type) { | |
| 540 case chrome::NOTIFICATION_TOP_SITES_CHANGED: { | |
| 541 // Most visited urls changed, query again. | |
| 542 history::TopSites* top_sites = profile_->GetTopSites(); | |
| 543 if (top_sites) { | |
| 544 top_sites->GetMostVisitedURLs( | |
| 545 &topsites_consumer_, | |
| 546 NewCallback(this, &JumpList::OnMostVisitedURLsAvailable)); | |
| 547 } | |
| 548 break; | |
| 549 } | |
| 550 case chrome::NOTIFICATION_PROFILE_DESTROYED: { | |
| 551 // Profile was destroyed, do clean-up. | |
| 552 Terminate(); | |
| 553 break; | |
| 554 } | |
| 555 default: | |
| 556 NOTREACHED() << "Unexpected notification type."; | |
| 557 } | |
| 558 } | |
| 559 | |
| 612 void JumpList::RemoveObserver() { | 560 void JumpList::RemoveObserver() { |
| 613 if (profile_) { | 561 if (profile_) { |
| 614 TabRestoreService* tab_restore_service = | 562 TabRestoreService* tab_restore_service = |
| 615 TabRestoreServiceFactory::GetForProfile(profile_); | 563 TabRestoreServiceFactory::GetForProfile(profile_); |
| 616 if (tab_restore_service) | 564 if (tab_restore_service) |
| 617 tab_restore_service->RemoveObserver(this); | 565 tab_restore_service->RemoveObserver(this); |
| 566 registrar_.Remove(this, chrome::NOTIFICATION_TOP_SITES_CHANGED, | |
| 567 Source<history::TopSites>(profile_->GetTopSites())); | |
| 568 registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_DESTROYED, | |
| 569 Source<Profile>(profile_)); | |
| 618 } | 570 } |
| 619 profile_ = NULL; | 571 profile_ = NULL; |
| 620 } | 572 } |
| 621 | 573 |
| 574 void JumpList::CancelPendingUpdate() { | |
| 575 if (handle_) { | |
| 576 FaviconService* favicon_service = | |
| 577 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); | |
| 578 favicon_service->CancelRequest(handle_); | |
| 579 handle_ = NULL; | |
| 580 } | |
| 581 } | |
| 582 | |
| 583 void JumpList::Terminate() { | |
| 584 CancelPendingUpdate(); | |
| 585 RemoveObserver(); | |
| 586 } | |
| 587 | |
| 588 void JumpList::OnMostVisitedURLsAvailable( | |
| 589 const history::MostVisitedURLList& data) { | |
| 590 | |
| 591 // If we have a pending favicon request, cancel it here (it is out of date). | |
| 592 CancelPendingUpdate(); | |
| 593 | |
| 594 { | |
| 595 base::AutoLock auto_lock(list_lock_); | |
| 596 most_visited_pages_.clear(); | |
| 597 for (size_t i = 0; i < data.size(); i++) { | |
| 598 const history::MostVisitedURL& url = data[i]; | |
| 599 scoped_refptr<ShellLinkItem> link(new ShellLinkItem); | |
| 600 std::string url_string = url.url.spec(); | |
| 601 link->SetArguments(UTF8ToWide(url_string)); | |
| 602 link->SetTitle(!url.title.empty()? url.title : link->arguments()); | |
| 603 most_visited_pages_.push_back(link); | |
| 604 icon_urls_.push_back(make_pair(url_string, link)); | |
| 605 } | |
| 606 } | |
| 607 | |
| 608 // Send a query that retrieves the first favicon. | |
| 609 StartLoadingFavicon(); | |
| 610 } | |
| 611 | |
| 622 void JumpList::TabRestoreServiceChanged(TabRestoreService* service) { | 612 void JumpList::TabRestoreServiceChanged(TabRestoreService* service) { |
| 623 // Added or removed a tab. | 613 // if we have a pending handle request, cancel it here (it is out of date). |
| 624 // Exit if we are updating the application JumpList. | 614 CancelPendingUpdate(); |
| 625 if (!icon_urls_.empty()) | |
| 626 return; | |
| 627 | 615 |
| 628 // Send a query to HistoryService and retrieve the "Most Visited" pages. | 616 // local list to pass to methods |
| 629 // This code is copied from MostVisitedHandler::HandleGetMostVisited() to | 617 ShellLinkItemList temp_list; |
| 630 // emulate its behaviors. | 618 |
| 631 const int kMostVisitedScope = 90; | 619 // Create a list of ShellLinkItems from the "Recently Closed" pages. |
| 632 const int kMostVisitedCount = 9; | 620 // As noted above, we create a ShellLinkItem objects with the following |
| 633 int result_count = kMostVisitedCount; | 621 // parameters. |
| 634 HistoryService* history_service = | 622 // * arguments |
| 635 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | 623 // The last URL of the tab object. |
| 636 history_service->QuerySegmentUsageSince( | 624 // * title |
| 637 &most_visited_consumer_, | 625 // The title of the last URL. |
| 638 base::Time::Now() - base::TimeDelta::FromDays(kMostVisitedScope), | 626 // * icon |
| 639 result_count, | 627 // An empty string. This value is to be updated in OnFaviconDataAvailable(). |
| 640 NewCallback(this, &JumpList::OnSegmentUsageAvailable)); | 628 // This code is copied from |
| 629 // RecentlyClosedTabsHandler::TabRestoreServiceChanged() to emulate it. | |
| 630 const int kRecentlyClosedCount = 4; | |
| 631 TabRestoreService* tab_restore_service = | |
| 632 TabRestoreServiceFactory::GetForProfile(profile_); | |
| 633 const TabRestoreService::Entries& entries = tab_restore_service->entries(); | |
| 634 for (TabRestoreService::Entries::const_iterator it = entries.begin(); | |
| 635 it != entries.end(); ++it) { | |
| 636 const TabRestoreService::Entry* entry = *it; | |
| 637 if (entry->type == TabRestoreService::TAB) { | |
| 638 AddTab(static_cast<const TabRestoreService::Tab*>(entry), | |
| 639 &temp_list, kRecentlyClosedCount); | |
| 640 } else if (entry->type == TabRestoreService::WINDOW) { | |
| 641 AddWindow(static_cast<const TabRestoreService::Window*>(entry), | |
| 642 &temp_list, kRecentlyClosedCount); | |
| 643 } | |
| 644 } | |
| 645 // Lock recently_closed_pages and copy temp_list into it. | |
| 646 { | |
| 647 base::AutoLock auto_lock(list_lock_); | |
| 648 recently_closed_pages_ = temp_list; | |
| 649 } | |
| 650 | |
| 651 // Send a query that retrieves the first favicon. | |
| 652 StartLoadingFavicon(); | |
| 641 } | 653 } |
| 642 | 654 |
| 643 void JumpList::TabRestoreServiceDestroyed(TabRestoreService* service) { | 655 void JumpList::TabRestoreServiceDestroyed(TabRestoreService* service) { |
| 644 } | 656 } |
| 645 | 657 |
| 646 bool JumpList::AddTab(const TabRestoreService::Tab* tab, | 658 bool JumpList::AddTab(const TabRestoreService::Tab* tab, |
| 647 ShellLinkItemList* list, | 659 ShellLinkItemList* list, |
| 648 size_t max_items) { | 660 size_t max_items) { |
| 649 // This code adds the URL and the title strings of the given tab to the | 661 // This code adds the URL and the title strings of the given tab to the |
| 650 // specified list. | 662 // specified list. |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 678 } | 690 } |
| 679 for (size_t i = 0; i < window->tabs.size(); ++i) { | 691 for (size_t i = 0; i < window->tabs.size(); ++i) { |
| 680 if (!AddTab(&window->tabs[i], list, max_items)) | 692 if (!AddTab(&window->tabs[i], list, max_items)) |
| 681 return false; | 693 return false; |
| 682 } | 694 } |
| 683 | 695 |
| 684 return true; | 696 return true; |
| 685 } | 697 } |
| 686 | 698 |
| 687 bool JumpList::StartLoadingFavicon() { | 699 bool JumpList::StartLoadingFavicon() { |
| 688 if (icon_urls_.empty()) | 700 GURL url; |
| 689 return false; | 701 { |
| 690 | 702 base::AutoLock auto_lock(list_lock_); |
| 691 // Ask FaviconService if it has a favicon of a URL. | 703 if (icon_urls_.empty()) |
| 692 // When FaviconService has one, it will call OnFaviconDataAvailable(). | 704 return false; |
| 693 GURL url(icon_urls_.front().first); | 705 // Ask FaviconService if it has a favicon of a URL. |
| 706 // When FaviconService has one, it will call OnFaviconDataAvailable(). | |
| 707 url = GURL(icon_urls_.front().first); | |
| 708 } | |
| 694 FaviconService* favicon_service = | 709 FaviconService* favicon_service = |
| 695 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); | 710 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); |
| 696 FaviconService::Handle handle = favicon_service->GetFaviconForURL( | 711 handle_ = favicon_service->GetFaviconForURL( |
| 697 url, history::FAVICON, &favicon_consumer_, | 712 url, history::FAVICON, &favicon_consumer_, |
| 698 NewCallback(this, &JumpList::OnFaviconDataAvailable)); | 713 NewCallback(this, &JumpList::OnFaviconDataAvailable)); |
| 699 return true; | 714 return true; |
| 700 } | 715 } |
| 701 | 716 |
| 702 void JumpList::OnSegmentUsageAvailable( | |
| 703 CancelableRequestProvider::Handle handle, | |
| 704 std::vector<PageUsageData*>* data) { | |
| 705 // Create a list of ShellLinkItem objects from the given list of | |
| 706 // PageUsageData objects. | |
| 707 // The command that opens a web page with chrome is: | |
| 708 // "chrome.exe <url-to-the-web-page>". | |
| 709 // So, we create a ShellLinkItem object with the following parameters. | |
| 710 // * arguments | |
| 711 // The URL of a PageUsagedata object (converted to std::wstring). | |
| 712 // * title | |
| 713 // The title of a PageUsageData object. If this string is empty, we use | |
| 714 // the URL as our "Most Visited" page does. | |
| 715 // * icon | |
| 716 // An empty string. This value is to be updated in OnFaviconDataAvailable(). | |
| 717 most_visited_pages_.clear(); | |
| 718 for (std::vector<PageUsageData*>::const_iterator page = data->begin(); | |
| 719 page != data->end(); ++page) { | |
| 720 scoped_refptr<ShellLinkItem> link(new ShellLinkItem); | |
| 721 std::string url = (*page)->GetURL().spec(); | |
| 722 link->SetArguments(UTF8ToWide(url)); | |
| 723 link->SetTitle( | |
| 724 !(*page)->GetTitle().empty() ? (*page)->GetTitle() : link->arguments()); | |
| 725 most_visited_pages_.push_back(link); | |
| 726 icon_urls_.push_back(make_pair(url, link)); | |
| 727 } | |
| 728 | |
| 729 // Create a list of ShellLinkItems from the "Recently Closed" pages. | |
| 730 // As noted above, we create a ShellLinkItem objects with the following | |
| 731 // parameters. | |
| 732 // * arguments | |
| 733 // The last URL of the tab object. | |
| 734 // * title | |
| 735 // The title of the last URL. | |
| 736 // * icon | |
| 737 // An empty string. This value is to be updated in OnFaviconDataAvailable(). | |
| 738 // This code is copied from | |
| 739 // RecentlyClosedTabsHandler::TabRestoreServiceChanged() to emulate it. | |
| 740 const int kRecentlyClosedCount = 4; | |
| 741 recently_closed_pages_.clear(); | |
| 742 TabRestoreService* tab_restore_service = | |
| 743 TabRestoreServiceFactory::GetForProfile(profile_); | |
| 744 const TabRestoreService::Entries& entries = tab_restore_service->entries(); | |
| 745 for (TabRestoreService::Entries::const_iterator it = entries.begin(); | |
| 746 it != entries.end(); ++it) { | |
| 747 const TabRestoreService::Entry* entry = *it; | |
| 748 if (entry->type == TabRestoreService::TAB) { | |
| 749 AddTab(static_cast<const TabRestoreService::Tab*>(entry), | |
| 750 &recently_closed_pages_, kRecentlyClosedCount); | |
| 751 } else if (entry->type == TabRestoreService::WINDOW) { | |
| 752 AddWindow(static_cast<const TabRestoreService::Window*>(entry), | |
| 753 &recently_closed_pages_, kRecentlyClosedCount); | |
| 754 } | |
| 755 } | |
| 756 | |
| 757 // Send a query that retrieves the first favicon. | |
| 758 StartLoadingFavicon(); | |
| 759 } | |
| 760 | |
| 761 void JumpList::OnFaviconDataAvailable( | 717 void JumpList::OnFaviconDataAvailable( |
| 762 FaviconService::Handle handle, | 718 FaviconService::Handle handle, |
| 763 history::FaviconData favicon) { | 719 history::FaviconData favicon) { |
| 764 // Attach the received data to the ShellLinkItem object. | 720 // If there is currently a favicon request in progress, it is now outdated, |
| 765 // This data will be decoded by JumpListUpdateTask. | 721 // as we have received another, so nullify the handle from the old request. |
| 766 if (favicon.is_valid()) { | 722 handle_ = NULL; |
| 767 if (!icon_urls_.empty() && icon_urls_.front().second) | 723 // lock the list to set icon data and pop the url |
| 768 icon_urls_.front().second->SetIconData(favicon.image_data); | 724 { |
| 725 base::AutoLock auto_lock(list_lock_); | |
| 726 // Attach the received data to the ShellLinkItem object. | |
| 727 // This data will be decoded by the RunUpdate method. | |
| 728 if (favicon.is_valid()) { | |
| 729 if (!icon_urls_.empty() && icon_urls_.front().second) | |
| 730 icon_urls_.front().second->SetIconData(favicon.image_data); | |
| 731 } | |
| 732 | |
| 733 if (!icon_urls_.empty()) | |
| 734 icon_urls_.pop_front(); | |
| 769 } | 735 } |
| 770 | |
| 771 // if we need to load more favicons, we send another query and exit. | 736 // if we need to load more favicons, we send another query and exit. |
| 772 if (!icon_urls_.empty()) | |
| 773 icon_urls_.pop_front(); | |
| 774 if (StartLoadingFavicon()) | 737 if (StartLoadingFavicon()) |
| 775 return; | 738 return; |
| 776 | 739 |
| 777 // Finished loading all favicons needed by the application JumpList. | 740 // Finished loading all favicons needed by the application JumpList. |
| 778 // We create a JumpListUpdateTask that creates icon files, and we post it to | 741 // We use a RunnableMethod that creates icon files, and we post it to |
| 779 // the file thread. | 742 // the file thread. |
| 780 BrowserThread::PostTask( | 743 BrowserThread::PostTask( |
| 781 BrowserThread::FILE, FROM_HERE, | 744 BrowserThread::FILE, FROM_HERE, |
| 782 new JumpListUpdateTask(app_id_.c_str(), icon_dir_, most_visited_pages_, | 745 NewRunnableMethod(this, &JumpList::RunUpdate)); |
| 783 recently_closed_pages_)); | 746 } |
| 784 | 747 |
| 785 // Delete all items in these lists since we don't need these lists any longer. | 748 void JumpList::RunUpdate() { |
| 786 most_visited_pages_.clear(); | 749 ShellLinkItemList local_most_visited_pages; |
| 787 recently_closed_pages_.clear(); | 750 ShellLinkItemList local_recently_closed_pages; |
| 751 | |
| 752 { | |
| 753 base::AutoLock auto_lock(list_lock_); | |
| 754 // Make sure we are not out of date: if icon_urls_ is not empty, then | |
| 755 // another notification has been received since we processed this one | |
| 756 if (!icon_urls_.empty()) | |
| 757 return; | |
| 758 | |
| 759 // Make local copies of lists so we can release the lock. | |
| 760 local_most_visited_pages = most_visited_pages_; | |
| 761 local_recently_closed_pages = recently_closed_pages_; | |
| 762 } | |
| 763 | |
| 764 // Delete the directory which contains old icon files, rename the current | |
| 765 // icon directory, and create a new directory which contains new JumpList | |
| 766 // icon files. | |
| 767 FilePath icon_dir_old(icon_dir_.value() + L"Old"); | |
| 768 if (file_util::PathExists(icon_dir_old)) | |
| 769 file_util::Delete(icon_dir_old, true); | |
| 770 file_util::Move(icon_dir_, icon_dir_old); | |
| 771 file_util::CreateDirectory(icon_dir_); | |
| 772 | |
| 773 // Create temporary icon files for shortcuts in the "Most Visited" category. | |
| 774 DecodeIconData(local_most_visited_pages); | |
| 775 | |
| 776 // Create temporary icon files for shortcuts in the "Recently Closed" | |
| 777 // category. | |
| 778 DecodeIconData(local_recently_closed_pages); | |
| 779 | |
| 780 // We finished collecting all resources needed for updating an appliation | |
| 781 // JumpList. So, create a new JumpList and replace the current JumpList | |
| 782 // with it. | |
| 783 UpdateJumpList(app_id_.c_str(), local_most_visited_pages, | |
| 784 local_recently_closed_pages); | |
|
MAD
2011/08/19 14:46:03
Wrong alignment, should be aligned to app_id, righ
| |
| 788 } | 785 } |
| 786 | |
| 787 void JumpList::DecodeIconData(const ShellLinkItemList& item_list) { | |
| 788 for (ShellLinkItemList::const_iterator item = item_list.begin(); | |
| 789 item != item_list.end(); ++item) { | |
| 790 SkBitmap icon_bitmap; | |
| 791 if ((*item)->data().get() && | |
| 792 gfx::PNGCodec::Decode((*item)->data()->front(), | |
| 793 (*item)->data()->size(), | |
| 794 &icon_bitmap)) { | |
| 795 FilePath icon_path; | |
| 796 if (CreateIconFile(icon_bitmap, icon_dir_, &icon_path)) | |
| 797 (*item)->SetIcon(icon_path.value(), 0, true); | |
| 798 } | |
| 799 } | |
| 800 } | |
| OLD | NEW |