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 |