Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2012 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/bind.h"
     16 #include "base/bind_helpers.h"
     17 #include "base/command_line.h"
     18 #include "base/file_util.h"
     19 #include "base/path_service.h"
     20 #include "base/strings/string_util.h"
     21 #include "base/strings/utf_string_conversions.h"
     22 #include "base/threading/thread.h"
     23 #include "base/win/scoped_comptr.h"
     24 #include "base/win/scoped_propvariant.h"
     25 #include "base/win/windows_version.h"
     26 #include "chrome/browser/chrome_notification_types.h"
     27 #include "chrome/browser/favicon/favicon_service.h"
     28 #include "chrome/browser/favicon/favicon_service_factory.h"
     29 #include "chrome/browser/history/history_service.h"
     30 #include "chrome/browser/history/page_usage_data.h"
     31 #include "chrome/browser/history/top_sites.h"
     32 #include "chrome/browser/profiles/profile.h"
     33 #include "chrome/browser/sessions/session_types.h"
     34 #include "chrome/browser/sessions/tab_restore_service.h"
     35 #include "chrome/browser/sessions/tab_restore_service_factory.h"
     36 #include "chrome/browser/shell_integration.h"
     37 #include "chrome/common/chrome_constants.h"
     38 #include "chrome/common/chrome_switches.h"
     39 #include "chrome/common/favicon/favicon_types.h"
     40 #include "chrome/common/url_constants.h"
     41 #include "content/public/browser/browser_thread.h"
     42 #include "content/public/browser/notification_source.h"
     43 #include "grit/chromium_strings.h"
     44 #include "grit/generated_resources.h"
     45 #include "third_party/skia/include/core/SkBitmap.h"
     46 #include "ui/base/l10n/l10n_util.h"
     47 #include "ui/gfx/codec/png_codec.h"
     48 #include "ui/gfx/favicon_size.h"
     49 #include "ui/gfx/icon_util.h"
     50 #include "ui/gfx/image/image_family.h"
     51 #include "url/gurl.h"
     52 
     53 using content::BrowserThread;
     54 
     55 namespace {
     56 
     57 // COM interfaces used in this file.
     58 // These interface declarations are copied from Windows SDK 7.0.
     59 // TODO(hbono): Bug 16903: delete them when we use Windows SDK 7.0.
     60 #ifndef __IObjectArray_INTERFACE_DEFINED__
     61 #define __IObjectArray_INTERFACE_DEFINED__
     62 
     63 MIDL_INTERFACE("92CA9DCD-5622-4bba-A805-5E9F541BD8C9")
     64 IObjectArray : public IUnknown {
     65  public:
     66   virtual HRESULT STDMETHODCALLTYPE GetCount(
     67       /* [out] */ __RPC__out UINT *pcObjects) = 0;
     68   virtual HRESULT STDMETHODCALLTYPE GetAt(
     69       /* [in] */ UINT uiIndex,
     70       /* [in] */ __RPC__in REFIID riid,
     71       /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0;
     72 };
     73 
     74 #endif  // __IObjectArray_INTERFACE_DEFINED__
     75 
     76 #ifndef __IObjectCollection_INTERFACE_DEFINED__
     77 #define __IObjectCollection_INTERFACE_DEFINED__
     78 
     79 MIDL_INTERFACE("5632b1a4-e38a-400a-928a-d4cd63230295")
     80 IObjectCollection : public IObjectArray {
     81  public:
     82   virtual HRESULT STDMETHODCALLTYPE AddObject(
     83       /* [in] */ __RPC__in_opt IUnknown *punk) = 0;
     84   virtual HRESULT STDMETHODCALLTYPE AddFromArray(
     85       /* [in] */ __RPC__in_opt IObjectArray *poaSource) = 0;
     86   virtual HRESULT STDMETHODCALLTYPE RemoveObjectAt(
     87       /* [in] */ UINT uiIndex) = 0;
     88   virtual HRESULT STDMETHODCALLTYPE Clear(void) = 0;
     89 };
     90 
     91 #endif  // __IObjectCollection_INTERFACE_DEFINED__
     92 
     93 #ifndef __ICustomDestinationList_INTERFACE_DEFINED__
     94 #define __ICustomDestinationList_INTERFACE_DEFINED__
     95 
     96 typedef /* [v1_enum] */ enum tagKNOWNDESTCATEGORY {
     97   KDC_FREQUENT = 1,
     98   KDC_RECENT = (KDC_FREQUENT + 1)
     99 } KNOWNDESTCATEGORY;
    100 
    101 MIDL_INTERFACE("6332debf-87b5-4670-90c0-5e57b408a49e")
    102 ICustomDestinationList : public IUnknown {
    103  public:
    104   virtual HRESULT STDMETHODCALLTYPE SetAppID(
    105       /* [string][in] */__RPC__in_string LPCWSTR pszAppID) = 0;
    106   virtual HRESULT STDMETHODCALLTYPE BeginList(
    107       /* [out] */ __RPC__out UINT *pcMaxSlots,
    108       /* [in] */ __RPC__in REFIID riid,
    109       /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0;
    110   virtual HRESULT STDMETHODCALLTYPE AppendCategory(
    111       /* [string][in] */ __RPC__in_string LPCWSTR pszCategory,
    112       /* [in] */ __RPC__in_opt IObjectArray *poa) = 0;
    113   virtual HRESULT STDMETHODCALLTYPE AppendKnownCategory(
    114       /* [in] */ KNOWNDESTCATEGORY category) = 0;
    115   virtual HRESULT STDMETHODCALLTYPE AddUserTasks(
    116       /* [in] */ __RPC__in_opt IObjectArray *poa) = 0;
    117   virtual HRESULT STDMETHODCALLTYPE CommitList(void) = 0;
    118   virtual HRESULT STDMETHODCALLTYPE GetRemovedDestinations(
    119       /* [in] */ __RPC__in REFIID riid,
    120       /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0;
    121   virtual HRESULT STDMETHODCALLTYPE DeleteList(
    122       /* [string][in] */ __RPC__in_string LPCWSTR pszAppID) = 0;
    123   virtual HRESULT STDMETHODCALLTYPE AbortList(void) = 0;
    124 };
    125 
    126 #endif  // __ICustomDestinationList_INTERFACE_DEFINED__
    127 
    128 // Class IDs used in this file.
    129 // These class IDs must be defined in an anonymous namespace to avoid
    130 // conflicts with ones defined in "shell32.lib" of Visual Studio 2008.
    131 // TODO(hbono): Bug 16903: delete them when we use Windows SDK 7.0.
    132 const CLSID CLSID_DestinationList = {
    133   0x77f10cf0, 0x3db5, 0x4966, {0xb5, 0x20, 0xb7, 0xc5, 0x4f, 0xd3, 0x5e, 0xd6}
    134 };
    135 
    136 const CLSID CLSID_EnumerableObjectCollection = {
    137   0x2d3468c1, 0x36a7, 0x43b6, {0xac, 0x24, 0xd3, 0xf0, 0x2f, 0xd9, 0x60, 0x7a}
    138 };
    139 
    140 };  // namespace
    141 
    142 // END OF WINDOWS 7 SDK DEFINITIONS
    143 
    144 namespace {
    145 
    146 // Creates an IShellLink object.
    147 // An IShellLink object is almost the same as an application shortcut, and it
    148 // requires three items: the absolute path to an application, an argument
    149 // string, and a title string.
    150 HRESULT AddShellLink(base::win::ScopedComPtr<IObjectCollection> collection,
    151                      const std::wstring& application,
    152                      const std::wstring& switches,
    153                      scoped_refptr<ShellLinkItem> item) {
    154   // Create an IShellLink object.
    155   base::win::ScopedComPtr<IShellLink> link;
    156   HRESULT result = link.CreateInstance(CLSID_ShellLink, NULL,
    157                                        CLSCTX_INPROC_SERVER);
    158   if (FAILED(result))
    159     return result;
    160 
    161   // Set the application path.
    162   // We should exit this function when this call fails because it doesn't make
    163   // any sense to add a shortcut that we cannot execute.
    164   result = link->SetPath(application.c_str());
    165   if (FAILED(result))
    166     return result;
    167 
    168   // Attach the command-line switches of this process before the given
    169   // arguments and set it as the arguments of this IShellLink object.
    170   // We also exit this function when this call fails because it isn't usuful to
    171   // add a shortcut that cannot open the given page.
    172   std::wstring arguments(switches);
    173   if (!item->arguments().empty()) {
    174     arguments.push_back(L' ');
    175     arguments += item->arguments();
    176   }
    177   result = link->SetArguments(arguments.c_str());
    178   if (FAILED(result))
    179     return result;
    180 
    181   // Attach the given icon path to this IShellLink object.
    182   // Since an icon is an optional item for an IShellLink object, so we don't
    183   // have to exit even when it fails.
    184   if (!item->icon().empty())
    185     link->SetIconLocation(item->icon().c_str(), item->index());
    186 
    187   // Set the title of the IShellLink object.
    188   // The IShellLink interface does not have any functions which update its
    189   // title because this interface is originally for creating an application
    190   // shortcut which doesn't have titles.
    191   // So, we should use the IPropertyStore interface to set its title as
    192   // listed in the steps below:
    193   // 1. Retrieve the IPropertyStore interface from the IShellLink object;
    194   // 2. Start a transaction that changes a value of the object with the
    195   //    IPropertyStore interface;
    196   // 3. Create a string PROPVARIANT, and;
    197   // 4. Call the IPropertyStore::SetValue() function to Set the title property
    198   //    of the IShellLink object.
    199   // 5. Commit the transaction.
    200   base::win::ScopedComPtr<IPropertyStore> property_store;
    201   result = link.QueryInterface(property_store.Receive());
    202   if (FAILED(result))
    203     return result;
    204 
    205   base::win::ScopedPropVariant property_title;
    206   // Call InitPropVariantFromString() to initialize |property_title|. Reading
    207   // <propvarutil.h>, it seems InitPropVariantFromString() is an inline function
    208   // that initializes a PROPVARIANT object and calls SHStrDupW() to set a copy
    209   // of its input string. It is thus safe to call it without first creating a
    210   // copy here.
    211   result = InitPropVariantFromString(item->title().c_str(),
    212                                      property_title.Receive());
    213   if (FAILED(result))
    214     return result;
    215 
    216   result = property_store->SetValue(PKEY_Title, property_title.get());
    217   if (FAILED(result))
    218     return result;
    219 
    220   result = property_store->Commit();
    221   if (FAILED(result))
    222     return result;
    223 
    224   // Add this IShellLink object to the given collection.
    225   return collection->AddObject(link);
    226 }
    227 
    228 // Creates a temporary icon file to be shown in JumpList.
    229 bool CreateIconFile(const SkBitmap& bitmap,
    230                     const base::FilePath& icon_dir,
    231                     base::FilePath* icon_path) {
    232   // Retrieve the path to a temporary file.
    233   // We don't have to care about the extension of this temporary file because
    234   // JumpList does not care about it.
    235   base::FilePath path;
    236   if (!file_util::CreateTemporaryFileInDir(icon_dir, &path))
    237     return false;
    238 
    239   // Create an icon file from the favicon attached to the given |page|, and
    240   // save it as the temporary file.
    241   gfx::ImageFamily image_family;
    242   image_family.Add(gfx::Image::CreateFrom1xBitmap(bitmap));
    243   if (!IconUtil::CreateIconFileFromImageFamily(image_family, path))
    244     return false;
    245 
    246   // Add this icon file to the list and return its absolute path.
    247   // The IShellLink::SetIcon() function needs the absolute path to an icon.
    248   *icon_path = path;
    249   return true;
    250 }
    251 
    252 // Updates a specified category of an application JumpList.
    253 // This function cannot update registered categories (such as "Tasks") because
    254 // special steps are required for updating them.
    255 // So, this function can be used only for adding an unregistered category.
    256 // Parameters:
    257 // * category_id (int)
    258 //   A string ID which contains the category name.
    259 // * application (std::wstring)
    260 //   An application name to be used for creating JumpList items.
    261 //   Even though we can add command-line switches to this parameter, it is
    262 //   better to use the |switches| parameter below.
    263 // * switches (std::wstring)
    264 //   Command-lien switches for the application. This string is to be added
    265 //   before the arguments of each ShellLinkItem object. If there aren't any
    266 //   switches, use an empty string.
    267 // * data (ShellLinkItemList)
    268 //   A list of ShellLinkItem objects to be added under the specified category.
    269 HRESULT UpdateCategory(base::win::ScopedComPtr<ICustomDestinationList> list,
    270                        int category_id,
    271                        const std::wstring& application,
    272                        const std::wstring& switches,
    273                        const ShellLinkItemList& data,
    274                        int max_slots) {
    275   // Exit this function when the given vector does not contain any items
    276   // because an ICustomDestinationList::AppendCategory() call fails in this
    277   // case.
    278   if (data.empty() || !max_slots)
    279     return S_OK;
    280 
    281   std::wstring category = UTF16ToWide(l10n_util::GetStringUTF16(category_id));
    282 
    283   // Create an EnumerableObjectCollection object.
    284   // We once add the given items to this collection object and add this
    285   // collection to the JumpList.
    286   base::win::ScopedComPtr<IObjectCollection> collection;
    287   HRESULT result = collection.CreateInstance(CLSID_EnumerableObjectCollection,
    288                                              NULL, CLSCTX_INPROC_SERVER);
    289   if (FAILED(result))
    290     return false;
    291 
    292   for (ShellLinkItemList::const_iterator item = data.begin();
    293        item != data.end() && max_slots > 0; ++item, --max_slots) {
    294     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.
    320 HRESULT 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.
    374 bool 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 
    465 JumpList::JumpList()
    466     : weak_ptr_factory_(this),
    467       profile_(NULL),
    468       task_id_(CancelableTaskTracker::kBadTaskId) {
    469 }
    470 
    471 JumpList::~JumpList() {
    472   Terminate();
    473 }
    474 
    475 // static
    476 bool JumpList::Enabled() {
    477   return (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
    478           !CommandLine::ForCurrentProcess()->HasSwitch(
    479               switches::kDisableCustomJumpList));
    480 }
    481 
    482 bool 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 
    519 void 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 
    543 void 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 
    554 void JumpList::CancelPendingUpdate() {
    555   if (task_id_ != CancelableTaskTracker::kBadTaskId) {
    556     cancelable_task_tracker_.TryCancel(task_id_);
    557     task_id_ = CancelableTaskTracker::kBadTaskId;
    558   }
    559 }
    560 
    561 void JumpList::Terminate() {
    562   CancelPendingUpdate();
    563   RemoveObserver();
    564 }
    565 
    566 void 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 
    590 void 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 
    633 void JumpList::TabRestoreServiceDestroyed(TabRestoreService* service) {
    634 }
    635 
    636 bool 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 
    655 void 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 
    668 void 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 
    696 void 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 
    718 void 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 (base::PathExists(icon_dir_old))
    739     base::DeleteFile(icon_dir_old, true);
    740   base::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 
    757 void 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