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 :handle_(NULL) { | |
|
MAD
2011/08/05 21:21:43
Single line...
| |
| 578 } | 486 } |
| 579 | 487 |
| 580 JumpList::~JumpList() { | 488 JumpList::~JumpList() { |
| 581 RemoveObserver(); | 489 RemoveObserver(); |
| 582 } | 490 } |
| 583 | 491 |
| 584 // static | 492 // static |
| 585 bool JumpList::Enabled() { | 493 bool JumpList::Enabled() { |
| 586 return (base::win::GetVersion() >= base::win::VERSION_WIN7 && | 494 return (base::win::GetVersion() >= base::win::VERSION_WIN7 && |
| 587 !CommandLine::ForCurrentProcess()->HasSwitch( | 495 !CommandLine::ForCurrentProcess()->HasSwitch( |
| 588 switches::kDisableCustomJumpList)); | 496 switches::kDisableCustomJumpList)); |
| 589 } | 497 } |
| 590 | 498 |
| 591 bool JumpList::AddObserver(Profile* profile) { | 499 bool JumpList::AddObserver(Profile* profile) { |
| 592 // To update JumpList when a tab is added or removed, we add this object to | 500 // To update JumpList when a tab is added or removed, we add this object to |
| 593 // the observer list of the TabRestoreService class. | 501 // the observer list of the TabRestoreService class. |
| 594 // When we add this object to the observer list, we save the pointer to this | 502 // 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 | 503 // TabRestoreService object. This pointer is used when we remove this object |
| 596 // from the observer list. | 504 // from the observer list. |
| 597 if (base::win::GetVersion() < base::win::VERSION_WIN7 || !profile) | 505 if (base::win::GetVersion() < base::win::VERSION_WIN7 || !profile) |
| 598 return false; | 506 return false; |
| 599 | 507 profile_ = profile; |
|
MAD
2011/08/05 21:21:43
No need to move that up anymore, might as well put
| |
| 600 TabRestoreService* tab_restore_service = | 508 TabRestoreService* tab_restore_service = |
| 601 TabRestoreServiceFactory::GetForProfile(profile); | 509 TabRestoreServiceFactory::GetForProfile(profile); |
| 602 if (!tab_restore_service) | 510 if (!tab_restore_service) |
| 603 return false; | 511 return false; |
| 604 | 512 |
| 605 app_id_ = ShellIntegration::GetChromiumAppId(profile->GetPath()); | 513 app_id_ = ShellIntegration::GetChromiumAppId(profile->GetPath()); |
| 606 icon_dir_ = profile->GetPath().Append(chrome::kJumpListIconDirname); | 514 icon_dir_ = profile->GetPath().Append(chrome::kJumpListIconDirname); |
| 607 profile_ = profile; | 515 history::TopSites* ts = profile_->GetTopSites(); |
|
MAD
2011/08/05 21:21:43
I would prefer a more meaningful variable name tha
| |
| 516 if (ts) { | |
| 517 // TopSites updates itself after a delay. This is especially noticable when | |
| 518 // your profile is empty. Ask TopSites to update itself when we're about to | |
| 519 // show the new tab page. | |
| 520 ts->SyncWithHistory(); | |
| 521 // Register for notification when TopSites changes so that we can update | |
| 522 // ourself. | |
| 523 registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED, | |
| 524 Source<history::TopSites>(ts)); | |
| 525 } | |
| 608 tab_restore_service->AddObserver(this); | 526 tab_restore_service->AddObserver(this); |
| 609 return true; | 527 return true; |
| 610 } | 528 } |
| 611 | 529 |
| 530 void JumpList::Observe(int type, | |
| 531 const NotificationSource& source, | |
| 532 const NotificationDetails& details) { | |
| 533 DCHECK_EQ(type, chrome::NOTIFICATION_TOP_SITES_CHANGED); | |
| 534 | |
| 535 // Most visited urls changed, query again. | |
|
MAD
2011/08/05 21:21:43
Most visited -> Top sites
| |
| 536 StartQueryForMostVisited(); | |
| 537 } | |
| 538 | |
| 612 void JumpList::RemoveObserver() { | 539 void JumpList::RemoveObserver() { |
| 613 if (profile_) { | 540 if (profile_) { |
| 614 TabRestoreService* tab_restore_service = | 541 TabRestoreService* tab_restore_service = |
| 615 TabRestoreServiceFactory::GetForProfile(profile_); | 542 TabRestoreServiceFactory::GetForProfile(profile_); |
| 616 if (tab_restore_service) | 543 if (tab_restore_service) |
| 617 tab_restore_service->RemoveObserver(this); | 544 tab_restore_service->RemoveObserver(this); |
| 618 } | 545 } |
| 619 profile_ = NULL; | 546 profile_ = NULL; |
| 620 } | 547 } |
| 621 | 548 |
| 549 void JumpList::StartQueryForMostVisited() { | |
| 550 history::TopSites* ts = profile_->GetTopSites(); | |
|
MAD
2011/08/05 21:21:43
Again, ts -> top_sites, please... :-)
| |
| 551 if (ts) { | |
| 552 ts->GetMostVisitedURLs( | |
| 553 &topsites_consumer_, | |
| 554 NewCallback(this, | |
| 555 &JumpList::OnMostVisitedURLsAvailable)); | |
|
Evan Stade
2011/08/05 20:39:03
too much indent. Can this fit on the line above?
| |
| 556 } | |
| 557 } | |
| 558 | |
| 559 void JumpList::OnMostVisitedURLsAvailable( | |
| 560 const history::MostVisitedURLList& data) { | |
| 561 | |
| 562 // If we have a pending favicon request, cancel it here (it is out of date). | |
| 563 if (handle_) { | |
| 564 FaviconService* favicon_service = | |
| 565 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); | |
|
MAD
2011/08/05 21:21:43
Need 4 more spaces of indent...
| |
| 566 favicon_service->CancelRequest(handle_); | |
| 567 handle_ = NULL; | |
| 568 } | |
| 569 | |
| 570 { | |
| 571 base::AutoLock auto_lock(list_lock_); | |
| 572 most_visited_pages_.clear(); | |
| 573 for (size_t i = 0; i < data.size(); i++) { | |
| 574 const history::MostVisitedURL& url = data[i]; | |
| 575 scoped_refptr<ShellLinkItem> link(new ShellLinkItem); | |
| 576 std::string url_string = url.url.spec(); | |
| 577 link->SetArguments(UTF8ToWide(url_string)); | |
| 578 link->SetTitle(!url.title.empty()? url.title : link->arguments()); | |
| 579 most_visited_pages_.push_back(link); | |
| 580 icon_urls_.push_back(make_pair(url_string, link)); | |
| 581 } | |
| 582 } | |
| 583 | |
| 584 // Send a query that retrieves the first favicon. | |
| 585 StartLoadingFavicon(); | |
| 586 } | |
| 587 | |
| 622 void JumpList::TabRestoreServiceChanged(TabRestoreService* service) { | 588 void JumpList::TabRestoreServiceChanged(TabRestoreService* service) { |
| 623 // Added or removed a tab. | 589 // if we have a pending handle request, cancel it here (it is out of date). |
| 624 // Exit if we are updating the application JumpList. | 590 if (handle_) { |
| 625 if (!icon_urls_.empty()) | 591 FaviconService* favicon_service = |
| 626 return; | 592 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); |
|
MAD
2011/08/05 21:21:43
Again, 4 more indent spaces please...
| |
| 593 favicon_service->CancelRequest(handle_); | |
| 594 handle_ = NULL; | |
| 595 } | |
| 627 | 596 |
| 628 // Send a query to HistoryService and retrieve the "Most Visited" pages. | 597 { |
| 629 // This code is copied from MostVisitedHandler::HandleGetMostVisited() to | 598 base::AutoLock auto_lock(list_lock_); |
| 630 // emulate its behaviors. | 599 recently_closed_pages_.clear(); |
| 631 const int kMostVisitedScope = 90; | 600 } |
| 632 const int kMostVisitedCount = 9; | 601 |
| 633 int result_count = kMostVisitedCount; | 602 // Create a list of ShellLinkItems from the "Recently Closed" pages. |
| 634 HistoryService* history_service = | 603 // As noted above, we create a ShellLinkItem objects with the following |
| 635 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | 604 // parameters. |
| 636 history_service->QuerySegmentUsageSince( | 605 // * arguments |
| 637 &most_visited_consumer_, | 606 // The last URL of the tab object. |
| 638 base::Time::Now() - base::TimeDelta::FromDays(kMostVisitedScope), | 607 // * title |
| 639 result_count, | 608 // The title of the last URL. |
| 640 NewCallback(this, &JumpList::OnSegmentUsageAvailable)); | 609 // * icon |
| 610 // An empty string. This value is to be updated in OnFaviconDataAvailable(). | |
| 611 // This code is copied from | |
| 612 // RecentlyClosedTabsHandler::TabRestoreServiceChanged() to emulate it. | |
| 613 const int kRecentlyClosedCount = 4; | |
| 614 TabRestoreService* tab_restore_service = | |
| 615 TabRestoreServiceFactory::GetForProfile(profile_); | |
| 616 const TabRestoreService::Entries& entries = tab_restore_service->entries(); | |
| 617 for (TabRestoreService::Entries::const_iterator it = entries.begin(); | |
| 618 it != entries.end(); ++it) { | |
| 619 const TabRestoreService::Entry* entry = *it; | |
| 620 if (entry->type == TabRestoreService::TAB) { | |
| 621 AddTab(static_cast<const TabRestoreService::Tab*>(entry), | |
| 622 &recently_closed_pages_, kRecentlyClosedCount); | |
|
MAD
2011/08/05 21:21:43
I'm not comfortable passing the pointers to protec
| |
| 623 } else if (entry->type == TabRestoreService::WINDOW) { | |
| 624 AddWindow(static_cast<const TabRestoreService::Window*>(entry), | |
| 625 &recently_closed_pages_, kRecentlyClosedCount); | |
| 626 } | |
| 627 } | |
| 628 | |
| 629 // Send a query that retrieves the first favicon. | |
| 630 StartLoadingFavicon(); | |
| 641 } | 631 } |
| 642 | 632 |
| 643 void JumpList::TabRestoreServiceDestroyed(TabRestoreService* service) { | 633 void JumpList::TabRestoreServiceDestroyed(TabRestoreService* service) { |
| 644 } | 634 } |
| 645 | 635 |
| 646 bool JumpList::AddTab(const TabRestoreService::Tab* tab, | 636 bool JumpList::AddTab(const TabRestoreService::Tab* tab, |
| 647 ShellLinkItemList* list, | 637 ShellLinkItemList* list, |
| 648 size_t max_items) { | 638 size_t max_items) { |
| 649 // This code adds the URL and the title strings of the given tab to the | 639 // This code adds the URL and the title strings of the given tab to the |
| 650 // specified list. | 640 // specified list. |
| 651 // This code is copied from RecentlyClosedTabsHandler::TabToValue(). | 641 // This code is copied from RecentlyClosedTabsHandler::TabToValue(). |
| 642 base::AutoLock auto_lock(list_lock_); | |
| 652 if (tab->navigations.empty() || list->size() >= max_items) | 643 if (tab->navigations.empty() || list->size() >= max_items) |
| 653 return false; | 644 return false; |
| 654 | 645 |
| 655 const TabNavigation& current_navigation = | 646 const TabNavigation& current_navigation = |
| 656 tab->navigations.at(tab->current_navigation_index); | 647 tab->navigations.at(tab->current_navigation_index); |
| 657 if (current_navigation.virtual_url() == GURL(chrome::kChromeUINewTabURL)) | 648 if (current_navigation.virtual_url() == GURL(chrome::kChromeUINewTabURL)) |
| 658 return false; | 649 return false; |
| 659 | 650 |
| 660 scoped_refptr<ShellLinkItem> link(new ShellLinkItem); | 651 scoped_refptr<ShellLinkItem> link(new ShellLinkItem); |
| 661 std::string url = current_navigation.virtual_url().spec(); | 652 std::string url = current_navigation.virtual_url().spec(); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 678 } | 669 } |
| 679 for (size_t i = 0; i < window->tabs.size(); ++i) { | 670 for (size_t i = 0; i < window->tabs.size(); ++i) { |
| 680 if (!AddTab(&window->tabs[i], list, max_items)) | 671 if (!AddTab(&window->tabs[i], list, max_items)) |
| 681 return false; | 672 return false; |
| 682 } | 673 } |
| 683 | 674 |
| 684 return true; | 675 return true; |
| 685 } | 676 } |
| 686 | 677 |
| 687 bool JumpList::StartLoadingFavicon() { | 678 bool JumpList::StartLoadingFavicon() { |
| 679 // Ask FaviconService if it has a favicon of a URL. | |
|
MAD
2011/08/05 21:21:43
I would bring back this comment where it was...
| |
| 680 // When FaviconService has one, it will call OnFaviconDataAvailable(). | |
| 688 if (icon_urls_.empty()) | 681 if (icon_urls_.empty()) |
|
MAD
2011/08/05 21:21:43
Even though this is atomic, I would prefer we did
| |
| 689 return false; | 682 return false; |
| 690 | 683 |
| 691 // Ask FaviconService if it has a favicon of a URL. | 684 GURL url; |
| 692 // When FaviconService has one, it will call OnFaviconDataAvailable(). | 685 { |
| 693 GURL url(icon_urls_.front().first); | 686 base::AutoLock auto_lock(list_lock_); |
| 687 url = GURL(icon_urls_.front().first); | |
| 688 } | |
| 694 FaviconService* favicon_service = | 689 FaviconService* favicon_service = |
| 695 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); | 690 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); |
| 696 FaviconService::Handle handle = favicon_service->GetFaviconForURL( | 691 handle_ = favicon_service->GetFaviconForURL( |
| 697 url, history::FAVICON, &favicon_consumer_, | 692 url, history::FAVICON, &favicon_consumer_, |
| 698 NewCallback(this, &JumpList::OnFaviconDataAvailable)); | 693 NewCallback(this, &JumpList::OnFaviconDataAvailable)); |
| 699 return true; | 694 return true; |
| 700 } | 695 } |
| 701 | 696 |
| 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( | 697 void JumpList::OnFaviconDataAvailable( |
| 762 FaviconService::Handle handle, | 698 FaviconService::Handle handle, |
| 763 history::FaviconData favicon) { | 699 history::FaviconData favicon) { |
| 764 // Attach the received data to the ShellLinkItem object. | 700 handle_ = NULL; |
|
MAD
2011/08/05 21:21:43
Maybe add a comment to specify why we do this?
| |
| 765 // This data will be decoded by JumpListUpdateTask. | 701 // lock the list to set icon data and pop the url |
| 766 if (favicon.is_valid()) { | 702 { |
| 767 if (!icon_urls_.empty() && icon_urls_.front().second) | 703 base::AutoLock auto_lock(list_lock_); |
| 768 icon_urls_.front().second->SetIconData(favicon.image_data); | 704 // Attach the received data to the ShellLinkItem object. |
| 705 // This data will be decoded by JumpListUpdateTask. | |
|
Evan Stade
2011/08/05 20:39:03
comment out of date
| |
| 706 if (favicon.is_valid()) { | |
| 707 if (!icon_urls_.empty() && icon_urls_.front().second) | |
| 708 icon_urls_.front().second->SetIconData(favicon.image_data); | |
| 709 } | |
| 710 | |
| 711 if (!icon_urls_.empty()) | |
| 712 icon_urls_.pop_front(); | |
| 769 } | 713 } |
| 770 | |
| 771 // if we need to load more favicons, we send another query and exit. | 714 // 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()) | 715 if (StartLoadingFavicon()) |
| 775 return; | 716 return; |
| 776 | 717 |
| 777 // Finished loading all favicons needed by the application JumpList. | 718 // Finished loading all favicons needed by the application JumpList. |
| 778 // We create a JumpListUpdateTask that creates icon files, and we post it to | 719 // We create a RunnableMethod that creates icon files, and we post it to |
| 779 // the file thread. | 720 // the file thread. |
| 721 this->AddRef(); | |
|
MAD
2011/08/05 21:21:43
Please add a comment stating who/where will releas
Cait (Slow)
2011/08/05 22:49:16
Actually..this AddRef is not supposed to be here..
| |
| 780 BrowserThread::PostTask( | 722 BrowserThread::PostTask( |
| 781 BrowserThread::FILE, FROM_HERE, | 723 BrowserThread::FILE, FROM_HERE, |
| 782 new JumpListUpdateTask(app_id_.c_str(), icon_dir_, most_visited_pages_, | 724 NewRunnableMethod(this, &JumpList::RunUpdate)); |
| 783 recently_closed_pages_)); | 725 } |
| 784 | 726 |
| 785 // Delete all items in these lists since we don't need these lists any longer. | 727 void JumpList::RunUpdate() { |
| 786 most_visited_pages_.clear(); | 728 ShellLinkItemList local_most_visited_pages; |
| 787 recently_closed_pages_.clear(); | 729 ShellLinkItemList local_recently_closed_pages; |
|
Evan Stade
2011/08/05 20:39:03
please sprinkle in some newlines for readability
| |
| 730 { | |
| 731 base::AutoLock auto_lock(list_lock_); | |
| 732 // Make sure we are not out of date: if icon_urls_ is not empty, then | |
| 733 // another notification has been received since we processed this one | |
| 734 if (!icon_urls_.empty()) | |
| 735 return; | |
| 736 // Make local copies of lists so we can release the lock. | |
| 737 ShellLinkItemList temp_most_visited_pages(most_visited_pages_); | |
|
MAD
2011/08/05 21:21:43
Why do we need these temps? Can't we simply do:
lo
Cait (Slow)
2011/08/05 22:49:16
The data member lists only get cleared immediately
| |
| 738 ShellLinkItemList temp_recently_closed_pages(recently_closed_pages_); | |
| 739 local_most_visited_pages = temp_most_visited_pages; | |
| 740 local_recently_closed_pages = temp_recently_closed_pages; | |
| 741 } | |
| 742 // Delete the directory which contains old icon files, rename the current | |
| 743 // icon directory, and create a new directory which contains new JumpList | |
| 744 // icon files. | |
| 745 FilePath icon_dir_old(icon_dir_.value() + L"Old"); | |
| 746 if (file_util::PathExists(icon_dir_old)) | |
| 747 file_util::Delete(icon_dir_old, true); | |
| 748 file_util::Move(icon_dir_, icon_dir_old); | |
| 749 file_util::CreateDirectory(icon_dir_); | |
| 750 | |
| 751 // Create temporary icon files for shortcuts in the "Most Visited" category. | |
| 752 for (ShellLinkItemList::const_iterator item = local_most_visited_pages.begin() ; | |
|
Evan Stade
2011/08/05 20:39:03
80
| |
| 753 item != local_most_visited_pages.end(); ++item) { | |
| 754 SkBitmap icon_bitmap; | |
| 755 if ((*item)->data().get() && | |
| 756 gfx::PNGCodec::Decode((*item)->data()->front(), | |
| 757 (*item)->data()->size(), | |
| 758 &icon_bitmap)) { | |
| 759 FilePath icon_path; | |
| 760 if (CreateIconFile(icon_bitmap, icon_dir_, &icon_path)) | |
| 761 (*item)->SetIcon(icon_path.value(), 0, true); | |
| 762 } | |
| 763 } | |
| 764 | |
| 765 // Create temporary icon files for shortcuts in the "Recently Closed" | |
| 766 // category. | |
| 767 for (ShellLinkItemList::const_iterator item = local_recently_closed_pages.begi n(); | |
|
Evan Stade
2011/08/05 20:39:03
80
| |
| 768 item != local_recently_closed_pages.end(); ++item) { | |
| 769 SkBitmap icon_bitmap; | |
| 770 if ((*item)->data().get() && | |
| 771 gfx::PNGCodec::Decode((*item)->data()->front(), | |
| 772 (*item)->data()->size(), | |
| 773 &icon_bitmap)) { | |
| 774 FilePath icon_path; | |
| 775 if (CreateIconFile(icon_bitmap, icon_dir_, &icon_path)) | |
| 776 (*item)->SetIcon(icon_path.value(), 0, true); | |
|
Evan Stade
2011/08/05 20:39:03
refactor this loop into helper function (it seems
| |
| 777 } | |
| 778 } | |
| 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); | |
| 788 } | 785 } |
| OLD | NEW |