summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorhbono@chromium.org <hbono@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-24 10:24:39 +0000
committerhbono@chromium.org <hbono@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-24 10:24:39 +0000
commit07388164de80e5f61c4faf558e040bb2182b343c (patch)
treeed2b462af58d45e2df10c49610bd0db2f32a139a /chrome
parent89dd1a308e2534e885a4609e86cc72368a657c4b (diff)
downloadchromium_src-07388164de80e5f61c4faf558e040bb2182b343c.zip
chromium_src-07388164de80e5f61c4faf558e040bb2182b343c.tar.gz
chromium_src-07388164de80e5f61c4faf558e040bb2182b343c.tar.bz2
Integrates a custom JumpList of Windows 7 into Chromium.
This change adds an option "--enable-custom-jumplist" that uses the ICustomDestinationList interface to add "Most Visited" pages, "Recently Closed" pages, and "Tasks" to the JumpList of Chromium. This change registers the CustomJumpList class into an observer of TabRestoreService so it can update the JumpList when a user adds/removes a tab. This change stores icon files used by a custom JumpList under "$(User Data Dir)/JumpListIcons" so Taskbar can show JumpList icons even when Chromium is not running. BUG=8037 TEST=Right-click the taskbar icon of Chromium on Windows 7. Review URL: http://codereview.chromium.org/56175 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21522 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/jumplist.cc736
-rw-r--r--chrome/browser/jumplist.h186
-rw-r--r--chrome/browser/views/frame/browser_view.cc10
-rw-r--r--chrome/browser/views/frame/browser_view.h9
-rw-r--r--chrome/chrome.gyp3
-rw-r--r--chrome/common/chrome_constants.cc1
-rw-r--r--chrome/common/chrome_constants.h1
-rw-r--r--chrome/common/chrome_switches.cc3
-rw-r--r--chrome/common/chrome_switches.h2
9 files changed, 951 insertions, 0 deletions
diff --git a/chrome/browser/jumplist.cc b/chrome/browser/jumplist.cc
new file mode 100644
index 0000000..2732971
--- /dev/null
+++ b/chrome/browser/jumplist.cc
@@ -0,0 +1,736 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/jumplist.h"
+
+#include <windows.h>
+#include <shobjidl.h>
+#include <propkey.h>
+#include <propvarutil.h>
+
+#include <string>
+#include <vector>
+
+#include "app/gfx/icon_util.h"
+#include "app/l10n_util.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/gfx/png_decoder.h"
+#include "base/path_service.h"
+#include "base/scoped_comptr_win.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "base/win_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/history/page_usage_data.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/sessions/tab_restore_service.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+#include "grit/generated_resources.h"
+
+namespace {
+
+// COM interfaces used in this file.
+// These interface declarations are copied from Windows SDK 7.0.
+// TODO(hbono): Bug 16903: to be deleted them when we use Windows SDK 7.0.
+#ifndef __IObjectArray_INTERFACE_DEFINED__
+#define __IObjectArray_INTERFACE_DEFINED__
+
+MIDL_INTERFACE("92CA9DCD-5622-4bba-A805-5E9F541BD8C9")
+IObjectArray : public IUnknown {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE GetCount(
+ /* [out] */ __RPC__out UINT *pcObjects) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetAt(
+ /* [in] */ UINT uiIndex,
+ /* [in] */ __RPC__in REFIID riid,
+ /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0;
+};
+
+#endif // __IObjectArray_INTERFACE_DEFINED__
+
+#ifndef __IObjectCollection_INTERFACE_DEFINED__
+#define __IObjectCollection_INTERFACE_DEFINED__
+
+MIDL_INTERFACE("5632b1a4-e38a-400a-928a-d4cd63230295")
+IObjectCollection : public IObjectArray {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE AddObject(
+ /* [in] */ __RPC__in_opt IUnknown *punk) = 0;
+ virtual HRESULT STDMETHODCALLTYPE AddFromArray(
+ /* [in] */ __RPC__in_opt IObjectArray *poaSource) = 0;
+ virtual HRESULT STDMETHODCALLTYPE RemoveObjectAt(
+ /* [in] */ UINT uiIndex) = 0;
+ virtual HRESULT STDMETHODCALLTYPE Clear(void) = 0;
+};
+
+#endif // __IObjectCollection_INTERFACE_DEFINED__
+
+#ifndef __ICustomDestinationList_INTERFACE_DEFINED__
+#define __ICustomDestinationList_INTERFACE_DEFINED__
+
+typedef /* [v1_enum] */ enum tagKNOWNDESTCATEGORY {
+ KDC_FREQUENT = 1,
+ KDC_RECENT = (KDC_FREQUENT + 1)
+} KNOWNDESTCATEGORY;
+
+MIDL_INTERFACE("6332debf-87b5-4670-90c0-5e57b408a49e")
+ICustomDestinationList : public IUnknown {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE SetAppID(
+ /* [string][in] */__RPC__in_string LPCWSTR pszAppID) = 0;
+ virtual HRESULT STDMETHODCALLTYPE BeginList(
+ /* [out] */ __RPC__out UINT *pcMaxSlots,
+ /* [in] */ __RPC__in REFIID riid,
+ /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0;
+ virtual HRESULT STDMETHODCALLTYPE AppendCategory(
+ /* [string][in] */ __RPC__in_string LPCWSTR pszCategory,
+ /* [in] */ __RPC__in_opt IObjectArray *poa) = 0;
+ virtual HRESULT STDMETHODCALLTYPE AppendKnownCategory(
+ /* [in] */ KNOWNDESTCATEGORY category) = 0;
+ virtual HRESULT STDMETHODCALLTYPE AddUserTasks(
+ /* [in] */ __RPC__in_opt IObjectArray *poa) = 0;
+ virtual HRESULT STDMETHODCALLTYPE CommitList(void) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetRemovedDestinations(
+ /* [in] */ __RPC__in REFIID riid,
+ /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0;
+ virtual HRESULT STDMETHODCALLTYPE DeleteList(
+ /* [string][in] */ __RPC__in_string LPCWSTR pszAppID) = 0;
+ virtual HRESULT STDMETHODCALLTYPE AbortList(void) = 0;
+};
+
+#endif // __ICustomDestinationList_INTERFACE_DEFINED__
+
+// Class IDs used in this file.
+// These class IDs should be defined in an anonymous namespace to avoid
+// potential conflicts with ones defined in "shell32.lib" of Microsoft SDK 7.0.
+// TODO(hbono): Bug 16903: to be deleted them when we use Windows SDK 7.0.
+EXTERN_C const CLSID CLSID_DestinationList = {
+ 0x77f10cf0, 0x3db5, 0x4966, {0xb5, 0x20, 0xb7, 0xc5, 0x4f, 0xd3, 0x5e, 0xd6}
+};
+
+EXTERN_C const CLSID CLSID_EnumerableObjectCollection = {
+ 0x2d3468c1, 0x36a7, 0x43b6, {0xac, 0x24, 0xd3, 0xf0, 0x2f, 0xd9, 0x60, 0x7a}
+};
+
+}; // namespace
+
+// END OF WINDOWS 7 SDK DEFINITIONS
+
+namespace {
+
+// Represents a class which encapsulates a PROPVARIANT object containing a
+// string for AddShellLink().
+// This class automatically deletes all the resources attached to the
+// PROPVARIANT object in its destructor.
+class PropVariantString {
+ public:
+ PropVariantString() {
+ property_.vt = VT_EMPTY;
+ }
+
+ HRESULT Init(const std::wstring& value) {
+ // Call InitPropVariantFromString() to initialize this PROPVARIANT object.
+ // To read <propvarutil.h>, it seems InitPropVariantFromString() is an
+ // inline function that initialize a PROPVARIANT object and calls
+ // SHStrDupW() to set a copy of its input string.
+ // So, we just calls it without creating a copy.
+ return InitPropVariantFromString(value.c_str(), &property_);
+ }
+
+ ~PropVariantString() {
+ if (property_.vt != VT_EMPTY)
+ PropVariantClear(&property_);
+ }
+
+ const PROPVARIANT& Get() {
+ return property_;
+ }
+
+ private:
+ PROPVARIANT property_;
+
+ DISALLOW_COPY_AND_ASSIGN(PropVariantString);
+};
+
+// Creates an IShellLink object.
+// An IShellLink object is almost the same as an application shortcut, and it
+// requires three items: the absolute path to an application, an argument
+// string, and a title string.
+HRESULT AddShellLink(ScopedComPtr<IObjectCollection> collection,
+ const std::wstring& application,
+ const std::wstring& switches,
+ scoped_refptr<ShellLinkItem> item) {
+ // Create an IShellLink object.
+ ScopedComPtr<IShellLink> link;
+ HRESULT result = link.CreateInstance(CLSID_ShellLink, NULL,
+ CLSCTX_INPROC_SERVER);
+ if (FAILED(result))
+ return result;
+
+ // Set the application path.
+ // We should exit this function when this call fails because it doesn't make
+ // any sense to add a shortcut that we cannot execute.
+ result = link->SetPath(application.c_str());
+ if (FAILED(result))
+ return result;
+
+ // Attach the command-line switches of this process before the given
+ // arguments and set it as the arguments of this IShellLink object.
+ // We also exit this function when this call fails because it isn't usuful to
+ // add a shortcut that cannot open the given page.
+ std::wstring arguments(switches);
+ if (!item->arguments().empty()) {
+ arguments.push_back(L' ');
+ arguments += item->arguments();
+ }
+ result = link->SetArguments(arguments.c_str());
+ if (FAILED(result))
+ return result;
+
+ // Attach the given icon path to this IShellLink object.
+ // Since an icon is an optional item for an IShellLink object, so we don't
+ // have to exit even when it fails.
+ if (!item->icon().empty())
+ link->SetIconLocation(item->icon().c_str(), item->index());
+
+ // Set the title of the IShellLink object.
+ // The IShellLink interface does not have any functions which update its
+ // title because this interface is originally for creating an application
+ // shortcut which doesn't have titles.
+ // So, we should use the IPropertyStore interface to set its title as
+ // listed in the steps below:
+ // 1. Retrieve the IPropertyStore interface from the IShellLink object;
+ // 2. Start a transaction that changes a value of the object with the
+ // IPropertyStore interface;
+ // 3. Create a string PROPVARIANT, and;
+ // 4. Call the IPropertyStore::SetValue() function to Set the title property
+ // of the IShellLink object.
+ // 5. Commit the transaction.
+ ScopedComPtr<IPropertyStore> property_store;
+ result = link.QueryInterface(property_store.Receive());
+ if (FAILED(result))
+ return result;
+
+ PropVariantString property_title;
+ result = property_title.Init(item->title());
+ if (FAILED(result))
+ return result;
+
+ result = property_store->SetValue(PKEY_Title, property_title.Get());
+ if (FAILED(result))
+ return result;
+
+ result = property_store->Commit();
+ if (FAILED(result))
+ return result;
+
+ // Add this IShellLink object to the given collection.
+ return collection->AddObject(link);
+}
+
+// Creates a temporary icon file to be shown in JumpList.
+bool CreateIconFile(const SkBitmap& bitmap,
+ const std::wstring& icon_dir,
+ std::wstring* icon_path) {
+ // Retrieve the path to a temporary file.
+ // We don't have to care about the extension of this temporary file because
+ // JumpList does not care about it.
+ std::wstring path;
+ if (!file_util::CreateTemporaryFileNameInDir(icon_dir, &path))
+ return false;
+
+ // Create an icon file from the favicon attached to the given |page|, and
+ // save it as the temporary file.
+ if (!IconUtil::CreateIconFileFromSkBitmap(bitmap, path))
+ return false;
+
+ // Add this icon file to the list and return its absolute path.
+ // The IShellLink::SetIcon() function needs the absolute path to an icon.
+ icon_path->assign(path);
+ return true;
+}
+
+// Updates a specified category of an application JumpList.
+// This function cannot update registered categories (such as "Tasks") because
+// special steps are required for updating them.
+// So, this function can be used only for adding an unregistered category.
+// Parameters:
+// * category_id (int)
+// A string ID which contains the category name.
+// * application (std::wstring)
+// An application name to be used for creating JumpList items.
+// Even though we can add command-line switches to this parameter, it is
+// better to use the |switches| parameter below.
+// * switches (std::wstring)
+// Command-lien switches for the application. This string is to be added
+// before the arguments of each ShellLinkItem object. If there aren't any
+// switches, use an empty string.
+// * data (ShellLinkItemList)
+// A list of ShellLinkItem objects to be added under the specified category.
+HRESULT UpdateCategory(ScopedComPtr<ICustomDestinationList> list,
+ int category_id,
+ const std::wstring& application,
+ const std::wstring& switches,
+ const ShellLinkItemList& data) {
+ // Exit this function when the given vector does not contain any items
+ // because an ICustomDestinationList::AppendCategory() call fails in this
+ // case.
+ if (data.empty())
+ return S_OK;
+
+ std::wstring category = l10n_util::GetString(category_id);
+
+ // Create an EnumerableObjectCollection object.
+ // We once add the given items to this collection object and add this
+ // collection to the JumpList.
+ ScopedComPtr<IObjectCollection> collection;
+ HRESULT result = collection.CreateInstance(CLSID_EnumerableObjectCollection,
+ NULL, CLSCTX_INPROC_SERVER);
+ if (FAILED(result))
+ return false;
+
+ for (ShellLinkItemList::const_iterator item = data.begin();
+ item != data.end(); ++item) {
+ scoped_refptr<ShellLinkItem> link(*item);
+ AddShellLink(collection, application, switches, link);
+ }
+
+ // We can now add the new list to the JumpList.
+ // The ICustomDestinationList::AppendCategory() function needs the
+ // IObjectArray interface to retrieve each item in the list. So, we retrive
+ // the IObjectArray interface from the IEnumeratableObjectCollection object
+ // and use it.
+ // It seems the ICustomDestinationList::AppendCategory() function just
+ // replaces all items in the given category with the ones in the new list.
+ ScopedComPtr<IObjectArray> object_array;
+ result = collection.QueryInterface(object_array.Receive());
+ if (FAILED(result))
+ return false;
+
+ return list->AppendCategory(category.c_str(), object_array);
+}
+
+// Updates the "Tasks" category of the JumpList.
+// Even though this function is almost the same as UpdateCategory(), this
+// function has the following differences:
+// * The "Task" category is a registered category.
+// We should use AddUserTasks() instead of AppendCategory().
+// * The items in the "Task" category are static.
+// We don't have to use a list.
+HRESULT UpdateTaskCategory(ScopedComPtr<ICustomDestinationList> list,
+ const std::wstring& chrome_path,
+ const std::wstring& chrome_switches) {
+ // Create an EnumerableObjectCollection object to be added items of the
+ // "Task" category. (We can also use this object for the "Task" category.)
+ ScopedComPtr<IObjectCollection> collection;
+ HRESULT result = collection.CreateInstance(CLSID_EnumerableObjectCollection,
+ NULL, CLSCTX_INPROC_SERVER);
+ if (FAILED(result))
+ return result;
+
+ // Create an IShellLink object which launches Chrome, and add it to the
+ // collection. We use our application icon as the icon for this item.
+ // We remove '&' characters from this string so we can share it with our
+ // system menu.
+ scoped_refptr<ShellLinkItem> chrome(new ShellLinkItem);
+ std::wstring chrome_title(l10n_util::GetString(IDS_NEW_WINDOW));
+ ReplaceSubstringsAfterOffset(&chrome_title, 0, L"&", L"");
+ chrome->SetTitle(chrome_title);
+ chrome->SetIcon(chrome_path, 0, false);
+ AddShellLink(collection, chrome_path, chrome_switches, chrome);
+
+ // Create an IShellLink object which launches Chrome in incognito mode, and
+ // add it to the collection. We use our application icon as the icon for
+ // this item.
+ scoped_refptr<ShellLinkItem> incognito(new ShellLinkItem);
+ incognito->SetArguments(
+ CommandLine::PrefixedSwitchString(switches::kIncognito));
+ std::wstring incognito_title(l10n_util::GetString(IDS_NEW_INCOGNITO_WINDOW));
+ ReplaceSubstringsAfterOffset(&incognito_title, 0, L"&", L"");
+ incognito->SetTitle(incognito_title);
+ incognito->SetIcon(chrome_path, 0, false);
+ AddShellLink(collection, chrome_path, chrome_switches, incognito);
+
+ // We can now add the new list to the JumpList.
+ // ICustomDestinationList::AddUserTasks() also uses the IObjectArray
+ // interface to retrieve each item in the list. So, we retrieve the
+ // IObjectArray interface from the EnumerableObjectCollection object.
+ ScopedComPtr<IObjectArray> object_array;
+ result = collection.QueryInterface(object_array.Receive());
+ if (FAILED(result))
+ return result;
+
+ return list->AddUserTasks(object_array);
+}
+
+// Updates the application JumpList.
+// This function encapsulates all OS-specific operations required for updating
+// the Chromium JumpList, such as:
+// * Creating an ICustomDestinationList instance;
+// * Updating the categories of the ICustomDestinationList instance, and;
+// * Sending it to Taskbar of Windows 7.
+bool UpdateJumpList(const ShellLinkItemList& most_visited_pages,
+ const ShellLinkItemList& recently_closed_pages) {
+ // JumpList is implemented only on Windows 7 or later.
+ // So, we should return now when this function is called on earlier versions
+ // of Windows.
+ if (win_util::GetWinVersion() < win_util::WINVERSION_WIN7)
+ return true;
+
+ // Create an ICustomDestinationList object and attach it to our application.
+ ScopedComPtr<ICustomDestinationList> destination_list;
+ HRESULT result = destination_list.CreateInstance(CLSID_DestinationList, NULL,
+ CLSCTX_INPROC_SERVER);
+ if (FAILED(result))
+ return false;
+
+ // Start a transaction that updates the JumpList of this application.
+ // This implementation just replaces the all items in this JumpList, so
+ // we don't have to use the IObjectArray object returned from this call.
+ // It seems Windows 7 RC (Build 7100) automatically checks the items in this
+ // removed list and prevent us from adding the same item.
+ UINT max_slots;
+ ScopedComPtr<IObjectArray> removed;
+ result = destination_list->BeginList(&max_slots, __uuidof(*removed),
+ reinterpret_cast<void**>(&removed));
+ if (FAILED(result))
+ return false;
+
+ // Retrieve the absolute path to "chrome.exe".
+ std::wstring chrome_path;
+ if (!PathService::Get(base::FILE_EXE, &chrome_path))
+ return false;
+
+ // Retrieve the command-line switches of this process.
+ std::wstring chrome_switches;
+
+ // Update the "Most Visited" category of the JumpList.
+ // This update request is applied into the JumpList when we commit this
+ // transaction.
+ result = UpdateCategory(destination_list, IDS_NEW_TAB_MOST_VISITED,
+ chrome_path, chrome_switches, most_visited_pages);
+ if (FAILED(result))
+ return false;
+
+ // Update the "Recently Closed" category of the JumpList.
+ result = UpdateCategory(destination_list, IDS_NEW_TAB_RECENTLY_CLOSED,
+ chrome_path, chrome_switches, recently_closed_pages);
+ if (FAILED(result))
+ return false;
+
+ // Update the "Tasks" category of the JumpList.
+ result = UpdateTaskCategory(destination_list, chrome_path, chrome_switches);
+ if (FAILED(result))
+ return false;
+
+ // Commit this transaction and send the updated JumpList to Windows.
+ result = destination_list->CommitList();
+ if (FAILED(result))
+ return false;
+
+ return true;
+}
+
+// Represents a task which updates an application JumpList.
+// This task encapsulates all I/O tasks and OS-specific tasks required for
+// updating a JumpList from Chromium, such as:
+// * Deleting the directory containing temporary icon files;
+// * Creating temporary icon files used by the JumpList;
+// * Creating an ICustomDestinationList instance;
+// * Adding items in the ICustomDestinationList instance.
+// To spawn this task,
+// 1. Prepare objects required by this task:
+// * a std::wstring that contains a temporary icons;
+// * a ShellLinkItemList that contains the items of the "Most Visited"
+// category, and;
+// * a ShellLinkItemList that contains the items of the "Recently Closed"
+// category.
+// 2. Create a JumpListUpdateTask instance, and;
+// 3. Post this task to the file thread.
+class JumpListUpdateTask : public Task {
+ public:
+ JumpListUpdateTask(const std::wstring& icon_dir,
+ const ShellLinkItemList& most_visited_pages,
+ const ShellLinkItemList& recently_closed_pages)
+ : icon_dir_(icon_dir),
+ most_visited_pages_(most_visited_pages),
+ recently_closed_pages_(recently_closed_pages) {
+ }
+
+ private:
+ // Represents an entry point of this task.
+ // When we post this task to a file thread, the thread calls this function.
+ void Run();
+
+ // The directory which contains JumpList icons.
+ std::wstring icon_dir_;
+
+ // Items in the "Most Visited" category of the application JumpList.
+ ShellLinkItemList most_visited_pages_;
+
+ // Items in the "Recently Closed" category of the application JumpList.
+ ShellLinkItemList recently_closed_pages_;
+};
+
+void JumpListUpdateTask::Run() {
+ // Delete the directory which contains old icon files, rename the current
+ // icon directory, and create a new directory which contains new JumpList
+ // icon files.
+ std::wstring icon_dir_old(icon_dir_ + L"Old");
+ if (file_util::PathExists(icon_dir_old))
+ file_util::Delete(icon_dir_old, true);
+ file_util::Move(icon_dir_, icon_dir_old);
+ file_util::CreateDirectory(icon_dir_);
+
+ // Create temporary icon files for shortcuts in the "Most Visited" category.
+ for (ShellLinkItemList::const_iterator item = most_visited_pages_.begin();
+ item != most_visited_pages_.end(); ++item) {
+ SkBitmap icon_bitmap;
+ if ((*item)->data().get() &&
+ PNGDecoder::Decode(&(*item)->data()->data, &icon_bitmap)) {
+ std::wstring icon_path;
+ if (CreateIconFile(icon_bitmap, icon_dir_, &icon_path))
+ (*item)->SetIcon(icon_path, 0, true);
+ }
+ }
+
+ // Create temporary icon files for shortcuts in the "Recently Closed"
+ // category.
+ for (ShellLinkItemList::const_iterator item = recently_closed_pages_.begin();
+ item != recently_closed_pages_.end(); ++item) {
+ SkBitmap icon_bitmap;
+ if ((*item)->data().get() &&
+ PNGDecoder::Decode(&(*item)->data()->data, &icon_bitmap)) {
+ std::wstring icon_path;
+ if (CreateIconFile(icon_bitmap, icon_dir_, &icon_path))
+ (*item)->SetIcon(icon_path, 0, true);
+ }
+ }
+
+ // We finished collecting all resources needed for updating an appliation
+ // JumpList. So, create a new JumpList and replace the current JumpList
+ // with it.
+ UpdateJumpList(most_visited_pages_, recently_closed_pages_);
+
+ // Delete all items in these lists now since we don't need the ShellLinkItem
+ // objects in these lists.
+ most_visited_pages_.clear();
+ recently_closed_pages_.clear();
+}
+
+} // namespace
+
+JumpList::JumpList() : profile_(NULL) {
+}
+
+JumpList::~JumpList() {
+ RemoveObserver();
+}
+
+// static
+bool JumpList::Enabled() {
+ return (win_util::GetWinVersion() >= win_util::WINVERSION_WIN7 &&
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableCustomJumpList));
+}
+
+bool JumpList::AddObserver(Profile* profile) {
+ // To update JumpList when a tab is added or removed, we add this object to
+ // the observer list of the TabRestoreService class.
+ // When we add this object to the observer list, we save the pointer to this
+ // TabRestoreService object. This pointer is used when we remove this object
+ // from the observer list.
+ if (win_util::GetWinVersion() < win_util::WINVERSION_WIN7 || !profile)
+ return false;
+
+ TabRestoreService* tab_restore_service = profile->GetTabRestoreService();
+ if (!tab_restore_service)
+ return false;
+
+ icon_dir_ = profile->GetPath().Append(chrome::kJumpListIconDirname).value();
+ profile_ = profile;
+ tab_restore_service->AddObserver(this);
+ return true;
+}
+
+void JumpList::RemoveObserver() {
+ if (profile_ && profile_->GetTabRestoreService())
+ profile_->GetTabRestoreService()->RemoveObserver(this);
+ profile_ = NULL;
+}
+
+void JumpList::TabRestoreServiceChanged(TabRestoreService* service) {
+ // Added or removed a tab.
+ // Exit if we are updating the application JumpList.
+ if (!icon_urls_.empty())
+ return;
+
+ // Send a query to HistoryService and retrieve the "Most Visited" pages.
+ // This code is copied from MostVisitedHandler::HandleGetMostVisited() to
+ // emulate its behaviors.
+ const int kMostVisitedScope = 90;
+ const int kMostVisitedCount = 9;
+ int result_count = kMostVisitedCount;
+ HistoryService* history_service =
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ history_service->QuerySegmentUsageSince(
+ &most_visited_consumer_,
+ base::Time::Now() - base::TimeDelta::FromDays(kMostVisitedScope),
+ result_count,
+ NewCallback(this, &JumpList::OnSegmentUsageAvailable));
+}
+
+void JumpList::TabRestoreServiceDestroyed(TabRestoreService* service) {
+}
+
+bool JumpList::AddTab(const TabRestoreService::Tab* tab,
+ ShellLinkItemList* list,
+ size_t max_items) {
+ // This code adds the URL and the title strings of the given tab to the
+ // specified list.
+ // This code is copied from RecentlyClosedTabsHandler::TabToValue().
+ if (tab->navigations.empty() || list->size() >= max_items)
+ return false;
+
+ const TabNavigation& current_navigation =
+ tab->navigations.at(tab->current_navigation_index);
+ if (current_navigation.url() == GURL(chrome::kChromeUINewTabURL))
+ return false;
+
+ scoped_refptr<ShellLinkItem> link(new ShellLinkItem);
+ std::string url = current_navigation.url().spec();
+ link->SetArguments(UTF8ToWide(url));
+ link->SetTitle(current_navigation.title());
+ list->push_back(link);
+ icon_urls_.push_back(make_pair(url, link));
+ return true;
+}
+
+bool JumpList::AddWindow(const TabRestoreService::Window* window,
+ ShellLinkItemList* list,
+ size_t max_items) {
+ // This code enumerates al the tabs in the given window object and add their
+ // URLs and titles to the list.
+ // This code is copied from RecentlyClosedTabsHandler::WindowToValue().
+ if (window->tabs.empty()) {
+ NOTREACHED();
+ return false;
+ }
+ for (size_t i = 0; i < window->tabs.size(); ++i) {
+ if (!AddTab(&window->tabs[i], list, max_items))
+ return false;
+ }
+
+ return true;
+}
+
+bool JumpList::StartLoadingFavIcon() {
+ if (icon_urls_.empty())
+ return false;
+
+ // Ask HistoryService if it has a fav icon of a URL.
+ // When HistoryService has one, it will call OnFavIconDataAvailable().
+ GURL url(icon_urls_.front().first);
+ HistoryService* history_service =
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ HistoryService::Handle handle = history_service->GetFavIconForURL(
+ url, &fav_icon_consumer_,
+ NewCallback(this, &JumpList::OnFavIconDataAvailable));
+ return true;
+}
+
+void JumpList::OnSegmentUsageAvailable(
+ CancelableRequestProvider::Handle handle,
+ std::vector<PageUsageData*>* data) {
+ // Create a list of ShellLinkItem objects from the given list of
+ // PageUsageData objects.
+ // The command that opens a web page with chrome is:
+ // "chrome.exe <url-to-the-web-page>".
+ // So, we create a ShellLinkItem object with the following parameters.
+ // * arguments
+ // The URL of a PageUsagedata object (converted to std::wstring).
+ // * title
+ // The title of a PageUsageData object. If this string is empty, we use
+ // the URL as our "Most Visited" page does.
+ // * icon
+ // An empty string. This value is to be updated in OnFavIconDataAvailable().
+ most_visited_pages_.clear();
+ for (std::vector<PageUsageData*>::const_iterator page = data->begin();
+ page != data->end(); ++page) {
+ scoped_refptr<ShellLinkItem> link(new ShellLinkItem);
+ std::string url = (*page)->GetURL().spec();
+ link->SetArguments(UTF8ToWide(url));
+ link->SetTitle(
+ !(*page)->GetTitle().empty() ? (*page)->GetTitle() : link->arguments());
+ most_visited_pages_.push_back(link);
+ icon_urls_.push_back(make_pair(url, link));
+ }
+
+ // Create a list of ShellLinkItems from the "Recently Closed" pages.
+ // As noted above, we create a ShellLinkItem objects with the following
+ // parameters.
+ // * arguments
+ // The last URL of the tab object.
+ // * title
+ // The title of the last URL.
+ // * icon
+ // An empty string. This value is to be updated in OnFavIconDataAvailable().
+ // This code is copied from
+ // RecentlyClosedTabsHandler::TabRestoreServiceChanged() to emulate it.
+ recently_closed_pages_.clear();
+ TabRestoreService* tab_restore_service = profile_->GetTabRestoreService();
+ const TabRestoreService::Entries& entries = tab_restore_service->entries();
+ for (TabRestoreService::Entries::const_iterator it = entries.begin();
+ it != entries.end(); ++it) {
+ const TabRestoreService::Entry* entry = *it;
+ if (entry->type == TabRestoreService::TAB) {
+ AddTab(static_cast<const TabRestoreService::Tab*>(entry),
+ &recently_closed_pages_, 3);
+ } else if (entry->type == TabRestoreService::WINDOW) {
+ AddWindow(static_cast<const TabRestoreService::Window*>(entry),
+ &recently_closed_pages_, 3);
+ }
+ }
+
+ // Send a query that retrieves the first fav icon.
+ StartLoadingFavIcon();
+}
+
+void JumpList::OnFavIconDataAvailable(
+ HistoryService::Handle handle,
+ bool know_favicon,
+ scoped_refptr<RefCountedBytes> data,
+ bool expired,
+ GURL icon_url) {
+ // Attach the received data to the ShellLinkItem object.
+ // This data will be decoded by JumpListUpdateTask.
+ if (know_favicon && data.get() && !data->data.empty())
+ icon_urls_.front().second->SetIconData(data);
+
+ // if we need to load more fav icons, we send another query and exit.
+ icon_urls_.pop_front();
+ if (StartLoadingFavIcon())
+ return;
+
+ // Finished Loading all fav icons needed by the application JumpList.
+ // We create a JumpListUpdateTask that creates icon files, and we post it to
+ // the file thread.
+ Task* icon_task = new JumpListUpdateTask(icon_dir_,
+ most_visited_pages_,
+ recently_closed_pages_);
+ MessageLoop* file_loop = g_browser_process->file_thread()->message_loop();
+ if (file_loop)
+ file_loop->PostTask(FROM_HERE, icon_task);
+
+ // Delete all items in these lists since we don't need these lists any longer.
+ most_visited_pages_.clear();
+ recently_closed_pages_.clear();
+}
diff --git a/chrome/browser/jumplist.h b/chrome/browser/jumplist.h
new file mode 100644
index 0000000..265377a
--- /dev/null
+++ b/chrome/browser/jumplist.h
@@ -0,0 +1,186 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_JUMPLIST_H_
+#define CHROME_BROWSER_JUMPLIST_H_
+
+#include <list>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "chrome/browser/cancelable_request.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/sessions/tab_restore_service.h"
+
+class Profile;
+class PageUsageData;
+
+// Represents a class used for creating an IShellLink object by the utility
+// functions in this file.
+// This class consists of three strings and a integer.
+// * arguments (std::wstring)
+// The arguments for the application.
+// * title (std::wstring)
+// The string to be displayed in a JumpList.
+// * icon (std::wstring)
+// The absolute path to an icon to be displayed in a JumpList.
+// * index (int)
+// The icon index in the icon file. If an icon file consists of two or more
+// icons, set this value to identify the icon. If an icon file consists of
+// one icon, this value is 0.
+// Even though an IShellLink also needs the absolute path to an application to
+// be executed, this class does not have any variables for it because our
+// utility functions always use "chrome.exe" as the application and we don't
+// need it.
+class ShellLinkItem : public base::RefCounted<ShellLinkItem> {
+ public:
+ ShellLinkItem() : index_(0), favicon_(false) {
+ }
+
+ ~ShellLinkItem() {
+ }
+
+ const std::wstring& arguments() const { return arguments_; }
+ const std::wstring& title() const { return title_; }
+ const std::wstring& icon() const { return icon_; }
+ int index() const { return index_; }
+ scoped_refptr<RefCountedBytes> data() const { return data_; }
+
+ void SetArguments(const std::wstring& arguments) {
+ arguments_ = arguments;
+ }
+
+ void SetTitle(const std::wstring& title) {
+ title_ = title;
+ }
+
+ void SetIcon(const std::wstring& icon, int index, bool favicon) {
+ icon_ = icon;
+ index_ = index;
+ favicon_ = favicon;
+ }
+
+ void SetIconData(scoped_refptr<RefCountedBytes> data) {
+ data_ = data;
+ }
+
+ private:
+ std::wstring arguments_;
+ std::wstring title_;
+ std::wstring icon_;
+ scoped_refptr<RefCountedBytes> data_;
+ int index_;
+ bool favicon_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShellLinkItem);
+};
+
+typedef std::vector<scoped_refptr<ShellLinkItem> > ShellLinkItemList;
+
+// A class which implements an application JumpList.
+// This class encapsulates operations required for updating an application
+// JumpList:
+// * Retrieving "Most Visited" pages from HistoryService;
+// * Retrieving strings from the application resource;
+// * Creatng COM objects used by JumpList from PageUsageData objects;
+// * Adding COM objects to JumpList, etc.
+//
+// This class also implements TabRestoreService::Observer. So, once we call
+// AddObserver() and register this class as an observer, it automatically
+// updates a JumpList when a tab is added or removed.
+//
+// Updating a JumpList requires some file operations and it is not good to
+// update it in a UI thread. To solve this problem, this class posts a
+// task when it actually updates a JumpList. (This task is implemented in an
+// anomynous namespace in "jumplist_win.cc".)
+class JumpList : public TabRestoreService::Observer {
+ public:
+ JumpList();
+ ~JumpList();
+
+ // Registers (or unregisters) this object as an observer.
+ // When the TabRestoreService object notifies the tab status is changed, this
+ // class automatically updates an application JumpList.
+ bool AddObserver(Profile* profile);
+ void RemoveObserver();
+
+ // Observer callback for TabRestoreService::Observer to notify when a tab is
+ // added or removed.
+ // This function sends a query that retrieves "Most Visited" pages to
+ // HistoryService. When the query finishes successfully, HistoryService call
+ // OnSegmentUsageAvailable().
+ virtual void TabRestoreServiceChanged(TabRestoreService* service);
+
+ // Observer callback to notice when our associated TabRestoreService
+ // is destroyed.
+ virtual void TabRestoreServiceDestroyed(TabRestoreService* service);
+
+ // Returns true if the custom JumpList is enabled.
+ // We use the custom JumpList when we satisfy the following conditions:
+ // * Chromium is running on Windows 7 and;
+ // * Chromium is lauched with an "--enable-custom-jumplist" option.
+ // TODO(hbono): to be enabled by default when we finalize the categories and
+ // items of our JumpList.
+ static bool Enabled();
+
+ protected:
+ // Creates a ShellLinkItem object from a tab (or a window) and add it to the
+ // given list.
+ // These functions are copied from the RecentlyClosedTabsHandler class for
+ // compatibility with the new-tab page.
+ bool AddTab(const TabRestoreService::Tab* tab,
+ ShellLinkItemList* list,
+ size_t max_items);
+ bool AddWindow(const TabRestoreService::Window* window,
+ ShellLinkItemList* list,
+ size_t max_items);
+
+ // Starts loading a fav icon for each URL in |icon_urls_|.
+ // This function just sends a query to HistoryService.
+ bool StartLoadingFavIcon();
+
+ // A callback function for HistoryService that notify when the "Most Visited"
+ // list is available.
+ // This function updates the ShellLinkItemList objects and send another query
+ // that retrieves a fav icon for each URL in the list.
+ void OnSegmentUsageAvailable(CancelableRequestProvider::Handle handle,
+ std::vector<PageUsageData*>* data);
+
+ // a callback function for HistoryService that notify when a requested fav
+ // icon is available.
+ // To avoid file operations, this function just attaches the given data to
+ // a ShellLinkItem object.
+ // When finishing loading all fav icons, this function posts a task that
+ // decompresses collected fav icons and updates a JumpList.
+ void OnFavIconDataAvailable(HistoryService::Handle handle,
+ bool know_favicon,
+ scoped_refptr<RefCountedBytes> data,
+ bool expired,
+ GURL icon_url);
+
+ private:
+ // Our consumers for HistoryService.
+ CancelableRequestConsumer most_visited_consumer_;
+ CancelableRequestConsumer fav_icon_consumer_;
+
+ // The Profile object used for listening its events.
+ Profile* profile_;
+
+ // The directory which contains JumpList icons.
+ std::wstring icon_dir_;
+
+ // Items in the "Most Visited" category of the application JumpList.
+ ShellLinkItemList most_visited_pages_;
+
+ // Items in the "Recently Closed" category of the application JumpList.
+ ShellLinkItemList recently_closed_pages_;
+
+ // A list of URLs we need to retrieve their fav icons.
+ typedef std::pair<std::string, scoped_refptr<ShellLinkItem> > URLPair;
+ std::list<URLPair> icon_urls_;
+};
+
+#endif // CHROME_BROWSER_JUMPLIST_H_
diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc
index 422a1d6..24e29da 100644
--- a/chrome/browser/views/frame/browser_view.cc
+++ b/chrome/browser/views/frame/browser_view.cc
@@ -27,6 +27,9 @@
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/find_bar.h"
#include "chrome/browser/find_bar_controller.h"
+#if defined(OS_WIN)
+#include "chrome/browser/jumplist.h"
+#endif
#include "chrome/browser/profile.h"
#include "chrome/browser/view_ids.h"
#include "chrome/browser/views/bookmark_bar_view.h"
@@ -1481,6 +1484,13 @@ void BrowserView::Init() {
#if defined(OS_WIN)
InitSystemMenu();
+
+ // Create a custom JumpList and add it to an observer of TabRestoreService
+ // so we can update the custom JumpList when a tab is added or removed.
+ if (JumpList::Enabled()) {
+ jumplist_.reset(new JumpList);
+ jumplist_->AddObserver(browser_->profile());
+ }
#endif
}
diff --git a/chrome/browser/views/frame/browser_view.h b/chrome/browser/views/frame/browser_view.h
index 859beba..a202530 100644
--- a/chrome/browser/views/frame/browser_view.h
+++ b/chrome/browser/views/frame/browser_view.h
@@ -5,9 +5,12 @@
#ifndef CHROME_BROWSER_VIEWS_FRAME_BROWSER_VIEW_H_
#define CHROME_BROWSER_VIEWS_FRAME_BROWSER_VIEW_H_
+#include <map>
#include <set>
+#include <string>
#include "base/gfx/native_widget_types.h"
+#include "base/scoped_ptr.h"
#include "base/timer.h"
#include "build/build_config.h"
#include "chrome/browser/browser.h"
@@ -37,6 +40,9 @@ class ExtensionShelf;
class FullscreenExitBubble;
class HtmlDialogUIDelegate;
class InfoBarContainer;
+#if defined(OS_WIN)
+class JumpList;
+#endif
class LocationBarView;
class StatusBubbleViews;
class TabContentsContainer;
@@ -473,6 +479,9 @@ class BrowserView : public BrowserWindow,
// This object is invoked by hung_window_detector_ when it detects a hung
// plugin window.
HungPluginAction hung_plugin_action_;
+
+ // The custom JumpList for Windows 7.
+ scoped_ptr<JumpList> jumplist_;
#endif
// The timer used to update frames for the Loading Animation.
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index f2ba135..14570e3 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1245,6 +1245,8 @@
'browser/input_window_dialog_win.cc',
'browser/jankometer.cc',
'browser/jankometer.h',
+ 'browser/jumplist.cc',
+ 'browser/jumplist.h',
'browser/jsmessage_box_handler.cc',
'browser/jsmessage_box_handler.h',
'browser/keychain_mac.cc',
@@ -2291,6 +2293,7 @@
'browser/history_view.cc',
'browser/ime_input.cc',
'browser/importer/ie_importer.cc',
+ 'browser/jumplist.cc',
'browser/memory_details.cc',
'browser/modal_html_dialog_delegate.cc',
'browser/sandbox_policy.cc',
diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc
index 797b418..fbedb7c 100644
--- a/chrome/common/chrome_constants.cc
+++ b/chrome/common/chrome_constants.cc
@@ -77,6 +77,7 @@ const FilePath::CharType kHistoryBookmarksFileName[] =
const FilePath::CharType kCustomDictionaryFileName[] =
FPL("Custom Dictionary.txt");
const FilePath::CharType kLoginDataFileName[] = FPL("Login Data");
+const FilePath::CharType kJumpListIconDirname[] = FPL("JumpListIcons");
// This number used to be limited to 32 in the past (see b/535234).
const unsigned int kMaxRendererProcessCount = 42;
diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h
index bfb0367..4f9f4a1 100644
--- a/chrome/common/chrome_constants.h
+++ b/chrome/common/chrome_constants.h
@@ -44,6 +44,7 @@ extern const FilePath::CharType kBookmarksFileName[];
extern const FilePath::CharType kHistoryBookmarksFileName[];
extern const FilePath::CharType kCustomDictionaryFileName[];
extern const FilePath::CharType kLoginDataFileName[];
+extern const FilePath::CharType kJumpListIconDirname[];
extern const unsigned int kMaxRendererProcessCount;
extern const int kStatsMaxThreads;
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index ee617fd..1835ea6 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -554,4 +554,7 @@ const wchar_t kAllowSandboxDebugging[] = L"allow-sandbox-debugging";
// Triggers a pletora of diagnostic modes.
const wchar_t kDiagnostics[] = L"diagnostics";
+// Enables the custom JumpList on Windows 7.
+const wchar_t kEnableCustomJumpList[] = L"enable-custom-jumplist";
+
} // namespace switches
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index ea089e8..87d4cd6 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -217,6 +217,8 @@ extern const wchar_t kAllowSandboxDebugging[];
extern const wchar_t kDiagnostics[];
+extern const wchar_t kEnableCustomJumpList[];
+
} // namespace switches
#endif // CHROME_COMMON_CHROME_SWITCHES_H_