| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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_updater_win.h" | |
| 6 | |
| 7 #include <windows.h> | |
| 8 #include <propkey.h> | |
| 9 #include <shobjidl.h> | |
| 10 | |
| 11 #include "base/command_line.h" | |
| 12 #include "base/files/file_path.h" | |
| 13 #include "base/path_service.h" | |
| 14 #include "base/win/win_util.h" | |
| 15 #include "base/win/windows_version.h" | |
| 16 #include "chrome/common/chrome_switches.h" | |
| 17 #include "content/public/common/content_switches.h" | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 // Creates an IShellLink object. | |
| 22 // An IShellLink object is almost the same as an application shortcut, and it | |
| 23 // requires three items: the absolute path to an application, an argument | |
| 24 // string, and a title string. | |
| 25 bool AddShellLink(base::win::ScopedComPtr<IObjectCollection> collection, | |
| 26 const std::wstring& application_path, | |
| 27 scoped_refptr<ShellLinkItem> item) { | |
| 28 // Create an IShellLink object. | |
| 29 base::win::ScopedComPtr<IShellLink> link; | |
| 30 HRESULT result = link.CreateInstance(CLSID_ShellLink, NULL, | |
| 31 CLSCTX_INPROC_SERVER); | |
| 32 if (FAILED(result)) | |
| 33 return false; | |
| 34 | |
| 35 // Set the application path. | |
| 36 // We should exit this function when this call fails because it doesn't make | |
| 37 // any sense to add a shortcut that we cannot execute. | |
| 38 result = link->SetPath(application_path.c_str()); | |
| 39 if (FAILED(result)) | |
| 40 return false; | |
| 41 | |
| 42 // Attach the command-line switches of this process before the given | |
| 43 // arguments and set it as the arguments of this IShellLink object. | |
| 44 // We also exit this function when this call fails because it isn't useful to | |
| 45 // add a shortcut that cannot open the given page. | |
| 46 std::wstring arguments(item->GetArguments()); | |
| 47 if (!arguments.empty()) { | |
| 48 result = link->SetArguments(arguments.c_str()); | |
| 49 if (FAILED(result)) | |
| 50 return false; | |
| 51 } | |
| 52 | |
| 53 // Attach the given icon path to this IShellLink object. | |
| 54 // Since an icon is an optional item for an IShellLink object, so we don't | |
| 55 // have to exit even when it fails. | |
| 56 if (!item->icon_path().empty()) | |
| 57 link->SetIconLocation(item->icon_path().c_str(), item->icon_index()); | |
| 58 | |
| 59 // Set the title of the IShellLink object. | |
| 60 // The IShellLink interface does not have any functions which update its | |
| 61 // title because this interface is originally for creating an application | |
| 62 // shortcut which doesn't have titles. | |
| 63 // So, we should use the IPropertyStore interface to set its title. | |
| 64 base::win::ScopedComPtr<IPropertyStore> property_store; | |
| 65 result = link.QueryInterface(property_store.Receive()); | |
| 66 if (FAILED(result)) | |
| 67 return false; | |
| 68 | |
| 69 if (!base::win::SetStringValueForPropertyStore( | |
| 70 property_store.get(), | |
| 71 PKEY_Title, | |
| 72 item->title().c_str())) { | |
| 73 return false; | |
| 74 } | |
| 75 | |
| 76 // Add this IShellLink object to the given collection. | |
| 77 return SUCCEEDED(collection->AddObject(link.get())); | |
| 78 } | |
| 79 | |
| 80 } // namespace | |
| 81 | |
| 82 | |
| 83 // ShellLinkItem | |
| 84 | |
| 85 ShellLinkItem::ShellLinkItem() | |
| 86 : command_line_(base::CommandLine::NO_PROGRAM), icon_index_(0) { | |
| 87 } | |
| 88 | |
| 89 ShellLinkItem::~ShellLinkItem() {} | |
| 90 | |
| 91 std::wstring ShellLinkItem::GetArguments() const { | |
| 92 return command_line_.GetArgumentsString(); | |
| 93 } | |
| 94 | |
| 95 base::CommandLine* ShellLinkItem::GetCommandLine() { | |
| 96 return &command_line_; | |
| 97 } | |
| 98 | |
| 99 | |
| 100 // JumpListUpdater | |
| 101 | |
| 102 JumpListUpdater::JumpListUpdater(const std::wstring& app_user_model_id) | |
| 103 : app_user_model_id_(app_user_model_id), | |
| 104 user_max_items_(0) { | |
| 105 } | |
| 106 | |
| 107 JumpListUpdater::~JumpListUpdater() { | |
| 108 } | |
| 109 | |
| 110 // static | |
| 111 bool JumpListUpdater::IsEnabled() { | |
| 112 // JumpList is implemented only on Windows 7 or later. | |
| 113 // Do not create custom JumpLists in tests. See http://crbug.com/389375. | |
| 114 return base::win::GetVersion() >= base::win::VERSION_WIN7 && | |
| 115 !base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 116 switches::kTestType); | |
| 117 } | |
| 118 | |
| 119 bool JumpListUpdater::BeginUpdate() { | |
| 120 // This instance is expected to be one-time-use only. | |
| 121 DCHECK(!destination_list_.get()); | |
| 122 | |
| 123 // Check preconditions. | |
| 124 if (!JumpListUpdater::IsEnabled() || app_user_model_id_.empty()) | |
| 125 return false; | |
| 126 | |
| 127 // Create an ICustomDestinationList object and attach it to our application. | |
| 128 HRESULT result = destination_list_.CreateInstance(CLSID_DestinationList, NULL, | |
| 129 CLSCTX_INPROC_SERVER); | |
| 130 if (FAILED(result)) | |
| 131 return false; | |
| 132 | |
| 133 // Set the App ID for this JumpList. | |
| 134 result = destination_list_->SetAppID(app_user_model_id_.c_str()); | |
| 135 if (FAILED(result)) | |
| 136 return false; | |
| 137 | |
| 138 // Start a transaction that updates the JumpList of this application. | |
| 139 // This implementation just replaces the all items in this JumpList, so | |
| 140 // we don't have to use the IObjectArray object returned from this call. | |
| 141 // It seems Windows 7 RC (Build 7100) automatically checks the items in this | |
| 142 // removed list and prevent us from adding the same item. | |
| 143 UINT max_slots; | |
| 144 base::win::ScopedComPtr<IObjectArray> removed; | |
| 145 result = destination_list_->BeginList(&max_slots, __uuidof(*removed), | |
| 146 removed.ReceiveVoid()); | |
| 147 if (FAILED(result)) | |
| 148 return false; | |
| 149 | |
| 150 user_max_items_ = max_slots; | |
| 151 | |
| 152 return true; | |
| 153 } | |
| 154 | |
| 155 bool JumpListUpdater::CommitUpdate() { | |
| 156 if (!destination_list_.get()) | |
| 157 return false; | |
| 158 | |
| 159 // Commit this transaction and send the updated JumpList to Windows. | |
| 160 return SUCCEEDED(destination_list_->CommitList()); | |
| 161 } | |
| 162 | |
| 163 bool JumpListUpdater::AddTasks(const ShellLinkItemList& link_items) { | |
| 164 if (!destination_list_.get()) | |
| 165 return false; | |
| 166 | |
| 167 // Retrieve the absolute path to "chrome.exe". | |
| 168 base::FilePath application_path; | |
| 169 if (!PathService::Get(base::FILE_EXE, &application_path)) | |
| 170 return false; | |
| 171 | |
| 172 // Create an EnumerableObjectCollection object to be added items of the | |
| 173 // "Task" category. | |
| 174 base::win::ScopedComPtr<IObjectCollection> collection; | |
| 175 HRESULT result = collection.CreateInstance(CLSID_EnumerableObjectCollection, | |
| 176 NULL, CLSCTX_INPROC_SERVER); | |
| 177 if (FAILED(result)) | |
| 178 return false; | |
| 179 | |
| 180 // Add items to the "Task" category. | |
| 181 for (ShellLinkItemList::const_iterator it = link_items.begin(); | |
| 182 it != link_items.end(); ++it) { | |
| 183 AddShellLink(collection, application_path.value(), *it); | |
| 184 } | |
| 185 | |
| 186 // We can now add the new list to the JumpList. | |
| 187 // ICustomDestinationList::AddUserTasks() also uses the IObjectArray | |
| 188 // interface to retrieve each item in the list. So, we retrieve the | |
| 189 // IObjectArray interface from the EnumerableObjectCollection object. | |
| 190 base::win::ScopedComPtr<IObjectArray> object_array; | |
| 191 result = collection.QueryInterface(object_array.Receive()); | |
| 192 if (FAILED(result)) | |
| 193 return false; | |
| 194 | |
| 195 return SUCCEEDED(destination_list_->AddUserTasks(object_array.get())); | |
| 196 } | |
| 197 | |
| 198 bool JumpListUpdater::AddCustomCategory(const std::wstring& category_name, | |
| 199 const ShellLinkItemList& link_items, | |
| 200 size_t max_items) { | |
| 201 if (!destination_list_.get()) | |
| 202 return false; | |
| 203 | |
| 204 // Retrieve the absolute path to "chrome.exe". | |
| 205 base::FilePath application_path; | |
| 206 if (!PathService::Get(base::FILE_EXE, &application_path)) | |
| 207 return false; | |
| 208 | |
| 209 // Exit this function when the given vector does not contain any items | |
| 210 // because an ICustomDestinationList::AppendCategory() call fails in this | |
| 211 // case. | |
| 212 if (link_items.empty() || !max_items) | |
| 213 return true; | |
| 214 | |
| 215 // Create an EnumerableObjectCollection object. | |
| 216 // We once add the given items to this collection object and add this | |
| 217 // collection to the JumpList. | |
| 218 base::win::ScopedComPtr<IObjectCollection> collection; | |
| 219 HRESULT result = collection.CreateInstance(CLSID_EnumerableObjectCollection, | |
| 220 NULL, CLSCTX_INPROC_SERVER); | |
| 221 if (FAILED(result)) | |
| 222 return false; | |
| 223 | |
| 224 for (ShellLinkItemList::const_iterator item = link_items.begin(); | |
| 225 item != link_items.end() && max_items > 0; ++item, --max_items) { | |
| 226 scoped_refptr<ShellLinkItem> link(*item); | |
| 227 AddShellLink(collection, application_path.value(), link); | |
| 228 } | |
| 229 | |
| 230 // We can now add the new list to the JumpList. | |
| 231 // The ICustomDestinationList::AppendCategory() function needs the | |
| 232 // IObjectArray interface to retrieve each item in the list. So, we retrive | |
| 233 // the IObjectArray interface from the IEnumerableObjectCollection object | |
| 234 // and use it. | |
| 235 // It seems the ICustomDestinationList::AppendCategory() function just | |
| 236 // replaces all items in the given category with the ones in the new list. | |
| 237 base::win::ScopedComPtr<IObjectArray> object_array; | |
| 238 result = collection.QueryInterface(object_array.Receive()); | |
| 239 if (FAILED(result)) | |
| 240 return false; | |
| 241 | |
| 242 return SUCCEEDED(destination_list_->AppendCategory(category_name.c_str(), | |
| 243 object_array.get())); | |
| 244 } | |
| OLD | NEW |