Home | History | Annotate | Download | only in browser
      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
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/jumplist_win.h"
      6 
      7 #include <windows.h>
      8 #include <shobjidl.h>
      9 #include <propkey.h>
     10 #include <propvarutil.h>
     11 
     12 #include <string>
     13 #include <vector>
     14 
     15 #include "base/callback.h"
     16 #include "base/command_line.h"
     17 #include "base/file_util.h"
     18 #include "base/path_service.h"
     19 #include "base/string_util.h"
     20 #include "base/threading/thread.h"
     21 #include "base/utf_string_conversions.h"
     22 #include "base/win/scoped_comptr.h"
     23 #include "base/win/windows_version.h"
     24 #include "chrome/browser/favicon_service.h"
     25 #include "chrome/browser/history/history.h"
     26 #include "chrome/browser/history/page_usage_data.h"
     27 #include "chrome/browser/profiles/profile.h"
     28 #include "chrome/browser/sessions/session_types.h"
     29 #include "chrome/browser/sessions/tab_restore_service.h"
     30 #include "chrome/browser/shell_integration.h"
     31 #include "chrome/common/chrome_constants.h"
     32 #include "chrome/common/chrome_switches.h"
     33 #include "chrome/common/url_constants.h"
     34 #include "content/browser/browser_thread.h"
     35 #include "googleurl/src/gurl.h"
     36 #include "grit/chromium_strings.h"
     37 #include "grit/generated_resources.h"
     38 #include "third_party/skia/include/core/SkBitmap.h"
     39 #include "ui/base/l10n/l10n_util.h"
     40 #include "ui/gfx/codec/png_codec.h"
     41 #include "ui/gfx/icon_util.h"
     42 
     43 
     44 namespace {
     45 
     46 // COM interfaces used in this file.
     47 // These interface declarations are copied from Windows SDK 7.0.
     48 // TODO(hbono): Bug 16903: delete them when we use Windows SDK 7.0.
     49 #ifndef __IObjectArray_INTERFACE_DEFINED__
     50 #define __IObjectArray_INTERFACE_DEFINED__
     51 
     52 MIDL_INTERFACE("92CA9DCD-5622-4bba-A805-5E9F541BD8C9")
     53 IObjectArray : public IUnknown {
     54  public:
     55   virtual HRESULT STDMETHODCALLTYPE GetCount(
     56       /* [out] */ __RPC__out UINT *pcObjects) = 0;
     57   virtual HRESULT STDMETHODCALLTYPE GetAt(
     58       /* [in] */ UINT uiIndex,
     59       /* [in] */ __RPC__in REFIID riid,
     60       /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0;
     61 };
     62 
     63 #endif  // __IObjectArray_INTERFACE_DEFINED__
     64 
     65 #ifndef __IObjectCollection_INTERFACE_DEFINED__
     66 #define __IObjectCollection_INTERFACE_DEFINED__
     67 
     68 MIDL_INTERFACE("5632b1a4-e38a-400a-928a-d4cd63230295")
     69 IObjectCollection : public IObjectArray {
     70  public:
     71   virtual HRESULT STDMETHODCALLTYPE AddObject(
     72       /* [in] */ __RPC__in_opt IUnknown *punk) = 0;
     73   virtual HRESULT STDMETHODCALLTYPE AddFromArray(
     74       /* [in] */ __RPC__in_opt IObjectArray *poaSource) = 0;
     75   virtual HRESULT STDMETHODCALLTYPE RemoveObjectAt(
     76       /* [in] */ UINT uiIndex) = 0;
     77   virtual HRESULT STDMETHODCALLTYPE Clear(void) = 0;
     78 };
     79 
     80 #endif  // __IObjectCollection_INTERFACE_DEFINED__
     81 
     82 #ifndef __ICustomDestinationList_INTERFACE_DEFINED__
     83 #define __ICustomDestinationList_INTERFACE_DEFINED__
     84 
     85 typedef /* [v1_enum] */ enum tagKNOWNDESTCATEGORY {
     86   KDC_FREQUENT = 1,
     87   KDC_RECENT = (KDC_FREQUENT + 1)
     88 } KNOWNDESTCATEGORY;
     89 
     90 MIDL_INTERFACE("6332debf-87b5-4670-90c0-5e57b408a49e")
     91 ICustomDestinationList : public IUnknown {
     92  public:
     93   virtual HRESULT STDMETHODCALLTYPE SetAppID(
     94       /* [string][in] */__RPC__in_string LPCWSTR pszAppID) = 0;
     95   virtual HRESULT STDMETHODCALLTYPE BeginList(
     96       /* [out] */ __RPC__out UINT *pcMaxSlots,
     97       /* [in] */ __RPC__in REFIID riid,
     98       /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0;
     99   virtual HRESULT STDMETHODCALLTYPE AppendCategory(
    100       /* [string][in] */ __RPC__in_string LPCWSTR pszCategory,
    101       /* [in] */ __RPC__in_opt IObjectArray *poa) = 0;
    102   virtual HRESULT STDMETHODCALLTYPE AppendKnownCategory(
    103       /* [in] */ KNOWNDESTCATEGORY category) = 0;
    104   virtual HRESULT STDMETHODCALLTYPE AddUserTasks(
    105       /* [in] */ __RPC__in_opt IObjectArray *poa) = 0;
    106   virtual HRESULT STDMETHODCALLTYPE CommitList(void) = 0;
    107   virtual HRESULT STDMETHODCALLTYPE GetRemovedDestinations(
    108       /* [in] */ __RPC__in REFIID riid,
    109       /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0;
    110   virtual HRESULT STDMETHODCALLTYPE DeleteList(
    111       /* [string][in] */ __RPC__in_string LPCWSTR pszAppID) = 0;
    112   virtual HRESULT STDMETHODCALLTYPE AbortList(void) = 0;
    113 };
    114 
    115 #endif  // __ICustomDestinationList_INTERFACE_DEFINED__
    116 
    117 // Class IDs used in this file.
    118 // These class IDs must be defined in an anonymous namespace to avoid
    119 // conflicts with ones defined in "shell32.lib" of Visual Studio 2008.
    120 // TODO(hbono): Bug 16903: delete them when we use Windows SDK 7.0.
    121 const CLSID CLSID_DestinationList = {
    122   0x77f10cf0, 0x3db5, 0x4966, {0xb5, 0x20, 0xb7, 0xc5, 0x4f, 0xd3, 0x5e, 0xd6}
    123 };
    124 
    125 const CLSID CLSID_EnumerableObjectCollection = {
    126   0x2d3468c1, 0x36a7, 0x43b6, {0xac, 0x24, 0xd3, 0xf0, 0x2f, 0xd9, 0x60, 0x7a}
    127 };
    128 
    129 };  // namespace
    130 
    131 // END OF WINDOWS 7 SDK DEFINITIONS
    132 
    133 namespace {
    134 
    135 // Represents a class which encapsulates a PROPVARIANT object containing a
    136 // string for AddShellLink().
    137 // This class automatically deletes all the resources attached to the
    138 // PROPVARIANT object in its destructor.
    139 class PropVariantString {
    140  public:
    141   PropVariantString() {
    142     property_.vt = VT_EMPTY;
    143   }
    144 
    145   HRESULT Init(const std::wstring& value) {
    146     // Call InitPropVariantFromString() to initialize this PROPVARIANT object.
    147     // To read <propvarutil.h>, it seems InitPropVariantFromString() is an
    148     // inline function that initialize a PROPVARIANT object and calls
    149     // SHStrDupW() to set a copy of its input string.
    150     // So, we just calls it without creating a copy.
    151     return InitPropVariantFromString(value.c_str(), &property_);
    152   }
    153 
    154   ~PropVariantString() {
    155     if (property_.vt != VT_EMPTY)
    156       PropVariantClear(&property_);
    157   }
    158 
    159   const PROPVARIANT& Get() {
    160     return property_;
    161   }
    162 
    163  private:
    164   PROPVARIANT property_;
    165 
    166   DISALLOW_COPY_AND_ASSIGN(PropVariantString);
    167 };
    168 
    169 // Creates an IShellLink object.
    170 // An IShellLink object is almost the same as an application shortcut, and it
    171 // requires three items: the absolute path to an application, an argument
    172 // string, and a title string.
    173 HRESULT AddShellLink(base::win::ScopedComPtr<IObjectCollection> collection,
    174                      const std::wstring& application,
    175                      const std::wstring& switches,
    176                      scoped_refptr<ShellLinkItem> item) {
    177   // Create an IShellLink object.
    178   base::win::ScopedComPtr<IShellLink> link;
    179   HRESULT result = link.CreateInstance(CLSID_ShellLink, NULL,
    180                                        CLSCTX_INPROC_SERVER);
    181   if (FAILED(result))
    182     return result;
    183 
    184   // Set the application path.
    185   // We should exit this function when this call fails because it doesn't make
    186   // any sense to add a shortcut that we cannot execute.
    187   result = link->SetPath(application.c_str());
    188   if (FAILED(result))
    189     return result;
    190 
    191   // Attach the command-line switches of this process before the given
    192   // arguments and set it as the arguments of this IShellLink object.
    193   // We also exit this function when this call fails because it isn't usuful to
    194   // add a shortcut that cannot open the given page.
    195   std::wstring arguments(switches);
    196   if (!item->arguments().empty()) {
    197     arguments.push_back(L' ');
    198     arguments += item->arguments();
    199   }
    200   result = link->SetArguments(arguments.c_str());
    201   if (FAILED(result))
    202     return result;
    203 
    204   // Attach the given icon path to this IShellLink object.
    205   // Since an icon is an optional item for an IShellLink object, so we don't
    206   // have to exit even when it fails.
    207   if (!item->icon().empty())
    208     link->SetIconLocation(item->icon().c_str(), item->index());
    209 
    210   // Set the title of the IShellLink object.
    211   // The IShellLink interface does not have any functions which update its
    212   // title because this interface is originally for creating an application
    213   // shortcut which doesn't have titles.
    214   // So, we should use the IPropertyStore interface to set its title as
    215   // listed in the steps below:
    216   // 1. Retrieve the IPropertyStore interface from the IShellLink object;
    217   // 2. Start a transaction that changes a value of the object with the
    218   //    IPropertyStore interface;
    219   // 3. Create a string PROPVARIANT, and;
    220   // 4. Call the IPropertyStore::SetValue() function to Set the title property
    221   //    of the IShellLink object.
    222   // 5. Commit the transaction.
    223   base::win::ScopedComPtr<IPropertyStore> property_store;
    224   result = link.QueryInterface(property_store.Receive());
    225   if (FAILED(result))
    226     return result;
    227 
    228   PropVariantString property_title;
    229   result = property_title.Init(item->title());
    230   if (FAILED(result))
    231     return result;
    232 
    233   result = property_store->SetValue(PKEY_Title, property_title.Get());
    234   if (FAILED(result))
    235     return result;
    236 
    237   result = property_store->Commit();
    238   if (FAILED(result))
    239     return result;
    240 
    241   // Add this IShellLink object to the given collection.
    242   return collection->AddObject(link);
    243 }
    244 
    245 // Creates a temporary icon file to be shown in JumpList.
    246 bool CreateIconFile(const SkBitmap& bitmap,
    247                     const FilePath& icon_dir,
    248                     FilePath* icon_path) {
    249   // Retrieve the path to a temporary file.
    250   // We don't have to care about the extension of this temporary file because
    251   // JumpList does not care about it.
    252   FilePath path;
    253   if (!file_util::CreateTemporaryFileInDir(icon_dir, &path))
    254     return false;
    255 
    256   // Create an icon file from the favicon attached to the given |page|, and
    257   // save it as the temporary file.
    258   if (!IconUtil::CreateIconFileFromSkBitmap(bitmap, path))
    259     return false;
    260 
    261   // Add this icon file to the list and return its absolute path.
    262   // The IShellLink::SetIcon() function needs the absolute path to an icon.
    263   *icon_path = path;
    264   return true;
    265 }
    266 
    267 // Updates a specified category of an application JumpList.
    268 // This function cannot update registered categories (such as "Tasks") because
    269 // special steps are required for updating them.
    270 // So, this function can be used only for adding an unregistered category.
    271 // Parameters:
    272 // * category_id (int)
    273 //   A string ID which contains the category name.
    274 // * application (std::wstring)
    275 //   An application name to be used for creating JumpList items.
    276 //   Even though we can add command-line switches to this parameter, it is
    277 //   better to use the |switches| parameter below.
    278 // * switches (std::wstring)
    279 //   Command-lien switches for the application. This string is to be added
    280 //   before the arguments of each ShellLinkItem object. If there aren't any
    281 //   switches, use an empty string.
    282 // * data (ShellLinkItemList)
    283 //   A list of ShellLinkItem objects to be added under the specified category.
    284 HRESULT UpdateCategory(base::win::ScopedComPtr<ICustomDestinationList> list,
    285                        int category_id,
    286                        const std::wstring& application,
    287                        const std::wstring& switches,
    288                        const ShellLinkItemList& data,
    289                        int max_slots) {
    290   // Exit this function when the given vector does not contain any items
    291   // because an ICustomDestinationList::AppendCategory() call fails in this
    292   // case.
    293   if (data.empty() || !max_slots)
    294     return S_OK;
    295 
    296   std::wstring category = UTF16ToWide(l10n_util::GetStringUTF16(category_id));
    297 
    298   // Create an EnumerableObjectCollection object.
    299   // We once add the given items to this collection object and add this
    300   // collection to the JumpList.
    301   base::win::ScopedComPtr<IObjectCollection> collection;
    302   HRESULT result = collection.CreateInstance(CLSID_EnumerableObjectCollection,
    303                                              NULL, CLSCTX_INPROC_SERVER);
    304   if (FAILED(result))
    305     return false;
    306 
    307   for (ShellLinkItemList::const_iterator item = data.begin();
    308        item != data.end() && max_slots > 0; ++item, --max_slots) {
    309     scoped_refptr<ShellLinkItem> link(*item);
    310     AddShellLink(collection, application, switches, link);
    311   }
    312 
    313   // We can now add the new list to the JumpList.
    314   // The ICustomDestinationList::AppendCategory() function needs the
    315   // IObjectArray interface to retrieve each item in the list. So, we retrive
    316   // the IObjectArray interface from the IEnumeratableObjectCollection object
    317   // and use it.
    318   // It seems the ICustomDestinationList::AppendCategory() function just
    319   // replaces all items in the given category with the ones in the new list.
    320   base::win::ScopedComPtr<IObjectArray> object_array;
    321   result = collection.QueryInterface(object_array.Receive());
    322   if (FAILED(result))
    323     return false;
    324 
    325   return list->AppendCategory(category.c_str(), object_array);
    326 }
    327 
    328 // Updates the "Tasks" category of the JumpList.
    329 // Even though this function is almost the same as UpdateCategory(), this
    330 // function has the following differences:
    331 // * The "Task" category is a registered category.
    332 //   We should use AddUserTasks() instead of AppendCategory().
    333 // * The items in the "Task" category are static.
    334 //   We don't have to use a list.
    335 HRESULT UpdateTaskCategory(base::win::ScopedComPtr<ICustomDestinationList> list,
    336                            const std::wstring& chrome_path,
    337                            const std::wstring& chrome_switches) {
    338   // Create an EnumerableObjectCollection object to be added items of the
    339   // "Task" category. (We can also use this object for the "Task" category.)
    340   base::win::ScopedComPtr<IObjectCollection> collection;
    341   HRESULT result = collection.CreateInstance(CLSID_EnumerableObjectCollection,
    342                                              NULL, CLSCTX_INPROC_SERVER);
    343   if (FAILED(result))
    344     return result;
    345 
    346   // Create an IShellLink object which launches Chrome, and add it to the
    347   // collection. We use our application icon as the icon for this item.
    348   // We remove '&' characters from this string so we can share it with our
    349   // system menu.
    350   scoped_refptr<ShellLinkItem> chrome(new ShellLinkItem);
    351   std::wstring chrome_title =
    352       UTF16ToWide(l10n_util::GetStringUTF16(IDS_NEW_WINDOW));
    353   ReplaceSubstringsAfterOffset(&chrome_title, 0, L"&", L"");
    354   chrome->SetTitle(chrome_title);
    355   chrome->SetIcon(chrome_path, 0, false);
    356   AddShellLink(collection, chrome_path, chrome_switches, chrome);
    357 
    358   // Create an IShellLink object which launches Chrome in incognito mode, and
    359   // add it to the collection. We use our application icon as the icon for
    360   // this item.
    361   scoped_refptr<ShellLinkItem> incognito(new ShellLinkItem);
    362   incognito->SetArguments(
    363       ASCIIToWide(std::string("--") + switches::kIncognito));
    364   std::wstring incognito_title =
    365       UTF16ToWide(l10n_util::GetStringUTF16(IDS_NEW_INCOGNITO_WINDOW));
    366   ReplaceSubstringsAfterOffset(&incognito_title, 0, L"&", L"");
    367   incognito->SetTitle(incognito_title);
    368   incognito->SetIcon(chrome_path, 0, false);
    369   AddShellLink(collection, chrome_path, chrome_switches, incognito);
    370 
    371   // We can now add the new list to the JumpList.
    372   // ICustomDestinationList::AddUserTasks() also uses the IObjectArray
    373   // interface to retrieve each item in the list. So, we retrieve the
    374   // IObjectArray interface from the EnumerableObjectCollection object.
    375   base::win::ScopedComPtr<IObjectArray> object_array;
    376   result = collection.QueryInterface(object_array.Receive());
    377   if (FAILED(result))
    378     return result;
    379 
    380   return list->AddUserTasks(object_array);
    381 }
    382 
    383 // Updates the application JumpList.
    384 // This function encapsulates all OS-specific operations required for updating
    385 // the Chromium JumpList, such as:
    386 // * Creating an ICustomDestinationList instance;
    387 // * Updating the categories of the ICustomDestinationList instance, and;
    388 // * Sending it to Taskbar of Windows 7.
    389 bool UpdateJumpList(const wchar_t* app_id,
    390                     const ShellLinkItemList& most_visited_pages,
    391                     const ShellLinkItemList& recently_closed_pages) {
    392   // JumpList is implemented only on Windows 7 or later.
    393   // So, we should return now when this function is called on earlier versions
    394   // of Windows.
    395   if (base::win::GetVersion() < base::win::VERSION_WIN7)
    396     return true;
    397 
    398   // Create an ICustomDestinationList object and attach it to our application.
    399   base::win::ScopedComPtr<ICustomDestinationList> destination_list;
    400   HRESULT result = destination_list.CreateInstance(CLSID_DestinationList, NULL,
    401                                                    CLSCTX_INPROC_SERVER);
    402   if (FAILED(result))
    403     return false;
    404 
    405   // Set the App ID for this JumpList.
    406   destination_list->SetAppID(app_id);
    407 
    408   // Start a transaction that updates the JumpList of this application.
    409   // This implementation just replaces the all items in this JumpList, so
    410   // we don't have to use the IObjectArray object returned from this call.
    411   // It seems Windows 7 RC (Build 7100) automatically checks the items in this
    412   // removed list and prevent us from adding the same item.
    413   UINT max_slots;
    414   base::win::ScopedComPtr<IObjectArray> removed;
    415   result = destination_list->BeginList(&max_slots, __uuidof(*removed),
    416                                        reinterpret_cast<void**>(&removed));
    417   if (FAILED(result))
    418     return false;
    419 
    420   // Retrieve the absolute path to "chrome.exe".
    421   FilePath chrome_path;
    422   if (!PathService::Get(base::FILE_EXE, &chrome_path))
    423     return false;
    424 
    425   // Retrieve the command-line switches of this process.
    426   CommandLine command_line(CommandLine::NO_PROGRAM);
    427   FilePath user_data_dir = CommandLine::ForCurrentProcess()->
    428       GetSwitchValuePath(switches::kUserDataDir);
    429   if (!user_data_dir.empty())
    430     command_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir);
    431 
    432   std::wstring chrome_switches = command_line.command_line_string();
    433 
    434   // We allocate 60% of the given JumpList slots to "most-visited" items
    435   // and 40% to "recently-closed" items, respectively.
    436   // Nevertheless, if there are not so many items in |recently_closed_pages|,
    437   // we give the remaining slots to "most-visited" items.
    438   const int kMostVisited = 60;
    439   const int kRecentlyClosed = 40;
    440   const int kTotal = kMostVisited + kRecentlyClosed;
    441   size_t most_visited_items = MulDiv(max_slots, kMostVisited, kTotal);
    442   size_t recently_closed_items = max_slots - most_visited_items;
    443   if (recently_closed_pages.size() < recently_closed_items) {
    444     most_visited_items += recently_closed_items - recently_closed_pages.size();
    445     recently_closed_items = recently_closed_pages.size();
    446   }
    447 
    448   // Update the "Most Visited" category of the JumpList.
    449   // This update request is applied into the JumpList when we commit this
    450   // transaction.
    451   result = UpdateCategory(destination_list, IDS_NEW_TAB_MOST_VISITED,
    452                           chrome_path.value(), chrome_switches,
    453                           most_visited_pages, most_visited_items);
    454   if (FAILED(result))
    455     return false;
    456 
    457   // Update the "Recently Closed" category of the JumpList.
    458   result = UpdateCategory(destination_list, IDS_NEW_TAB_RECENTLY_CLOSED,
    459                           chrome_path.value(), chrome_switches,
    460                           recently_closed_pages, recently_closed_items);
    461   if (FAILED(result))
    462     return false;
    463 
    464   // Update the "Tasks" category of the JumpList.
    465   result = UpdateTaskCategory(destination_list, chrome_path.value(),
    466                               chrome_switches);
    467   if (FAILED(result))
    468     return false;
    469 
    470   // Commit this transaction and send the updated JumpList to Windows.
    471   result = destination_list->CommitList();
    472   if (FAILED(result))
    473     return false;
    474 
    475   return true;
    476 }
    477 
    478 // Represents a task which updates an application JumpList.
    479 // This task encapsulates all I/O tasks and OS-specific tasks required for
    480 // updating a JumpList from Chromium, such as:
    481 // * Deleting the directory containing temporary icon files;
    482 // * Creating temporary icon files used by the JumpList;
    483 // * Creating an ICustomDestinationList instance;
    484 // * Adding items in the ICustomDestinationList instance.
    485 // To spawn this task,
    486 // 1. Prepare objects required by this task:
    487 //    * a std::wstring that contains a temporary icons;
    488 //    * a ShellLinkItemList that contains the items of the "Most Visited"
    489 //      category, and;
    490 //    * a ShellLinkItemList that contains the items of the "Recently Closed"
    491 //      category.
    492 // 2. Create a JumpListUpdateTask instance, and;
    493 // 3. Post this task to the file thread.
    494 class JumpListUpdateTask : public Task {
    495  public:
    496   JumpListUpdateTask(const wchar_t* app_id,
    497                      const FilePath& icon_dir,
    498                      const ShellLinkItemList& most_visited_pages,
    499                      const ShellLinkItemList& recently_closed_pages)
    500     : app_id_(app_id),
    501       icon_dir_(icon_dir),
    502       most_visited_pages_(most_visited_pages),
    503       recently_closed_pages_(recently_closed_pages) {
    504   }
    505 
    506  private:
    507   // Represents an entry point of this task.
    508   // When we post this task to a file thread, the thread calls this function.
    509   void Run();
    510 
    511   // App id to associate with the jump list.
    512   std::wstring app_id_;
    513 
    514   // The directory which contains JumpList icons.
    515   FilePath icon_dir_;
    516 
    517   // Items in the "Most Visited" category of the application JumpList.
    518   ShellLinkItemList most_visited_pages_;
    519 
    520   // Items in the "Recently Closed" category of the application JumpList.
    521   ShellLinkItemList recently_closed_pages_;
    522 };
    523 
    524 void JumpListUpdateTask::Run() {
    525   // Delete the directory which contains old icon files, rename the current
    526   // icon directory, and create a new directory which contains new JumpList
    527   // icon files.
    528   FilePath icon_dir_old(icon_dir_.value() + L"Old");
    529   if (file_util::PathExists(icon_dir_old))
    530     file_util::Delete(icon_dir_old, true);
    531   file_util::Move(icon_dir_, icon_dir_old);
    532   file_util::CreateDirectory(icon_dir_);
    533 
    534   // Create temporary icon files for shortcuts in the "Most Visited" category.
    535   for (ShellLinkItemList::const_iterator item = most_visited_pages_.begin();
    536        item != most_visited_pages_.end(); ++item) {
    537     SkBitmap icon_bitmap;
    538     if ((*item)->data().get() &&
    539         gfx::PNGCodec::Decode((*item)->data()->front(),
    540                               (*item)->data()->size(),
    541                               &icon_bitmap)) {
    542       FilePath icon_path;
    543       if (CreateIconFile(icon_bitmap, icon_dir_, &icon_path))
    544         (*item)->SetIcon(icon_path.value(), 0, true);
    545     }
    546   }
    547 
    548   // Create temporary icon files for shortcuts in the "Recently Closed"
    549   // category.
    550   for (ShellLinkItemList::const_iterator item = recently_closed_pages_.begin();
    551        item != recently_closed_pages_.end(); ++item) {
    552     SkBitmap icon_bitmap;
    553     if ((*item)->data().get() &&
    554         gfx::PNGCodec::Decode((*item)->data()->front(),
    555                               (*item)->data()->size(),
    556                               &icon_bitmap)) {
    557       FilePath icon_path;
    558       if (CreateIconFile(icon_bitmap, icon_dir_, &icon_path))
    559         (*item)->SetIcon(icon_path.value(), 0, true);
    560     }
    561   }
    562 
    563   // We finished collecting all resources needed for updating an appliation
    564   // JumpList. So, create a new JumpList and replace the current JumpList
    565   // with it.
    566   UpdateJumpList(app_id_.c_str(), most_visited_pages_, recently_closed_pages_);
    567 
    568   // Delete all items in these lists now since we don't need the ShellLinkItem
    569   // objects in these lists.
    570   most_visited_pages_.clear();
    571   recently_closed_pages_.clear();
    572 }
    573 
    574 }  // namespace
    575 
    576 JumpList::JumpList() : profile_(NULL) {
    577 }
    578 
    579 JumpList::~JumpList() {
    580   RemoveObserver();
    581 }
    582 
    583 // static
    584 bool JumpList::Enabled() {
    585   return (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
    586           !CommandLine::ForCurrentProcess()->HasSwitch(
    587               switches::kDisableCustomJumpList));
    588 }
    589 
    590 bool JumpList::AddObserver(Profile* profile) {
    591   // To update JumpList when a tab is added or removed, we add this object to
    592   // the observer list of the TabRestoreService class.
    593   // When we add this object to the observer list, we save the pointer to this
    594   // TabRestoreService object. This pointer is used when we remove this object
    595   // from the observer list.
    596   if (base::win::GetVersion() < base::win::VERSION_WIN7 || !profile)
    597     return false;
    598 
    599   TabRestoreService* tab_restore_service = profile->GetTabRestoreService();
    600   if (!tab_restore_service)
    601     return false;
    602 
    603   app_id_ = ShellIntegration::GetChromiumAppId(profile->GetPath());
    604   icon_dir_ = profile->GetPath().Append(chrome::kJumpListIconDirname);
    605   profile_ = profile;
    606   tab_restore_service->AddObserver(this);
    607   return true;
    608 }
    609 
    610 void JumpList::RemoveObserver() {
    611   if (profile_ && profile_->GetTabRestoreService())
    612     profile_->GetTabRestoreService()->RemoveObserver(this);
    613   profile_ = NULL;
    614 }
    615 
    616 void JumpList::TabRestoreServiceChanged(TabRestoreService* service) {
    617   // Added or removed a tab.
    618   // Exit if we are updating the application JumpList.
    619   if (!icon_urls_.empty())
    620     return;
    621 
    622   // Send a query to HistoryService and retrieve the "Most Visited" pages.
    623   // This code is copied from MostVisitedHandler::HandleGetMostVisited() to
    624   // emulate its behaviors.
    625   const int kMostVisitedScope = 90;
    626   const int kMostVisitedCount = 9;
    627   int result_count = kMostVisitedCount;
    628   HistoryService* history_service =
    629       profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
    630   history_service->QuerySegmentUsageSince(
    631       &most_visited_consumer_,
    632       base::Time::Now() - base::TimeDelta::FromDays(kMostVisitedScope),
    633       result_count,
    634       NewCallback(this, &JumpList::OnSegmentUsageAvailable));
    635 }
    636 
    637 void JumpList::TabRestoreServiceDestroyed(TabRestoreService* service) {
    638 }
    639 
    640 bool JumpList::AddTab(const TabRestoreService::Tab* tab,
    641                       ShellLinkItemList* list,
    642                       size_t max_items) {
    643   // This code adds the URL and the title strings of the given tab to the
    644   // specified list.
    645   // This code is copied from RecentlyClosedTabsHandler::TabToValue().
    646   if (tab->navigations.empty() || list->size() >= max_items)
    647     return false;
    648 
    649   const TabNavigation& current_navigation =
    650       tab->navigations.at(tab->current_navigation_index);
    651   if (current_navigation.virtual_url() == GURL(chrome::kChromeUINewTabURL))
    652     return false;
    653 
    654   scoped_refptr<ShellLinkItem> link(new ShellLinkItem);
    655   std::string url = current_navigation.virtual_url().spec();
    656   link->SetArguments(UTF8ToWide(url));
    657   link->SetTitle(current_navigation.title());
    658   list->push_back(link);
    659   icon_urls_.push_back(make_pair(url, link));
    660   return true;
    661 }
    662 
    663 bool JumpList::AddWindow(const TabRestoreService::Window* window,
    664                          ShellLinkItemList* list,
    665                          size_t max_items) {
    666   // This code enumerates al the tabs in the given window object and add their
    667   // URLs and titles to the list.
    668   // This code is copied from RecentlyClosedTabsHandler::WindowToValue().
    669   if (window->tabs.empty()) {
    670     NOTREACHED();
    671     return false;
    672   }
    673   for (size_t i = 0; i < window->tabs.size(); ++i) {
    674     if (!AddTab(&window->tabs[i], list, max_items))
    675       return false;
    676   }
    677 
    678   return true;
    679 }
    680 
    681 bool JumpList::StartLoadingFavicon() {
    682   if (icon_urls_.empty())
    683     return false;
    684 
    685   // Ask FaviconService if it has a favicon of a URL.
    686   // When FaviconService has one, it will call OnFaviconDataAvailable().
    687   GURL url(icon_urls_.front().first);
    688   FaviconService* favicon_service =
    689       profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
    690   FaviconService::Handle handle = favicon_service->GetFaviconForURL(
    691       url, history::FAVICON, &favicon_consumer_,
    692       NewCallback(this, &JumpList::OnFaviconDataAvailable));
    693   return true;
    694 }
    695 
    696 void JumpList::OnSegmentUsageAvailable(
    697     CancelableRequestProvider::Handle handle,
    698     std::vector<PageUsageData*>* data) {
    699   // Create a list of ShellLinkItem objects from the given list of
    700   // PageUsageData objects.
    701   // The command that opens a web page with chrome is:
    702   //   "chrome.exe <url-to-the-web-page>".
    703   // So, we create a ShellLinkItem object with the following parameters.
    704   // * arguments
    705   //   The URL of a PageUsagedata object (converted to std::wstring).
    706   // * title
    707   //   The title of a PageUsageData object. If this string is empty, we use
    708   //   the URL as our "Most Visited" page does.
    709   // * icon
    710   //   An empty string. This value is to be updated in OnFaviconDataAvailable().
    711   most_visited_pages_.clear();
    712   for (std::vector<PageUsageData*>::const_iterator page = data->begin();
    713        page != data->end(); ++page) {
    714     scoped_refptr<ShellLinkItem> link(new ShellLinkItem);
    715     std::string url = (*page)->GetURL().spec();
    716     link->SetArguments(UTF8ToWide(url));
    717     link->SetTitle(
    718         !(*page)->GetTitle().empty() ? (*page)->GetTitle() : link->arguments());
    719     most_visited_pages_.push_back(link);
    720     icon_urls_.push_back(make_pair(url, link));
    721   }
    722 
    723   // Create a list of ShellLinkItems from the "Recently Closed" pages.
    724   // As noted above, we create a ShellLinkItem objects with the following
    725   // parameters.
    726   // * arguments
    727   //   The last URL of the tab object.
    728   // * title
    729   //   The title of the last URL.
    730   // * icon
    731   //   An empty string. This value is to be updated in OnFaviconDataAvailable().
    732   // This code is copied from
    733   // RecentlyClosedTabsHandler::TabRestoreServiceChanged() to emulate it.
    734   const int kRecentlyClosedCount = 4;
    735   recently_closed_pages_.clear();
    736   TabRestoreService* tab_restore_service = profile_->GetTabRestoreService();
    737   const TabRestoreService::Entries& entries = tab_restore_service->entries();
    738   for (TabRestoreService::Entries::const_iterator it = entries.begin();
    739        it != entries.end(); ++it) {
    740     const TabRestoreService::Entry* entry = *it;
    741     if (entry->type == TabRestoreService::TAB) {
    742       AddTab(static_cast<const TabRestoreService::Tab*>(entry),
    743              &recently_closed_pages_, kRecentlyClosedCount);
    744     } else if (entry->type == TabRestoreService::WINDOW) {
    745       AddWindow(static_cast<const TabRestoreService::Window*>(entry),
    746                 &recently_closed_pages_, kRecentlyClosedCount);
    747     }
    748   }
    749 
    750   // Send a query that retrieves the first favicon.
    751   StartLoadingFavicon();
    752 }
    753 
    754 void JumpList::OnFaviconDataAvailable(
    755     FaviconService::Handle handle,
    756     history::FaviconData favicon) {
    757   // Attach the received data to the ShellLinkItem object.
    758   // This data will be decoded by JumpListUpdateTask.
    759   if (favicon.is_valid()) {
    760     if (!icon_urls_.empty() && icon_urls_.front().second)
    761       icon_urls_.front().second->SetIconData(favicon.image_data);
    762   }
    763 
    764   // if we need to load more favicons, we send another query and exit.
    765   if (!icon_urls_.empty())
    766     icon_urls_.pop_front();
    767   if (StartLoadingFavicon())
    768     return;
    769 
    770   // Finished loading all favicons needed by the application JumpList.
    771   // We create a JumpListUpdateTask that creates icon files, and we post it to
    772   // the file thread.
    773   BrowserThread::PostTask(
    774       BrowserThread::FILE, FROM_HERE,
    775       new JumpListUpdateTask(app_id_.c_str(), icon_dir_, most_visited_pages_,
    776                              recently_closed_pages_));
    777 
    778   // Delete all items in these lists since we don't need these lists any longer.
    779   most_visited_pages_.clear();
    780   recently_closed_pages_.clear();
    781 }
    782