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

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

Issue 11777006: Speculative revert 175230. I suspect that dependency on propsys.dll makes chrome.dll unloadable on … (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: 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>
13 11
14 #include "base/bind.h" 12 #include "base/bind.h"
15 #include "base/command_line.h" 13 #include "base/command_line.h"
16 #include "base/file_util.h" 14 #include "base/file_util.h"
17 #include "base/message_loop.h" 15 #include "base/message_loop.h"
18 #include "base/path_service.h" 16 #include "base/path_service.h"
19 #include "base/string_number_conversions.h" 17 #include "base/string_number_conversions.h"
20 #include "base/string_util.h" 18 #include "base/string_util.h"
21 #include "base/stringprintf.h" 19 #include "base/stringprintf.h"
22 #include "base/utf_string_conversions.h" 20 #include "base/utf_string_conversions.h"
23 #include "base/win/registry.h" 21 #include "base/win/registry.h"
24 #include "base/win/scoped_comptr.h" 22 #include "base/win/scoped_comptr.h"
25 #include "base/win/shortcut.h" 23 #include "base/win/shortcut.h"
26 #include "base/win/windows_version.h" 24 #include "base/win/windows_version.h"
27 #include "chrome/browser/web_applications/web_app.h" 25 #include "chrome/browser/web_applications/web_app.h"
28 #include "chrome/common/chrome_constants.h" 26 #include "chrome/common/chrome_constants.h"
29 #include "chrome/common/chrome_paths_internal.h" 27 #include "chrome/common/chrome_paths_internal.h"
30 #include "chrome/common/chrome_switches.h" 28 #include "chrome/common/chrome_switches.h"
31 #include "chrome/installer/setup/setup_util.h" 29 #include "chrome/installer/setup/setup_util.h"
32 #include "chrome/installer/util/browser_distribution.h" 30 #include "chrome/installer/util/browser_distribution.h"
33 #include "chrome/installer/util/create_reg_key_work_item.h" 31 #include "chrome/installer/util/create_reg_key_work_item.h"
34 #include "chrome/installer/util/install_util.h" 32 #include "chrome/installer/util/install_util.h"
35 #include "chrome/installer/util/set_reg_value_work_item.h" 33 #include "chrome/installer/util/set_reg_value_work_item.h"
36 #include "chrome/installer/util/shell_util.h" 34 #include "chrome/installer/util/shell_util.h"
37 #include "chrome/installer/util/util_constants.h" 35 #include "chrome/installer/util/util_constants.h"
38 #include "chrome/installer/util/work_item.h" 36 #include "chrome/installer/util/work_item.h"
39 #include "chrome/installer/util/work_item_list.h" 37 #include "chrome/installer/util/work_item_list.h"
40 #include "content/public/browser/browser_thread.h" 38 #include "content/public/browser/browser_thread.h"
41 39
42 // propsys.lib is required for PropvariantTo*().
43 #pragma comment(lib, "propsys.lib")
44
45 using content::BrowserThread; 40 using content::BrowserThread;
46 41
47 namespace { 42 namespace {
48 43
49 #if defined(GOOGLE_CHROME_BUILD) 44 #if defined(GOOGLE_CHROME_BUILD)
50 const wchar_t kAppListAppName[] = L"ChromeAppList"; 45 const wchar_t kAppListAppName[] = L"ChromeAppList";
51 #else 46 #else
52 const wchar_t kAppListAppName[] = L"ChromiumAppList"; 47 const wchar_t kAppListAppName[] = L"ChromiumAppList";
53 #endif 48 #endif
54 49
(...skipping 26 matching lines...) Expand all
81 for (size_t i = 0; i < basenames.length(); ++i) { 76 for (size_t i = 0; i < basenames.length(); ++i) {
82 if (IsAsciiAlpha(basenames[i]) || 77 if (IsAsciiAlpha(basenames[i]) ||
83 IsAsciiDigit(basenames[i]) || 78 IsAsciiDigit(basenames[i]) ||
84 basenames[i] == L'.') 79 basenames[i] == L'.')
85 profile_id += basenames[i]; 80 profile_id += basenames[i];
86 } 81 }
87 82
88 return profile_id; 83 return profile_id;
89 } 84 }
90 85
91 // Gets expected app id for given Chrome (based on |command_line| and 86 bool GetShortcutAppId(IShellLink* shell_link, string16* app_id) {
92 // |is_per_user_install|). 87 DCHECK(shell_link);
93 string16 GetExpectedAppId(const CommandLine& command_line, 88 DCHECK(app_id);
94 bool is_per_user_install) { 89
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) {
113 DCHECK(shell_link);
114 DCHECK(expected_app_id);
115
116 expected_app_id->clear();
117
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
95 FilePath profile_path; 134 FilePath profile_path;
96 if (command_line.HasSwitch(switches::kUserDataDir)) { 135 if (command_line.HasSwitch(switches::kUserDataDir)) {
97 profile_path = 136 profile_path =
98 command_line.GetSwitchValuePath(switches::kUserDataDir).AppendASCII( 137 command_line.GetSwitchValuePath(switches::kUserDataDir).AppendASCII(
99 chrome::kInitialProfile); 138 chrome::kInitialProfile);
100 } 139 }
101 140
102 string16 app_name; 141 string16 app_name;
103 if (command_line.HasSwitch(switches::kApp)) { 142 if (command_line.HasSwitch(switches::kApp)) {
104 app_name = UTF8ToUTF16(web_app::GenerateApplicationNameFromURL( 143 app_name = UTF8ToUTF16(web_app::GenerateApplicationNameFromURL(
105 GURL(command_line.GetSwitchValueASCII(switches::kApp)))); 144 GURL(command_line.GetSwitchValueASCII(switches::kApp))));
106 } else if (command_line.HasSwitch(switches::kAppId)) { 145 } else if (command_line.HasSwitch(switches::kAppId)) {
107 app_name = UTF8ToUTF16(web_app::GenerateApplicationNameFromExtensionId( 146 app_name = UTF8ToUTF16(web_app::GenerateApplicationNameFromExtensionId(
108 command_line.GetSwitchValueASCII(switches::kAppId))); 147 command_line.GetSwitchValueASCII(switches::kAppId)));
109 } else if (command_line.HasSwitch(switches::kShowAppList)) { 148 } else if (command_line.HasSwitch(switches::kShowAppList)) {
110 app_name = kAppListAppName; 149 app_name = kAppListAppName;
111 } else { 150 } else {
112 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 151 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
113 app_name = ShellUtil::GetBrowserModelId(dist, is_per_user_install); 152 app_name = ShellUtil::GetBrowserModelId(
153 dist, InstallUtil::IsPerUserInstall(chrome_exe.value().c_str()));
114 } 154 }
115 155
116 return ShellIntegration::GetAppModelIdForProfile(app_name, profile_path); 156 expected_app_id->assign(
157 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 }
117 } 203 }
118 204
119 void MigrateChromiumShortcutsCallback() { 205 void MigrateChromiumShortcutsCallback() {
120 // This should run on the file thread. 206 // This should run on the file thread.
121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
122 208
123 // Get full path of chrome. 209 // Get full path of chrome.
124 FilePath chrome_exe; 210 FilePath chrome_exe;
125 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) 211 if (!PathService::Get(base::FILE_EXE, &chrome_exe))
126 return; 212 return;
(...skipping 21 matching lines...) Expand all
148 for (int i = 0; i < arraysize(kLocations); ++i) { 234 for (int i = 0; i < arraysize(kLocations); ++i) {
149 FilePath path; 235 FilePath path;
150 if (!PathService::Get(kLocations[i].location_id, &path)) { 236 if (!PathService::Get(kLocations[i].location_id, &path)) {
151 NOTREACHED(); 237 NOTREACHED();
152 continue; 238 continue;
153 } 239 }
154 240
155 if (kLocations[i].sub_dir) 241 if (kLocations[i].sub_dir)
156 path = path.Append(kLocations[i].sub_dir); 242 path = path.Append(kLocations[i].sub_dir);
157 243
158 bool check_dual_mode = (kLocations[i].location_id == base::DIR_START_MENU); 244 MigrateWin7ShortcutsInPath(chrome_exe, path);
159 ShellIntegration::MigrateShortcutsInPathInternal(chrome_exe, path,
160 check_dual_mode);
161 } 245 }
162 } 246 }
163 247
164 ShellIntegration::DefaultWebClientState 248 ShellIntegration::DefaultWebClientState
165 GetDefaultWebClientStateFromShellUtilDefaultState( 249 GetDefaultWebClientStateFromShellUtilDefaultState(
166 ShellUtil::DefaultState default_state) { 250 ShellUtil::DefaultState default_state) {
167 switch (default_state) { 251 switch (default_state) {
168 case ShellUtil::NOT_DEFAULT: 252 case ShellUtil::NOT_DEFAULT:
169 return ShellIntegration::NOT_DEFAULT; 253 return ShellIntegration::NOT_DEFAULT;
170 case ShellUtil::IS_DEFAULT: 254 case ShellUtil::IS_DEFAULT:
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
362 // This needs to happen eventually (e.g. so that the appid is fixed and the 446 // This needs to happen eventually (e.g. so that the appid is fixed and the
363 // run-time Chrome icon is merged with the taskbar shortcut), but this is not 447 // run-time Chrome icon is merged with the taskbar shortcut), but this is not
364 // urgent and shouldn't delay Chrome startup. 448 // urgent and shouldn't delay Chrome startup.
365 static const int64 kMigrateChromiumShortcutsDelaySeconds = 15; 449 static const int64 kMigrateChromiumShortcutsDelaySeconds = 15;
366 BrowserThread::PostDelayedTask( 450 BrowserThread::PostDelayedTask(
367 BrowserThread::FILE, FROM_HERE, 451 BrowserThread::FILE, FROM_HERE,
368 base::Bind(&MigrateChromiumShortcutsCallback), 452 base::Bind(&MigrateChromiumShortcutsCallback),
369 base::TimeDelta::FromSeconds(kMigrateChromiumShortcutsDelaySeconds)); 453 base::TimeDelta::FromSeconds(kMigrateChromiumShortcutsDelaySeconds));
370 } 454 }
371 455
372 int ShellIntegration::MigrateShortcutsInPathInternal(const FilePath& chrome_exe,
373 const FilePath& path,
374 bool check_dual_mode) {
375 DCHECK(base::win::GetVersion() >= base::win::VERSION_WIN7);
376
377 // Enumerate all pinned shortcuts in the given path directly.
378 file_util::FileEnumerator shortcuts_enum(
379 path, false, // not recursive
380 file_util::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
381
382 bool is_per_user_install =
383 InstallUtil::IsPerUserInstall(chrome_exe.value().c_str());
384
385 int shortcuts_migrated = 0;
386 FilePath target_path;
387 string16 arguments;
388 string16 existing_app_id;
389 for (FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty();
390 shortcut = shortcuts_enum.Next()) {
391 // TODO(gab): Use ProgramCompare instead of comparing FilePaths below once
392 // it is fixed to work with FilePaths with spaces.
393 if (!base::win::ResolveShortcut(shortcut, &target_path, &arguments) ||
394 chrome_exe != target_path) {
395 continue;
396 }
397 CommandLine command_line(CommandLine::FromString(base::StringPrintf(
398 L"\"%ls\" %ls", target_path.value().c_str(), arguments.c_str())));
399
400 // Get the expected AppId for this Chrome shortcut.
401 string16 expected_app_id(
402 GetExpectedAppId(command_line, is_per_user_install));
403 if (expected_app_id.empty())
404 continue;
405
406 // Load the shortcut.
407 base::win::ScopedComPtr<IShellLink> shell_link;
408 base::win::ScopedComPtr<IPersistFile> persist_file;
409 if (FAILED(shell_link.CreateInstance(CLSID_ShellLink, NULL,
410 CLSCTX_INPROC_SERVER)) ||
411 FAILED(persist_file.QueryFrom(shell_link)) ||
412 FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) {
413 DLOG(WARNING) << "Failed loading shortcut at " << shortcut.value();
414 continue;
415 }
416
417 // Any properties that need to be updated on the shortcut will be stored in
418 // |updated_properties|.
419 base::win::ShortcutProperties updated_properties;
420
421 // Validate the existing app id for the shortcut.
422 base::win::ScopedComPtr<IPropertyStore> property_store;
423 PROPVARIANT pv_app_id;
424 PropVariantInit(&pv_app_id);
425 if (FAILED(property_store.QueryFrom(shell_link)) ||
426 property_store->GetValue(PKEY_AppUserModel_ID, &pv_app_id) != S_OK) {
427 // When in doubt, prefer not updating the shortcut.
428 NOTREACHED();
429 continue;
430 } else if (pv_app_id.vt == VT_EMPTY) {
431 // If there is no app_id set, set our app_id if one is expected.
432 if (!expected_app_id.empty())
433 updated_properties.set_app_id(expected_app_id);
434 } else {
435 // Validate that the existing app_id is the expected app_id; if not, set
436 // the expected app_id on the shortcut.
437 size_t expected_size = expected_app_id.size() + 1;
438 HRESULT result = PropVariantToString(
439 pv_app_id, WriteInto(&existing_app_id, expected_size), expected_size);
440 PropVariantClear(&pv_app_id);
441 if (result != S_OK && result != STRSAFE_E_INSUFFICIENT_BUFFER) {
442 // Accept the STRSAFE_E_INSUFFICIENT_BUFFER error state as it means the
443 // existing appid is longer than |expected_app_id| and thus we will
444 // simply assume inequality.
445 NOTREACHED();
446 continue;
447 } else if (result == STRSAFE_E_INSUFFICIENT_BUFFER ||
448 expected_app_id != existing_app_id) {
449 updated_properties.set_app_id(expected_app_id);
450 }
451 }
452
453 if (check_dual_mode) {
454 BOOL existing_dual_mode;
455 PROPVARIANT pv_dual_mode;
456 PropVariantInit(&pv_dual_mode);
457 if (property_store->GetValue(PKEY_AppUserModel_IsDualMode,
458 &pv_dual_mode) != S_OK) {
459 // When in doubt, prefer to not update the shortcut.
460 NOTREACHED();
461 continue;
462 } else if (pv_dual_mode.vt == VT_EMPTY) {
463 // If dual_mode is not set at all, make sure it gets set to true.
464 updated_properties.set_dual_mode(true);
465 } else {
466 // If it is set to false, make sure it gets set to true as well.
467 if (PropVariantToBoolean(pv_dual_mode, &existing_dual_mode) != S_OK) {
468 NOTREACHED();
469 continue;
470 }
471 PropVariantClear(&pv_dual_mode);
472 if (!existing_dual_mode)
473 updated_properties.set_dual_mode(true);
474 }
475 }
476
477 persist_file.Release();
478 shell_link.Release();
479
480 // Update the shortcut if some of its properties need to be updated.
481 if (updated_properties.options &&
482 base::win::CreateOrUpdateShortcutLink(
483 shortcut, updated_properties,
484 base::win::SHORTCUT_UPDATE_EXISTING)) {
485 ++shortcuts_migrated;
486 }
487 }
488 return shortcuts_migrated;
489 }
490
491 FilePath ShellIntegration::GetStartMenuShortcut(const FilePath& chrome_exe) { 456 FilePath ShellIntegration::GetStartMenuShortcut(const FilePath& chrome_exe) {
492 static const int kFolderIds[] = { 457 static const int kFolderIds[] = {
493 base::DIR_COMMON_START_MENU, 458 base::DIR_COMMON_START_MENU,
494 base::DIR_START_MENU, 459 base::DIR_START_MENU,
495 }; 460 };
496 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 461 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
497 string16 shortcut_name(dist->GetAppShortCutName()); 462 string16 shortcut_name(dist->GetAppShortCutName());
498 FilePath shortcut; 463 FilePath shortcut;
499 464
500 // Check both the common and the per-user Start Menu folders for system-level 465 // Check both the common and the per-user Start Menu folders for system-level
501 // installs. 466 // installs.
502 size_t folder = 467 size_t folder =
503 InstallUtil::IsPerUserInstall(chrome_exe.value().c_str()) ? 1 : 0; 468 InstallUtil::IsPerUserInstall(chrome_exe.value().c_str()) ? 1 : 0;
504 for (; folder < arraysize(kFolderIds); ++folder) { 469 for (; folder < arraysize(kFolderIds); ++folder) {
505 if (!PathService::Get(kFolderIds[folder], &shortcut)) { 470 if (!PathService::Get(kFolderIds[folder], &shortcut)) {
506 NOTREACHED(); 471 NOTREACHED();
507 continue; 472 continue;
508 } 473 }
509 474
510 shortcut = shortcut.Append(shortcut_name).Append(shortcut_name + 475 shortcut = shortcut.Append(shortcut_name).Append(shortcut_name +
511 installer::kLnkExt); 476 installer::kLnkExt);
512 if (file_util::PathExists(shortcut)) 477 if (file_util::PathExists(shortcut))
513 return shortcut; 478 return shortcut;
514 } 479 }
515 480
516 return FilePath(); 481 return FilePath();
517 } 482 }
OLDNEW
« no previous file with comments | « chrome/browser/shell_integration_unittest.cc ('k') | chrome/browser/shell_integration_win_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698