jumplist_win.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
11e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 51e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "chrome/browser/jumplist_win.h" 61e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <windows.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <shobjidl.h> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <propkey.h> 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <propvarutil.h> 111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include <string> 13a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include <vector> 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h" 16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/bind_helpers.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_util.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/path_service.h" 2090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/string_util.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/thread.h" 227dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "base/utf_string_conversions.h" 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/scoped_comptr.h" 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/scoped_propvariant.h" 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/windows_version.h" 261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "chrome/browser/favicon/favicon_service.h" 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/favicon/favicon_service_factory.h" 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/favicon/favicon_types.h" 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/history/history_service.h" 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/history/page_usage_data.h" 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/history/top_sites.h" 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h" 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sessions/session_types.h" 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sessions/tab_restore_service.h" 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sessions/tab_restore_service_factory.h" 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/shell_integration.h" 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_constants.h" 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_notification_types.h" 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_switches.h" 4090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/common/url_constants.h" 41a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "content/public/browser/browser_thread.h" 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_source.h" 43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "googleurl/src/gurl.h" 44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "grit/chromium_strings.h" 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/generated_resources.h" 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkBitmap.h" 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h" 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/codec/png_codec.h" 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/favicon_size.h" 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/icon_util.h" 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image_family.h" 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread; 541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// COM interfaces used in this file. 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// These interface declarations are copied from Windows SDK 7.0. 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(hbono): Bug 16903: delete them when we use Windows SDK 7.0. 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef __IObjectArray_INTERFACE_DEFINED__ 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define __IObjectArray_INTERFACE_DEFINED__ 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MIDL_INTERFACE("92CA9DCD-5622-4bba-A805-5E9F541BD8C9") 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IObjectArray : public IUnknown { 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE GetCount( 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /* [out] */ __RPC__out UINT *pcObjects) = 0; 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE GetAt( 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /* [in] */ UINT uiIndex, 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /* [in] */ __RPC__in REFIID riid, 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif // __IObjectArray_INTERFACE_DEFINED__ 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef __IObjectCollection_INTERFACE_DEFINED__ 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define __IObjectCollection_INTERFACE_DEFINED__ 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MIDL_INTERFACE("5632b1a4-e38a-400a-928a-d4cd63230295") 802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)IObjectCollection : public IObjectArray { 812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public: 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE AddObject( 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /* [in] */ __RPC__in_opt IUnknown *punk) = 0; 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE AddFromArray( 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /* [in] */ __RPC__in_opt IObjectArray *poaSource) = 0; 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE RemoveObjectAt( 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /* [in] */ UINT uiIndex) = 0; 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE Clear(void) = 0; 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif // __IObjectCollection_INTERFACE_DEFINED__ 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef __ICustomDestinationList_INTERFACE_DEFINED__ 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#define __ICustomDestinationList_INTERFACE_DEFINED__ 955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)typedef /* [v1_enum] */ enum tagKNOWNDESTCATEGORY { 975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) KDC_FREQUENT = 1, 985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) KDC_RECENT = (KDC_FREQUENT + 1) 995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} KNOWNDESTCATEGORY; 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MIDL_INTERFACE("6332debf-87b5-4670-90c0-5e57b408a49e") 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ICustomDestinationList : public IUnknown { 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE SetAppID( 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /* [string][in] */__RPC__in_string LPCWSTR pszAppID) = 0; 10690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE BeginList( 107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /* [out] */ __RPC__out UINT *pcMaxSlots, 108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /* [in] */ __RPC__in REFIID riid, 109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0; 110010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE AppendCategory( 111868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) /* [string][in] */ __RPC__in_string LPCWSTR pszCategory, 112868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) /* [in] */ __RPC__in_opt IObjectArray *poa) = 0; 11390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE AppendKnownCategory( 114a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) /* [in] */ KNOWNDESTCATEGORY category) = 0; 115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE AddUserTasks( 116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /* [in] */ __RPC__in_opt IObjectArray *poa) = 0; 1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE CommitList(void) = 0; 118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE GetRemovedDestinations( 119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /* [in] */ __RPC__in REFIID riid, 120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0; 121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE DeleteList( 122116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch /* [string][in] */ __RPC__in_string LPCWSTR pszAppID) = 0; 123a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) virtual HRESULT STDMETHODCALLTYPE AbortList(void) = 0; 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#endif // __ICustomDestinationList_INTERFACE_DEFINED__ 127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 128c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// Class IDs used in this file. 129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// These class IDs must be defined in an anonymous namespace to avoid 130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// conflicts with ones defined in "shell32.lib" of Visual Studio 2008. 131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// TODO(hbono): Bug 16903: delete them when we use Windows SDK 7.0. 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const CLSID CLSID_DestinationList = { 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 0x77f10cf0, 0x3db5, 0x4966, {0xb5, 0x20, 0xb7, 0xc5, 0x4f, 0xd3, 0x5e, 0xd6} 1346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}; 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const CLSID CLSID_EnumerableObjectCollection = { 137a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 0x2d3468c1, 0x36a7, 0x43b6, {0xac, 0x24, 0xd3, 0xf0, 0x2f, 0xd9, 0x60, 0x7a} 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 139868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; // namespace 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 142a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// END OF WINDOWS 7 SDK DEFINITIONS 143a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 144f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace { 145a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 146a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Creates an IShellLink object. 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// An IShellLink object is almost the same as an application shortcut, and it 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// requires three items: the absolute path to an application, an argument 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// string, and a title string. 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HRESULT AddShellLink(base::win::ScopedComPtr<IObjectCollection> collection, 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::wstring& application, 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::wstring& switches, 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_refptr<ShellLinkItem> item) { 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Create an IShellLink object. 155a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch base::win::ScopedComPtr<IShellLink> link; 156a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch HRESULT result = link.CreateInstance(CLSID_ShellLink, NULL, 157a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch CLSCTX_INPROC_SERVER); 1584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (FAILED(result)) 1594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return result; 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set the application path. 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We should exit this function when this call fails because it doesn't make 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // any sense to add a shortcut that we cannot execute. 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result = link->SetPath(application.c_str()); 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (FAILED(result)) 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result; 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Attach the command-line switches of this process before the given 169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // arguments and set it as the arguments of this IShellLink object. 170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // We also exit this function when this call fails because it isn't usuful to 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // add a shortcut that cannot open the given page. 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::wstring arguments(switches); 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!item->arguments().empty()) { 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) arguments.push_back(L' '); 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) arguments += item->arguments(); 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result = link->SetArguments(arguments.c_str()); 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (FAILED(result)) 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result; 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Attach the given icon path to this IShellLink object. 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Since an icon is an optional item for an IShellLink object, so we don't 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // have to exit even when it fails. 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!item->icon().empty()) 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) link->SetIconLocation(item->icon().c_str(), item->index()); 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set the title of the IShellLink object. 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The IShellLink interface does not have any functions which update its 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // title because this interface is originally for creating an application 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // shortcut which doesn't have titles. 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // So, we should use the IPropertyStore interface to set its title as 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // listed in the steps below: 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 1. Retrieve the IPropertyStore interface from the IShellLink object; 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 2. Start a transaction that changes a value of the object with the 1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // IPropertyStore interface; 1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // 3. Create a string PROPVARIANT, and; 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 4. Call the IPropertyStore::SetValue() function to Set the title property 19890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // of the IShellLink object. 19990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // 5. Commit the transaction. 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::win::ScopedComPtr<IPropertyStore> property_store; 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result = link.QueryInterface(property_store.Receive()); 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (FAILED(result)) 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result; 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::win::ScopedPropVariant property_title; 206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Call InitPropVariantFromString() to initialize |property_title|. Reading 207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // <propvarutil.h>, it seems InitPropVariantFromString() is an inline function 208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // that initializes a PROPVARIANT object and calls SHStrDupW() to set a copy 2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // of its input string. It is thus safe to call it without first creating a 2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // copy here. 2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) result = InitPropVariantFromString(item->title().c_str(), 2121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) property_title.Receive()); 2131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (FAILED(result)) 2141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return result; 2151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 2161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) result = property_store->SetValue(PKEY_Title, property_title.get()); 2171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (FAILED(result)) 2181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return result; 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result = property_store->Commit(); 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (FAILED(result)) 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result; 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Add this IShellLink object to the given collection. 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return collection->AddObject(link); 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Creates a temporary icon file to be shown in JumpList. 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CreateIconFile(const SkBitmap& bitmap, 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const base::FilePath& icon_dir, 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::FilePath* icon_path) { 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Retrieve the path to a temporary file. 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We don't have to care about the extension of this temporary file because 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // JumpList does not care about it. 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::FilePath path; 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!file_util::CreateTemporaryFileInDir(icon_dir, &path)) 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Create an icon file from the favicon attached to the given |page|, and 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // save it as the temporary file. 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gfx::ImageFamily image_family; 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) image_family.Add(gfx::Image::CreateFrom1xBitmap(bitmap)); 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!IconUtil::CreateIconFileFromImageFamily(image_family, path)) 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Add this icon file to the list and return its absolute path. 2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // The IShellLink::SetIcon() function needs the absolute path to an icon. 2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *icon_path = path; 2495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return true; 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Updates a specified category of an application JumpList. 2534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// This function cannot update registered categories (such as "Tasks") because 2544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// special steps are required for updating them. 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// So, this function can be used only for adding an unregistered category. 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Parameters: 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// * category_id (int) 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A string ID which contains the category name. 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// * application (std::wstring) 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// An application name to be used for creating JumpList items. 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Even though we can add command-line switches to this parameter, it is 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// better to use the |switches| parameter below. 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// * switches (std::wstring) 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Command-lien switches for the application. This string is to be added 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// before the arguments of each ShellLinkItem object. If there aren't any 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// switches, use an empty string. 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// * data (ShellLinkItemList) 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A list of ShellLinkItem objects to be added under the specified category. 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HRESULT UpdateCategory(base::win::ScopedComPtr<ICustomDestinationList> list, 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int category_id, 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::wstring& application, 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::wstring& switches, 273a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const ShellLinkItemList& data, 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int max_slots) { 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Exit this function when the given vector does not contain any items 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // because an ICustomDestinationList::AppendCategory() call fails in this 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // case. 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (data.empty() || !max_slots) 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return S_OK; 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::wstring category = UTF16ToWide(l10n_util::GetStringUTF16(category_id)); 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Create an EnumerableObjectCollection object. 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We once add the given items to this collection object and add this 285a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch // collection to the JumpList. 286a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch base::win::ScopedComPtr<IObjectCollection> collection; 2871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci HRESULT result = collection.CreateInstance(CLSID_EnumerableObjectCollection, 2881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci NULL, CLSCTX_INPROC_SERVER); 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (FAILED(result)) 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (ShellLinkItemList::const_iterator item = data.begin(); 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) item != data.end() && max_slots > 0; ++item, --max_slots) { 2941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) scoped_refptr<ShellLinkItem> link(*item); 295 AddShellLink(collection, application, switches, link); 296 } 297 298 // We can now add the new list to the JumpList. 299 // The ICustomDestinationList::AppendCategory() function needs the 300 // IObjectArray interface to retrieve each item in the list. So, we retrive 301 // the IObjectArray interface from the IEnumeratableObjectCollection object 302 // and use it. 303 // It seems the ICustomDestinationList::AppendCategory() function just 304 // replaces all items in the given category with the ones in the new list. 305 base::win::ScopedComPtr<IObjectArray> object_array; 306 result = collection.QueryInterface(object_array.Receive()); 307 if (FAILED(result)) 308 return false; 309 310 return list->AppendCategory(category.c_str(), object_array); 311} 312 313// Updates the "Tasks" category of the JumpList. 314// Even though this function is almost the same as UpdateCategory(), this 315// function has the following differences: 316// * The "Task" category is a registered category. 317// We should use AddUserTasks() instead of AppendCategory(). 318// * The items in the "Task" category are static. 319// We don't have to use a list. 320HRESULT UpdateTaskCategory(base::win::ScopedComPtr<ICustomDestinationList> list, 321 const std::wstring& chrome_path, 322 const std::wstring& chrome_switches) { 323 // Create an EnumerableObjectCollection object to be added items of the 324 // "Task" category. (We can also use this object for the "Task" category.) 325 base::win::ScopedComPtr<IObjectCollection> collection; 326 HRESULT result = collection.CreateInstance(CLSID_EnumerableObjectCollection, 327 NULL, CLSCTX_INPROC_SERVER); 328 if (FAILED(result)) 329 return result; 330 331 // Create an IShellLink object which launches Chrome, and add it to the 332 // collection. We use our application icon as the icon for this item. 333 // We remove '&' characters from this string so we can share it with our 334 // system menu. 335 scoped_refptr<ShellLinkItem> chrome(new ShellLinkItem); 336 std::wstring chrome_title = 337 UTF16ToWide(l10n_util::GetStringUTF16(IDS_NEW_WINDOW)); 338 ReplaceSubstringsAfterOffset(&chrome_title, 0, L"&", L""); 339 chrome->SetTitle(chrome_title); 340 chrome->SetIcon(chrome_path, 0, false); 341 AddShellLink(collection, chrome_path, chrome_switches, chrome); 342 343 // Create an IShellLink object which launches Chrome in incognito mode, and 344 // add it to the collection. We use our application icon as the icon for 345 // this item. 346 scoped_refptr<ShellLinkItem> incognito(new ShellLinkItem); 347 incognito->SetArguments( 348 ASCIIToWide(std::string("--") + switches::kIncognito)); 349 std::wstring incognito_title = 350 UTF16ToWide(l10n_util::GetStringUTF16(IDS_NEW_INCOGNITO_WINDOW)); 351 ReplaceSubstringsAfterOffset(&incognito_title, 0, L"&", L""); 352 incognito->SetTitle(incognito_title); 353 incognito->SetIcon(chrome_path, 0, false); 354 AddShellLink(collection, chrome_path, chrome_switches, incognito); 355 356 // We can now add the new list to the JumpList. 357 // ICustomDestinationList::AddUserTasks() also uses the IObjectArray 358 // interface to retrieve each item in the list. So, we retrieve the 359 // IObjectArray interface from the EnumerableObjectCollection object. 360 base::win::ScopedComPtr<IObjectArray> object_array; 361 result = collection.QueryInterface(object_array.Receive()); 362 if (FAILED(result)) 363 return result; 364 365 return list->AddUserTasks(object_array); 366} 367 368// Updates the application JumpList. 369// This function encapsulates all OS-specific operations required for updating 370// the Chromium JumpList, such as: 371// * Creating an ICustomDestinationList instance; 372// * Updating the categories of the ICustomDestinationList instance, and; 373// * Sending it to Taskbar of Windows 7. 374bool UpdateJumpList(const wchar_t* app_id, 375 const ShellLinkItemList& most_visited_pages, 376 const ShellLinkItemList& recently_closed_pages) { 377 // JumpList is implemented only on Windows 7 or later. 378 // So, we should return now when this function is called on earlier versions 379 // of Windows. 380 if (base::win::GetVersion() < base::win::VERSION_WIN7) 381 return true; 382 383 // Create an ICustomDestinationList object and attach it to our application. 384 base::win::ScopedComPtr<ICustomDestinationList> destination_list; 385 HRESULT result = destination_list.CreateInstance(CLSID_DestinationList, NULL, 386 CLSCTX_INPROC_SERVER); 387 if (FAILED(result)) 388 return false; 389 390 // Set the App ID for this JumpList. 391 destination_list->SetAppID(app_id); 392 393 // Start a transaction that updates the JumpList of this application. 394 // This implementation just replaces the all items in this JumpList, so 395 // we don't have to use the IObjectArray object returned from this call. 396 // It seems Windows 7 RC (Build 7100) automatically checks the items in this 397 // removed list and prevent us from adding the same item. 398 UINT max_slots; 399 base::win::ScopedComPtr<IObjectArray> removed; 400 result = destination_list->BeginList(&max_slots, __uuidof(*removed), 401 reinterpret_cast<void**>(&removed)); 402 if (FAILED(result)) 403 return false; 404 405 // Retrieve the absolute path to "chrome.exe". 406 base::FilePath chrome_path; 407 if (!PathService::Get(base::FILE_EXE, &chrome_path)) 408 return false; 409 410 // Retrieve the command-line switches of this process. 411 CommandLine command_line(CommandLine::NO_PROGRAM); 412 base::FilePath user_data_dir = CommandLine::ForCurrentProcess()-> 413 GetSwitchValuePath(switches::kUserDataDir); 414 if (!user_data_dir.empty()) 415 command_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir); 416 417 std::wstring chrome_switches = command_line.GetCommandLineString(); 418 419 // We allocate 60% of the given JumpList slots to "most-visited" items 420 // and 40% to "recently-closed" items, respectively. 421 // Nevertheless, if there are not so many items in |recently_closed_pages|, 422 // we give the remaining slots to "most-visited" items. 423 const int kMostVisited = 60; 424 const int kRecentlyClosed = 40; 425 const int kTotal = kMostVisited + kRecentlyClosed; 426 size_t most_visited_items = MulDiv(max_slots, kMostVisited, kTotal); 427 size_t recently_closed_items = max_slots - most_visited_items; 428 if (recently_closed_pages.size() < recently_closed_items) { 429 most_visited_items += recently_closed_items - recently_closed_pages.size(); 430 recently_closed_items = recently_closed_pages.size(); 431 } 432 433 // Update the "Most Visited" category of the JumpList. 434 // This update request is applied into the JumpList when we commit this 435 // transaction. 436 result = UpdateCategory(destination_list, IDS_NEW_TAB_MOST_VISITED, 437 chrome_path.value(), chrome_switches, 438 most_visited_pages, most_visited_items); 439 if (FAILED(result)) 440 return false; 441 442 // Update the "Recently Closed" category of the JumpList. 443 result = UpdateCategory(destination_list, IDS_NEW_TAB_RECENTLY_CLOSED, 444 chrome_path.value(), chrome_switches, 445 recently_closed_pages, recently_closed_items); 446 if (FAILED(result)) 447 return false; 448 449 // Update the "Tasks" category of the JumpList. 450 result = UpdateTaskCategory(destination_list, chrome_path.value(), 451 chrome_switches); 452 if (FAILED(result)) 453 return false; 454 455 // Commit this transaction and send the updated JumpList to Windows. 456 result = destination_list->CommitList(); 457 if (FAILED(result)) 458 return false; 459 460 return true; 461} 462 463} // namespace 464 465JumpList::JumpList() 466 : weak_ptr_factory_(this), 467 profile_(NULL), 468 task_id_(CancelableTaskTracker::kBadTaskId) { 469} 470 471JumpList::~JumpList() { 472 Terminate(); 473} 474 475// static 476bool JumpList::Enabled() { 477 return (base::win::GetVersion() >= base::win::VERSION_WIN7 && 478 !CommandLine::ForCurrentProcess()->HasSwitch( 479 switches::kDisableCustomJumpList)); 480} 481 482bool JumpList::AddObserver(Profile* profile) { 483 // To update JumpList when a tab is added or removed, we add this object to 484 // the observer list of the TabRestoreService class. 485 // When we add this object to the observer list, we save the pointer to this 486 // TabRestoreService object. This pointer is used when we remove this object 487 // from the observer list. 488 if (base::win::GetVersion() < base::win::VERSION_WIN7 || !profile) 489 return false; 490 491 TabRestoreService* tab_restore_service = 492 TabRestoreServiceFactory::GetForProfile(profile); 493 if (!tab_restore_service) 494 return false; 495 496 app_id_ = ShellIntegration::GetChromiumModelIdForProfile(profile->GetPath()); 497 icon_dir_ = profile->GetPath().Append(chrome::kJumpListIconDirname); 498 profile_ = profile; 499 history::TopSites* top_sites = profile_->GetTopSites(); 500 if (top_sites) { 501 // TopSites updates itself after a delay. This is especially noticable when 502 // your profile is empty. Ask TopSites to update itself when jumplist is 503 // initialized. 504 top_sites->SyncWithHistory(); 505 registrar_.reset(new content::NotificationRegistrar); 506 // Register for notification when TopSites changes so that we can update 507 // ourself. 508 registrar_->Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED, 509 content::Source<history::TopSites>(top_sites)); 510 // Register for notification when profile is destroyed to ensure that all 511 // observers are detatched at that time. 512 registrar_->Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, 513 content::Source<Profile>(profile_)); 514 } 515 tab_restore_service->AddObserver(this); 516 return true; 517} 518 519void JumpList::Observe(int type, 520 const content::NotificationSource& source, 521 const content::NotificationDetails& details) { 522 switch (type) { 523 case chrome::NOTIFICATION_TOP_SITES_CHANGED: { 524 // Most visited urls changed, query again. 525 history::TopSites* top_sites = profile_->GetTopSites(); 526 if (top_sites) { 527 top_sites->GetMostVisitedURLs( 528 base::Bind(&JumpList::OnMostVisitedURLsAvailable, 529 weak_ptr_factory_.GetWeakPtr())); 530 } 531 break; 532 } 533 case chrome::NOTIFICATION_PROFILE_DESTROYED: { 534 // Profile was destroyed, do clean-up. 535 Terminate(); 536 break; 537 } 538 default: 539 NOTREACHED() << "Unexpected notification type."; 540 } 541} 542 543void JumpList::RemoveObserver() { 544 if (profile_) { 545 TabRestoreService* tab_restore_service = 546 TabRestoreServiceFactory::GetForProfile(profile_); 547 if (tab_restore_service) 548 tab_restore_service->RemoveObserver(this); 549 registrar_.reset(); 550 } 551 profile_ = NULL; 552} 553 554void JumpList::CancelPendingUpdate() { 555 if (task_id_ != CancelableTaskTracker::kBadTaskId) { 556 cancelable_task_tracker_.TryCancel(task_id_); 557 task_id_ = CancelableTaskTracker::kBadTaskId; 558 } 559} 560 561void JumpList::Terminate() { 562 CancelPendingUpdate(); 563 RemoveObserver(); 564} 565 566void JumpList::OnMostVisitedURLsAvailable( 567 const history::MostVisitedURLList& data) { 568 569 // If we have a pending favicon request, cancel it here (it is out of date). 570 CancelPendingUpdate(); 571 572 { 573 base::AutoLock auto_lock(list_lock_); 574 most_visited_pages_.clear(); 575 for (size_t i = 0; i < data.size(); i++) { 576 const history::MostVisitedURL& url = data[i]; 577 scoped_refptr<ShellLinkItem> link(new ShellLinkItem); 578 std::string url_string = url.url.spec(); 579 link->SetArguments(UTF8ToWide(url_string)); 580 link->SetTitle(!url.title.empty()? url.title : link->arguments()); 581 most_visited_pages_.push_back(link); 582 icon_urls_.push_back(make_pair(url_string, link)); 583 } 584 } 585 586 // Send a query that retrieves the first favicon. 587 StartLoadingFavicon(); 588} 589 590void JumpList::TabRestoreServiceChanged(TabRestoreService* service) { 591 // if we have a pending handle request, cancel it here (it is out of date). 592 CancelPendingUpdate(); 593 594 // local list to pass to methods 595 ShellLinkItemList temp_list; 596 597 // Create a list of ShellLinkItems from the "Recently Closed" pages. 598 // As noted above, we create a ShellLinkItem objects with the following 599 // parameters. 600 // * arguments 601 // The last URL of the tab object. 602 // * title 603 // The title of the last URL. 604 // * icon 605 // An empty string. This value is to be updated in OnFaviconDataAvailable(). 606 // This code is copied from 607 // RecentlyClosedTabsHandler::TabRestoreServiceChanged() to emulate it. 608 const int kRecentlyClosedCount = 4; 609 TabRestoreService* tab_restore_service = 610 TabRestoreServiceFactory::GetForProfile(profile_); 611 const TabRestoreService::Entries& entries = tab_restore_service->entries(); 612 for (TabRestoreService::Entries::const_iterator it = entries.begin(); 613 it != entries.end(); ++it) { 614 const TabRestoreService::Entry* entry = *it; 615 if (entry->type == TabRestoreService::TAB) { 616 AddTab(static_cast<const TabRestoreService::Tab*>(entry), 617 &temp_list, kRecentlyClosedCount); 618 } else if (entry->type == TabRestoreService::WINDOW) { 619 AddWindow(static_cast<const TabRestoreService::Window*>(entry), 620 &temp_list, kRecentlyClosedCount); 621 } 622 } 623 // Lock recently_closed_pages and copy temp_list into it. 624 { 625 base::AutoLock auto_lock(list_lock_); 626 recently_closed_pages_ = temp_list; 627 } 628 629 // Send a query that retrieves the first favicon. 630 StartLoadingFavicon(); 631} 632 633void JumpList::TabRestoreServiceDestroyed(TabRestoreService* service) { 634} 635 636bool JumpList::AddTab(const TabRestoreService::Tab* tab, 637 ShellLinkItemList* list, 638 size_t max_items) { 639 // This code adds the URL and the title strings of the given tab to the 640 // specified list. 641 if (list->size() >= max_items) 642 return false; 643 644 scoped_refptr<ShellLinkItem> link(new ShellLinkItem); 645 const sessions::SerializedNavigationEntry& current_navigation = 646 tab->navigations.at(tab->current_navigation_index); 647 std::string url = current_navigation.virtual_url().spec(); 648 link->SetArguments(UTF8ToWide(url)); 649 link->SetTitle(current_navigation.title()); 650 list->push_back(link); 651 icon_urls_.push_back(make_pair(url, link)); 652 return true; 653} 654 655void JumpList::AddWindow(const TabRestoreService::Window* window, 656 ShellLinkItemList* list, 657 size_t max_items) { 658 // This code enumerates al the tabs in the given window object and add their 659 // URLs and titles to the list. 660 DCHECK(!window->tabs.empty()); 661 662 for (size_t i = 0; i < window->tabs.size(); ++i) { 663 if (!AddTab(&window->tabs[i], list, max_items)) 664 return; 665 } 666} 667 668void JumpList::StartLoadingFavicon() { 669 GURL url; 670 { 671 base::AutoLock auto_lock(list_lock_); 672 if (icon_urls_.empty()) { 673 // No more favicons are needed by the application JumpList. Schedule a 674 // RunUpdate call. 675 BrowserThread::PostTask( 676 BrowserThread::FILE, FROM_HERE, 677 base::Bind(&JumpList::RunUpdate, this)); 678 return; 679 } 680 // Ask FaviconService if it has a favicon of a URL. 681 // When FaviconService has one, it will call OnFaviconDataAvailable(). 682 url = GURL(icon_urls_.front().first); 683 } 684 FaviconService* favicon_service = 685 FaviconServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS); 686 task_id_ = favicon_service->GetFaviconImageForURL( 687 FaviconService::FaviconForURLParams(profile_, 688 url, 689 chrome::FAVICON, 690 gfx::kFaviconSize), 691 base::Bind(&JumpList::OnFaviconDataAvailable, 692 base::Unretained(this)), 693 &cancelable_task_tracker_); 694} 695 696void JumpList::OnFaviconDataAvailable( 697 const chrome::FaviconImageResult& image_result) { 698 // If there is currently a favicon request in progress, it is now outdated, 699 // as we have received another, so nullify the handle from the old request. 700 task_id_ = CancelableTaskTracker::kBadTaskId; 701 // lock the list to set icon data and pop the url 702 { 703 base::AutoLock auto_lock(list_lock_); 704 // Attach the received data to the ShellLinkItem object. 705 // This data will be decoded by the RunUpdate method. 706 if (!image_result.image.IsEmpty()) { 707 if (!icon_urls_.empty() && icon_urls_.front().second) 708 icon_urls_.front().second->SetIconData(image_result.image.AsBitmap()); 709 } 710 711 if (!icon_urls_.empty()) 712 icon_urls_.pop_front(); 713 } 714 // Check whether we need to load more favicons. 715 StartLoadingFavicon(); 716} 717 718void JumpList::RunUpdate() { 719 ShellLinkItemList local_most_visited_pages; 720 ShellLinkItemList local_recently_closed_pages; 721 722 { 723 base::AutoLock auto_lock(list_lock_); 724 // Make sure we are not out of date: if icon_urls_ is not empty, then 725 // another notification has been received since we processed this one 726 if (!icon_urls_.empty()) 727 return; 728 729 // Make local copies of lists so we can release the lock. 730 local_most_visited_pages = most_visited_pages_; 731 local_recently_closed_pages = recently_closed_pages_; 732 } 733 734 // Delete the directory which contains old icon files, rename the current 735 // icon directory, and create a new directory which contains new JumpList 736 // icon files. 737 base::FilePath icon_dir_old(icon_dir_.value() + L"Old"); 738 if (file_util::PathExists(icon_dir_old)) 739 file_util::Delete(icon_dir_old, true); 740 file_util::Move(icon_dir_, icon_dir_old); 741 file_util::CreateDirectory(icon_dir_); 742 743 // Create temporary icon files for shortcuts in the "Most Visited" category. 744 CreateIconFiles(local_most_visited_pages); 745 746 // Create temporary icon files for shortcuts in the "Recently Closed" 747 // category. 748 CreateIconFiles(local_recently_closed_pages); 749 750 // We finished collecting all resources needed for updating an appliation 751 // JumpList. So, create a new JumpList and replace the current JumpList 752 // with it. 753 UpdateJumpList(app_id_.c_str(), local_most_visited_pages, 754 local_recently_closed_pages); 755} 756 757void JumpList::CreateIconFiles(const ShellLinkItemList& item_list) { 758 for (ShellLinkItemList::const_iterator item = item_list.begin(); 759 item != item_list.end(); ++item) { 760 base::FilePath icon_path; 761 if (CreateIconFile((*item)->data(), icon_dir_, &icon_path)) 762 (*item)->SetIcon(icon_path.value(), 0, true); 763 } 764} 765