Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(369)

Side by Side Diff: chrome/browser/shell_integration_win.cc

Issue 11712003: [Fixit-Dec-2012] Also add dual_mode to Start Menu shortcuts in MigrateChromiumShortcuts. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: small tweaks Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/shell_integration.h" 5 #include "chrome/browser/shell_integration.h"
6 6
7 #include <windows.h> 7 #include <windows.h>
8 #include <shobjidl.h> 8 #include <shobjidl.h>
9 #include <propkey.h> 9 #include <propkey.h>
10 #include <propvarutil.h> 10 #include <propvarutil.h>
11 #include <tchar.h>
12 #include <strsafe.h>
11 13
12 #include "base/bind.h" 14 #include "base/bind.h"
13 #include "base/command_line.h" 15 #include "base/command_line.h"
14 #include "base/file_util.h" 16 #include "base/file_util.h"
15 #include "base/message_loop.h" 17 #include "base/message_loop.h"
16 #include "base/path_service.h" 18 #include "base/path_service.h"
17 #include "base/string_number_conversions.h" 19 #include "base/string_number_conversions.h"
18 #include "base/string_util.h" 20 #include "base/string_util.h"
19 #include "base/stringprintf.h" 21 #include "base/stringprintf.h"
20 #include "base/utf_string_conversions.h" 22 #include "base/utf_string_conversions.h"
21 #include "base/win/registry.h" 23 #include "base/win/registry.h"
22 #include "base/win/scoped_comptr.h" 24 #include "base/win/scoped_comptr.h"
23 #include "base/win/shortcut.h" 25 #include "base/win/shortcut.h"
24 #include "base/win/windows_version.h" 26 #include "base/win/windows_version.h"
25 #include "chrome/browser/web_applications/web_app.h" 27 #include "chrome/browser/web_applications/web_app.h"
26 #include "chrome/common/chrome_constants.h" 28 #include "chrome/common/chrome_constants.h"
27 #include "chrome/common/chrome_paths_internal.h" 29 #include "chrome/common/chrome_paths_internal.h"
28 #include "chrome/common/chrome_switches.h" 30 #include "chrome/common/chrome_switches.h"
29 #include "chrome/installer/setup/setup_util.h" 31 #include "chrome/installer/setup/setup_util.h"
30 #include "chrome/installer/util/browser_distribution.h" 32 #include "chrome/installer/util/browser_distribution.h"
31 #include "chrome/installer/util/create_reg_key_work_item.h" 33 #include "chrome/installer/util/create_reg_key_work_item.h"
32 #include "chrome/installer/util/install_util.h" 34 #include "chrome/installer/util/install_util.h"
33 #include "chrome/installer/util/set_reg_value_work_item.h" 35 #include "chrome/installer/util/set_reg_value_work_item.h"
34 #include "chrome/installer/util/shell_util.h" 36 #include "chrome/installer/util/shell_util.h"
35 #include "chrome/installer/util/util_constants.h" 37 #include "chrome/installer/util/util_constants.h"
36 #include "chrome/installer/util/work_item.h" 38 #include "chrome/installer/util/work_item.h"
37 #include "chrome/installer/util/work_item_list.h" 39 #include "chrome/installer/util/work_item_list.h"
38 #include "content/public/browser/browser_thread.h" 40 #include "content/public/browser/browser_thread.h"
39 41
42 // propsys.lib is required for PropvariantTo*().
43 #pragma comment(lib, "propsys.lib")
44
40 using content::BrowserThread; 45 using content::BrowserThread;
41 46
42 namespace { 47 namespace {
43 48
44 #if defined(GOOGLE_CHROME_BUILD) 49 #if defined(GOOGLE_CHROME_BUILD)
45 const wchar_t kAppListAppName[] = L"ChromeAppList"; 50 const wchar_t kAppListAppName[] = L"ChromeAppList";
46 #else 51 #else
47 const wchar_t kAppListAppName[] = L"ChromiumAppList"; 52 const wchar_t kAppListAppName[] = L"ChromiumAppList";
48 #endif 53 #endif
49 54
(...skipping 26 matching lines...) Expand all
76 for (size_t i = 0; i < basenames.length(); ++i) { 81 for (size_t i = 0; i < basenames.length(); ++i) {
77 if (IsAsciiAlpha(basenames[i]) || 82 if (IsAsciiAlpha(basenames[i]) ||
78 IsAsciiDigit(basenames[i]) || 83 IsAsciiDigit(basenames[i]) ||
79 basenames[i] == L'.') 84 basenames[i] == L'.')
80 profile_id += basenames[i]; 85 profile_id += basenames[i];
81 } 86 }
82 87
83 return profile_id; 88 return profile_id;
84 } 89 }
85 90
86 bool GetShortcutAppId(IShellLink* shell_link, string16* app_id) { 91 // Gets expected app id for given Chrome (based on |command_line| and
87 DCHECK(shell_link); 92 // |is_per_user_install|.
grt (UTC plus 2) 2013/01/03 02:30:44 nit: close-paren at the end of the sentence.
gab 2013/01/03 21:14:57 Done.
88 DCHECK(app_id); 93 void GetExpectedAppId(const CommandLine& command_line,
grt (UTC plus 2) 2013/01/03 02:30:44 string16 is a value type, so return the expected i
gab 2013/01/03 21:14:57 Done.
89 94 bool is_per_user_install,
90 app_id->clear();
91
92 base::win::ScopedComPtr<IPropertyStore> property_store;
93 if (FAILED(property_store.QueryFrom(shell_link)))
94 return false;
95
96 PROPVARIANT appid_value;
97 PropVariantInit(&appid_value);
98 if (FAILED(property_store->GetValue(PKEY_AppUserModel_ID, &appid_value)))
99 return false;
100
101 if (appid_value.vt == VT_LPWSTR || appid_value.vt == VT_BSTR)
102 app_id->assign(appid_value.pwszVal);
103
104 PropVariantClear(&appid_value);
105 return true;
106 }
107
108 // Gets expected app id for given chrome shortcut. Returns true if the shortcut
109 // points to chrome and expected app id is successfully derived.
110 bool GetExpectedAppId(const FilePath& chrome_exe,
111 IShellLink* shell_link,
112 string16* expected_app_id) { 95 string16* expected_app_id) {
113 DCHECK(shell_link);
114 DCHECK(expected_app_id); 96 DCHECK(expected_app_id);
115
116 expected_app_id->clear(); 97 expected_app_id->clear();
117 98
118 // Check if the shortcut points to chrome_exe.
119 string16 source;
120 if (FAILED(shell_link->GetPath(WriteInto(&source, MAX_PATH), MAX_PATH, NULL,
121 SLGP_RAWPATH)) ||
122 lstrcmpi(chrome_exe.value().c_str(), source.c_str()))
123 return false;
124
125 string16 arguments;
126 if (FAILED(shell_link->GetArguments(WriteInto(&arguments, MAX_PATH),
127 MAX_PATH)))
128 return false;
129
130 // Get expected app id from shortcut command line.
131 CommandLine command_line = CommandLine::FromString(base::StringPrintf(
132 L"\"%ls\" %ls", source.c_str(), arguments.c_str()));
133
134 FilePath profile_path; 99 FilePath profile_path;
135 if (command_line.HasSwitch(switches::kUserDataDir)) { 100 if (command_line.HasSwitch(switches::kUserDataDir)) {
136 profile_path = 101 profile_path =
137 command_line.GetSwitchValuePath(switches::kUserDataDir).AppendASCII( 102 command_line.GetSwitchValuePath(switches::kUserDataDir).AppendASCII(
138 chrome::kInitialProfile); 103 chrome::kInitialProfile);
139 } 104 }
140 105
141 string16 app_name; 106 string16 app_name;
142 if (command_line.HasSwitch(switches::kApp)) { 107 if (command_line.HasSwitch(switches::kApp)) {
143 app_name = UTF8ToUTF16(web_app::GenerateApplicationNameFromURL( 108 app_name = UTF8ToUTF16(web_app::GenerateApplicationNameFromURL(
144 GURL(command_line.GetSwitchValueASCII(switches::kApp)))); 109 GURL(command_line.GetSwitchValueASCII(switches::kApp))));
145 } else if (command_line.HasSwitch(switches::kAppId)) { 110 } else if (command_line.HasSwitch(switches::kAppId)) {
146 app_name = UTF8ToUTF16(web_app::GenerateApplicationNameFromExtensionId( 111 app_name = UTF8ToUTF16(web_app::GenerateApplicationNameFromExtensionId(
147 command_line.GetSwitchValueASCII(switches::kAppId))); 112 command_line.GetSwitchValueASCII(switches::kAppId)));
148 } else if (command_line.HasSwitch(switches::kShowAppList)) { 113 } else if (command_line.HasSwitch(switches::kShowAppList)) {
149 app_name = kAppListAppName; 114 app_name = kAppListAppName;
150 } else { 115 } else {
151 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 116 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
152 app_name = ShellUtil::GetBrowserModelId( 117 app_name = ShellUtil::GetBrowserModelId(dist, is_per_user_install);
153 dist, InstallUtil::IsPerUserInstall(chrome_exe.value().c_str()));
154 } 118 }
155 119
156 expected_app_id->assign( 120 expected_app_id->assign(
157 ShellIntegration::GetAppModelIdForProfile(app_name, profile_path)); 121 ShellIntegration::GetAppModelIdForProfile(app_name, profile_path));
158 return true;
159 }
160
161 void MigrateWin7ShortcutsInPath(
162 const FilePath& chrome_exe, const FilePath& path) {
163 // Enumerate all pinned shortcuts in the given path directly.
164 file_util::FileEnumerator shortcuts_enum(
165 path, false, // not recursive
166 file_util::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
167
168 for (FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty();
169 shortcut = shortcuts_enum.Next()) {
170 // Load the shortcut.
171 base::win::ScopedComPtr<IShellLink> shell_link;
172 if (FAILED(shell_link.CreateInstance(CLSID_ShellLink, NULL,
173 CLSCTX_INPROC_SERVER))) {
174 NOTREACHED();
175 return;
176 }
177
178 base::win::ScopedComPtr<IPersistFile> persist_file;
179 if (FAILED(persist_file.QueryFrom(shell_link)) ||
180 FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) {
181 NOTREACHED();
182 return;
183 }
184
185 // Get expected app id from shortcut.
186 string16 expected_app_id;
187 if (!GetExpectedAppId(chrome_exe, shell_link, &expected_app_id) ||
188 expected_app_id.empty())
189 continue;
190
191 // Get existing app id from shortcut if any.
192 string16 existing_app_id;
193 GetShortcutAppId(shell_link, &existing_app_id);
194
195 if (expected_app_id != existing_app_id) {
196 base::win::ShortcutProperties properties_app_id_only;
197 properties_app_id_only.set_app_id(expected_app_id);
198 base::win::CreateOrUpdateShortcutLink(
199 shortcut, properties_app_id_only,
200 base::win::SHORTCUT_UPDATE_EXISTING);
201 }
202 }
203 } 122 }
204 123
205 void MigrateChromiumShortcutsCallback() { 124 void MigrateChromiumShortcutsCallback() {
206 // This should run on the file thread. 125 // This should run on the file thread.
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
208 127
209 // Get full path of chrome. 128 // Get full path of chrome.
210 FilePath chrome_exe; 129 FilePath chrome_exe;
211 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) 130 if (!PathService::Get(base::FILE_EXE, &chrome_exe))
212 return; 131 return;
(...skipping 21 matching lines...) Expand all
234 for (int i = 0; i < arraysize(kLocations); ++i) { 153 for (int i = 0; i < arraysize(kLocations); ++i) {
235 FilePath path; 154 FilePath path;
236 if (!PathService::Get(kLocations[i].location_id, &path)) { 155 if (!PathService::Get(kLocations[i].location_id, &path)) {
237 NOTREACHED(); 156 NOTREACHED();
238 continue; 157 continue;
239 } 158 }
240 159
241 if (kLocations[i].sub_dir) 160 if (kLocations[i].sub_dir)
242 path = path.Append(kLocations[i].sub_dir); 161 path = path.Append(kLocations[i].sub_dir);
243 162
244 MigrateWin7ShortcutsInPath(chrome_exe, path); 163 bool check_dual_mode = (kLocations[i].location_id == base::DIR_START_MENU);
164 shell_integration::internals::MigrateShortcutsInPath(chrome_exe, path,
165 check_dual_mode);
245 } 166 }
246 } 167 }
247 168
248 ShellIntegration::DefaultWebClientState 169 ShellIntegration::DefaultWebClientState
249 GetDefaultWebClientStateFromShellUtilDefaultState( 170 GetDefaultWebClientStateFromShellUtilDefaultState(
250 ShellUtil::DefaultState default_state) { 171 ShellUtil::DefaultState default_state) {
251 switch (default_state) { 172 switch (default_state) {
252 case ShellUtil::NOT_DEFAULT: 173 case ShellUtil::NOT_DEFAULT:
253 return ShellIntegration::NOT_DEFAULT; 174 return ShellIntegration::NOT_DEFAULT;
254 case ShellUtil::IS_DEFAULT: 175 case ShellUtil::IS_DEFAULT:
255 return ShellIntegration::IS_DEFAULT; 176 return ShellIntegration::IS_DEFAULT;
256 default: 177 default:
257 DCHECK_EQ(ShellUtil::UNKNOWN_DEFAULT, default_state); 178 DCHECK_EQ(ShellUtil::UNKNOWN_DEFAULT, default_state);
258 return ShellIntegration::UNKNOWN_DEFAULT; 179 return ShellIntegration::UNKNOWN_DEFAULT;
259 } 180 }
260 } 181 }
261 182
262 } // namespace 183 } // namespace
263 184
185 namespace shell_integration {
186
187 namespace internals {
188
189 int MigrateShortcutsInPath(
190 const FilePath& chrome_exe, const FilePath& path, bool check_dual_mode) {
191 DCHECK(base::win::GetVersion() >= base::win::VERSION_WIN7);
192
193 // Enumerate all pinned shortcuts in the given path directly.
194 file_util::FileEnumerator shortcuts_enum(
195 path, false, // not recursive
196 file_util::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
197
198 bool is_per_user_install =
199 InstallUtil::IsPerUserInstall(chrome_exe.value().c_str());
200
201 int shortcuts_migrated = 0;
202 FilePath target_path;
203 string16 arguments;
204 string16 expected_app_id;
205 string16 existing_app_id;
206 BOOL existing_dual_mode;
grt (UTC plus 2) 2013/01/03 02:30:44 move down to line 272/3
gab 2013/01/03 21:14:57 Done.
207 for (FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty();
208 shortcut = shortcuts_enum.Next()) {
209 // TODO(gab): Use ProgramCompare instead of comparing FilePaths below once
210 // it is fixed to work with FilePaths with spaces.
211 if (!base::win::ResolveShortcut(shortcut, &target_path, &arguments) ||
212 chrome_exe != target_path) {
213 continue;
214 }
215 CommandLine command_line(CommandLine::FromString(base::StringPrintf(
216 L"\"%ls\" %ls", target_path.value().c_str(), arguments.c_str())));
217
218 // Get the expected AppId for this Chrome shortcut.
219 GetExpectedAppId(command_line, is_per_user_install, &expected_app_id);
220 if (expected_app_id.empty())
221 continue;
222
223 // Load the shortcut.
224 base::win::ScopedComPtr<IShellLink> shell_link;
225 base::win::ScopedComPtr<IPersistFile> persist_file;
226 if (FAILED(shell_link.CreateInstance(CLSID_ShellLink, NULL,
227 CLSCTX_INPROC_SERVER)) ||
228 FAILED(persist_file.QueryFrom(shell_link)) ||
229 FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) {
230 NOTREACHED();
grt (UTC plus 2) 2013/01/03 02:30:44 it seems wrong for chrome to crash if there's an i
gab 2013/01/03 21:14:57 Done.
231 continue;
232 }
233
234 // Any properties that needs to be updated on the shortcut will be stored in
grt (UTC plus 2) 2013/01/03 02:30:44 needs -> need
gab 2013/01/03 21:14:57 Done.
235 // |updated_properties|.
236 base::win::ShortcutProperties updated_properties;
237
238 // Get existing app id from shortcut if any.
grt (UTC plus 2) 2013/01/03 02:30:44 "Get the existing app id from the shortcut."
gab 2013/01/03 21:14:57 Done.
239 // Note, as mentioned on MSDN at
240 // http://msdn.microsoft.com/library/windows/desktop/bb776559.aspx, if
241 // PKEY_AppUserModel_ID is not set on the shortcut: GetValue() will still
242 // return S_OK and |pv_app_id| will be set to VT_EMPTY which will in turn be
grt (UTC plus 2) 2013/01/03 02:30:44 please explicitly test for VT_EMPTY rather than us
gab 2013/01/03 21:14:57 Done.
grt (UTC plus 2) 2013/01/07 15:23:22 Why did you mark this "Done" but not actually do i
gab 2013/01/07 15:37:09 Hmm?! I did do this... i.e. check for VT_EMPTY. I
243 // converted to the empty string by PropVariantToString() as desired.
244 base::win::ScopedComPtr<IPropertyStore> property_store;
245 PROPVARIANT pv_app_id;
grt (UTC plus 2) 2013/01/03 02:30:44 please continue to use PropVariantInit and PropVar
gab 2013/01/03 21:14:57 Done, but I don't see why those are necessary; I h
246 if (FAILED(property_store.QueryFrom(shell_link)) ||
247 property_store->GetValue(PKEY_AppUserModel_ID, &pv_app_id) != S_OK) {
248 // When in doubt, prefer not updating the shortcut.
grt (UTC plus 2) 2013/01/03 02:30:44 "not updating" -> "to not update"
gab 2013/01/03 21:14:57 Done.
249 NOTREACHED();
250 continue;
251 } else {
252 size_t expected_size = expected_app_id.size() + 1;
253 HRESULT result = PropVariantToString(
254 pv_app_id, WriteInto(&existing_app_id, expected_size), expected_size);
255 if (result != S_OK && result != STRSAFE_E_INSUFFICIENT_BUFFER) {
256 // Accept the STRSAFE_E_INSUFFICIENT_BUFFER error state as it means the
257 // existing appid is longer than |expected_app_id| and thus we will
258 // simply assume inequality.
259 NOTREACHED();
260 continue;
261 } else if (result == STRSAFE_E_INSUFFICIENT_BUFFER ||
262 expected_app_id != existing_app_id) {
263 updated_properties.set_app_id(expected_app_id);
264 }
265 }
266
267 if (check_dual_mode) {
268 // Note, similar to the behavior described above, if
grt (UTC plus 2) 2013/01/03 02:30:44 same comment about checking for VT_EMPTY instead o
gab 2013/01/03 21:14:57 Done.
269 // PKEY_AppUserModel_IsDualMode is not set on this shortcut the operations
270 // below will result in |existing_dual_mode| being set to FALSE as
271 // desired.
272 PROPVARIANT pv_dual_mode;
273 if (property_store->GetValue(PKEY_AppUserModel_IsDualMode,
274 &pv_dual_mode) != S_OK ||
275 PropVariantToBoolean(pv_dual_mode, &existing_dual_mode) != S_OK) {
276 // When in doubt, prefer not updating the shortcut.
grt (UTC plus 2) 2013/01/03 02:30:44 "not updating" -> "to not update"
gab 2013/01/03 21:14:57 Done.
277 NOTREACHED();
278 continue;
279 } else if (!existing_dual_mode) {
280 updated_properties.set_dual_mode(true);
281 }
282 }
283
284 persist_file.Release();
285 shell_link.Release();
286
287 // Update the shortcut iff some of its properties need to be updated.
grt (UTC plus 2) 2013/01/03 02:30:44 iff -> if (this is becoming a pet peeve of mine. :
gab 2013/01/03 21:14:57 Done.
288 if (updated_properties.options &&
289 base::win::CreateOrUpdateShortcutLink(
290 shortcut, updated_properties,
291 base::win::SHORTCUT_UPDATE_EXISTING)) {
292 ++shortcuts_migrated;
293 }
294 }
295 return shortcuts_migrated;
296 }
297
298 } // namespace internals
299
300 } // namespace shell_integration
301
264 ShellIntegration::DefaultWebClientSetPermission 302 ShellIntegration::DefaultWebClientSetPermission
265 ShellIntegration::CanSetAsDefaultBrowser() { 303 ShellIntegration::CanSetAsDefaultBrowser() {
266 if (!BrowserDistribution::GetDistribution()->CanSetAsDefault()) 304 if (!BrowserDistribution::GetDistribution()->CanSetAsDefault())
267 return SET_DEFAULT_NOT_ALLOWED; 305 return SET_DEFAULT_NOT_ALLOWED;
268 306
269 if (ShellUtil::CanMakeChromeDefaultUnattended()) 307 if (ShellUtil::CanMakeChromeDefaultUnattended())
270 return SET_DEFAULT_UNATTENDED; 308 return SET_DEFAULT_UNATTENDED;
271 else 309 else
272 return SET_DEFAULT_INTERACTIVE; 310 return SET_DEFAULT_INTERACTIVE;
273 } 311 }
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after
473 } 511 }
474 512
475 shortcut = shortcut.Append(shortcut_name).Append(shortcut_name + 513 shortcut = shortcut.Append(shortcut_name).Append(shortcut_name +
476 installer::kLnkExt); 514 installer::kLnkExt);
477 if (file_util::PathExists(shortcut)) 515 if (file_util::PathExists(shortcut))
478 return shortcut; 516 return shortcut;
479 } 517 }
480 518
481 return FilePath(); 519 return FilePath();
482 } 520 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698