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